Compare commits

...

84 Commits

Author SHA1 Message Date
8695b57584 Bump version for 0.80.0 release (#9212)
# Checklist

- [x] merged reedline PR
- [ ] release notes
2023-05-17 10:11:13 +12:00
c9652bce00 Pin reedline to 0.19.1 for 0.80 release (#9211)
# Description
This just includes the bracketed paste bugfix in nushell/reedline#577


# User-Facing Changes
None

# Tests + Formatting

# After Submitting
2023-05-17 10:05:24 +12:00
bf86cd50a5 REFACTOR: remove the shell commands (#8415)
Related to #8368.

# Description
as planned in #8311, the `enter`, `shells`, `g`, `n` and `p` commands
have been re-implemented in pure-`nushell` in the standard library.
this PR removes the `rust` implementations of these commands.

- all the "shells" tests have been removed from
`crates/nu-commnand/tests/commands/` in
2cc6a82da6, except for the `exit` command
- `cd` does not use the `shells` feature in its source code anymore =>
that does not change its single-shell behaviour
- all the command implementations have been removed from
`crates/nu-command/src/shells/`, except for `exit.rs` => `mod.rs` has
been modified accordingly
- the `exit` command now does not compute any "shell" related things
- the `--now` option has been removed from `exit`, as it does not serve
any purpose without sub-shells

# User-Facing Changes
users may now not use `enter`, `shells`, `g`, `n` and `p`
now they would have to use the standard library to have access to
equivalent features, thanks to the `dirs.nu` module introduced by @bobhy
in #8368

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
the website will have to be regenerated to reflect the removed commands
👍
2023-05-13 12:40:11 -05:00
b4a1f0f003 document how to run dataframe tests as well (#9188)
We currently were not documenting how to run the dataframe tests also...

- Run all tests:

  ```shell
  cargo test --workspace
  ```

  along with dataframe tests

  ```shell
  cargo test --workspace --features=dataframe
  ```
2023-05-13 09:18:37 -07:00
8d8304cf91 Allow recursive module dirs; Require mod.nu in dirs (#9185) 2023-05-13 01:20:33 +03:00
92c1051143 fix the span of the global std help item (#9184)
related to 
- #9101
- #9039

# Description
i actually forgot to fix in #9039 the new "*item*" introduced by
#9101... 👀
there it is 😇 

# User-facing changes
going from
```
> std help git-commiteuwqi
Help pages from external command git-commiteuwqi:
No manual entry for git-commiteuwqi
Error:
  × std::help::item_not_found
     ╭─[help:721:1]
 721 │
 722 │     let item = ($item | str join " ")
     ·     ─┬─
     ·      ╰── item not found
 723 │
     ╰────
```
to
```
> std help git-commiteuwqi
Help pages from external command git-commiteuwqi:
No manual entry for git-commiteuwqi
Error:
  × std::help::item_not_found
   ╭─[entry #2:1:1]
 1 │ std help git-commiteuwqi
   ·          ───────┬───────
   ·                 ╰── item not found
   ╰────
```

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-12 20:30:38 +02:00
9ebb61fc2d FIX: correct bad span in std help errors (#9039)
# Description
##  before this PR
```
>_ std help modules euwioq
Error: nu:🐚:external_command

  × External command failed
     ╭─[help:259:1]
 259 │         if ($found_module | is-empty) {
 260 │             module_not_found_error (metadata $module | get span)
     ·                                    ──────────────┬──────────────
     ·                                                  ╰── Cannot convert record<start: int, end: int> to a string
 261 │         }
     ╰────
  help: All arguments to an external command need to be string-compatible
```
```
>_ std help externs euwioq
Error:
  × std::help::extern_not_found
     ╭─[help:401:1]
 401 │
 402 │     let extern = ($extern | str join " ")
     ·     ─┬─
     ·      ╰── extern not found
 403 │
     ╰────
```
> **Note**
> same kind of error with all the others

## ✔️ after this PR
```
> std help modules euwioq                                                                                         04/28/2023 05:45:50 PM
Error:
  × std::help::module_not_found
   ╭─[entry #2:1:1]
 1 │ std help modules euwioq
   ·                  ───┬──
   ·                     ╰── module not found
   ╰────
```
```
> std help externs euwioq                                                                                         04/28/2023 05:45:53 PM
Error:
  × std::help::extern_not_found
   ╭─[entry #3:1:1]
 1 │ std help externs euwioq
   ·                  ───┬──
   ·                     ╰── extern not found
   ╰────
```
> **Note**
> same with the others

# User-Facing Changes
fixes the errors to have proper messages

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-12 19:44:39 +02:00
2254805a6d make the pattern-matcher and eval engine use the same unit computation (#8973)
# Description
this pr adresses
[this](7413ef2824/crates/nu-protocol/src/engine/pattern_match.rs (L149))
'fix me'
2023-05-12 12:18:11 -05:00
6cbd42974b add dataframe support to toolkit (#9173)
# Description
This PR allows you to pass `--dataframe` into `toolkit check pr --fast
--dataframe` in order to check and build with the dataframe feature.

# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-12 12:03:51 -05:00
8584aa79a2 Span fixes during duration conversion (#9143)
Description: Fix of #8945.


# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->

---------

Co-authored-by: jpaldino <jpaldino@zaloni.com>
2023-05-12 18:57:50 +02:00
a3bf2bff49 improve error when name and parameters are not space-separated (#8958)
# Description
closes #8934

this pr improves the diagnostic emitted when the name and parameters of
either `def`, `def-env` or `extern` are not separated by a space

```nu
Error:
  × no space between name and parameters
   ╭─[entry #1:1:1]
 1 │ def err[] {}
   ·        ▲
   ·        ╰── expected space
   ╰────
  help: consider adding a space between the `def` command's name and its parameters
```

from

```nu
Error: nu::parser::missing_positional

  × Missing required positional argument.
   ╭─[entry #1:1:1]
 1 │ def err[] {}
   ╰────
  help: Usage: def <def_name> <params> <body>
```

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
Co-authored-by: Jelle Besseling <jelle@pingiun.com>
2023-05-12 09:10:40 -05:00
5e8754bd85 Start to move to polars 0.29 (#9145)
This does part of the work of porting to polars 0.29.
However, I am not familiar enough with this part of the codebase to
finish it.

Things to be done:
- We match two times over `polars::Expr` but `Expr::Cache` isn't
handled. I don't know what should be done here
- `ArgExpr:::List` was renamed to `ArgExpr::Implode`. Does that mean
that `dfr list` should be renamed to `dfr implode`?

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-05-12 07:44:35 -05:00
JT
a45bd0301a remove a few unnecessary allocations (#9176)
# Description

Change the parser a little bit so it does less allocations when it's
parsing, specifically when parsing lists/tables

# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-12 05:10:54 +02:00
45c17d9664 chore: enable setup-git-hooks on windows (#9097)
# Description
Enable setup-git-hooks on windows. It works just fine without doing
anything special. More testing from other windows users is welcomed as
there might be a reason it was done that way before.

# User-Facing Changes
`toolkit setup-git-hooks` now work on windows
2023-05-11 18:51:06 -07:00
39cdf56214 Update default_env.nu to work with windows (#9172)
# Description
This PR fixes the `create_left_prompt` custom command to work with
Windows.
Before:

![image](https://github.com/nushell/nushell/assets/343840/5a78fc3b-3c2a-4c2f-9c1d-2451063273af)
After:

![image](https://github.com/nushell/nushell/assets/343840/c3785767-5000-454e-9b7b-b0094c3d0834)


# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-11 15:48:56 -05:00
9e9fe83bfd Parameter defaults to $nu.scope.commands (#9152)
(*third* try at posting this PR, #9104, like #9084, got polluted with
unrelated commits. I'm never going to pull from the github feature
branch again!)

# 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.
-->
Show parameter defaults in scope command signature, where they're
available for display by help.
per https://github.com/nushell/nushell/issues/8928.

I found unexpected ramifications in one completer (NuHelpCompleter) and
plugins, which both use the flag-formatting routine from builtin help.
For the moment I made the minimum necessary changes to get the mainline
scenario to pass tests and run. But we should circle back on what to do
with plugins and help completer..

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
1. New `parameter_default` column to `signatures` table in
`$nu.scope.commands`
It is populated with whatever parameters can be defaulted: currently
positional args and named flags.
2. Built in help (both `help <command>` and `<command> --help` will
display the defaults
3. Help completer will display defaults for flags, but not for
positionals.

Example:
A custom command with some default parameters:
```
〉cat ~/work/dflts.nu 
# sample function to show defaults in help
export def main [
    arg1: string        # mandatory positional
    arg2:string=abc     # optional positional
    --switch            # no default here
    --named:int         # named flag, no default
    --other:string=def  # flag 
    --hard:record<foo:int bar:string, bas:bool> # default can be compound type
            = {foo:22, bar:"other worlds", bas:false}
] { {arg1: $arg1,
    arg2: $arg2,
    switch: $switch,
    named: $named,
    other: $other,
    hard: $hard, }
}

〉use ~/work/dflts.nu

〉$nu.scope.commands | where name == 'dflts' | get signatures.0.any | reject short_flag description custom_completion
╭───┬────────────────┬────────────────┬──────────────────────────────────────────┬─────────────┬───────────────────────────╮
│ # │ parameter_name │ parameter_type │               syntax_shape               │ is_optional │     parameter_default     │
├───┼────────────────┼────────────────┼──────────────────────────────────────────┼─────────────┼───────────────────────────┤
│ 0 │                │ input          │ any                                      │ false       │                           │
│ 1 │ arg1           │ positional     │ string                                   │ false       │                           │
│ 2 │ arg2           │ positional     │ string                                   │ true        │ abc                       │
│ 3 │ switch         │ switch         │                                          │ true        │                           │
│ 4 │ named          │ named          │ int                                      │ true        │                           │
│ 5 │ other          │ named          │ string                                   │ true        │ def                       │
│ 6 │ hard           │ named          │ record<foo: int, bar: string, bas: bool> │ true        │ ╭───────┬───────────────╮ │
│   │                │                │                                          │             │ │ foo   │ 22            │ │
│   │                │                │                                          │             │ │ bar   │ other worlds  │ │
│   │                │                │                                          │             │ │ bas   │ false         │ │
│   │                │                │                                          │             │ ╰───────┴───────────────╯ │
│ 7 │                │ output         │ any                                      │ false       │                           │
╰───┴────────────────┴────────────────┴──────────────────────────────────────────┴─────────────┴───────────────────────────╯

〉help dflts
sample function to show defaults in help

Usage:
  > dflts {flags} <arg1> (arg2) 

Flags:
  --switch - switch -- no default here
  --named <Int> - named flag, typed, but no default
  --other <String> - flag with default (default: 'def')
  --hard <Record([("foo", Int), ("bar", String), ("bas", Boolean)])> - default can be compound type (default: {foo: 22, bar: 'other worlds', bas: false})
  -h, --help - Display the help message for this command

Parameters:
  arg1 <string>: mandatory positional
  arg2 <string>: optional positional (optional, default: 'abc')
```

Compared to (relevant bits of) help output previously:
```
Flags:
  -h, --help - Display the help message for this command
  -, --switch - no default here
  -, --named <int> - named flag, no default
  -, --other <string> - flag
  -, --hard <record<foo: int, bar: string, bas: bool>> - default can be compound type

Signatures:
  <any> | dflts <string> <string> -> <any>

Parameters:
  arg1 <string>: mandatory positional
  (optional) arg2 <string>: optional positional
```

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-11 13:59:56 -05:00
e735d0c561 Fix find -v command on tables (issue #9043) (#9159)
# Description
This PR fixes issue #9043 where find -v was returning empty tables
and/or wrong output.
It also refactors some big code chunks with repetitions into it's own
functions.

# User-Facing Changes

# Tests + Formatting
Unit tests added for asserting changes.

# After Submitting
2023-05-11 13:39:21 -05:00
39e51f1953 add more commands to std iter (#9129)
# Description

this pr adds the following commands to `std iter`
- `iter find-index` -> returns the index of the first element that
matches the predicate or `-1` if none
- `iter flat-map` -> maps a closure to each nested structure and
flattens the result
- `iter zip-with` -> zips two structures and applies a closure to each
of the zips

it also fixes some \*\*very minor\*\* inconsistencies in the module
2023-05-10 19:18:42 +02:00
43a3983d36 REFACTOR: move the banner from the rust source to the standard library (#8406)
Related to:
- #8311 
- #8353

# Description
with the new `$nu.startup-time` from #8353 and as mentionned in #8311,
we are now able to fully move the `nushell` banner from the `rust`
source base to the standard library.

this PR
- removes all the `rust` source code for the banner
- rewrites a perfect clone of the banner to `std.nu`, called `std
banner`
- call `std banner` from `default_config.nu`

# User-Facing Changes
see the demo: https://asciinema.org/a/566521

- no config will show the banner (e.g. `cargo run --release --
--no-config-file`)
- a custom config without the `if $env.config.show_banner` block and no
call to `std banner` would never show the banner
- a custom config with the block and `config.show_banner = true` will
show the banner
- a custom config with the block and `config.show_banner = false` will
NOT show the banner

# Tests + Formatting
a new test line has been added to `tests.nu` to check the length of the
`std banner` output.
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting
```
$nothing
```

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-05-10 07:05:01 -05:00
a8b4e81408 add a negation glob option to the glob command (#9153)
# Description
This PR adds the ability to add a negation glob.

Normal Example:
```
> glob **/tsconfig.json
╭───┬────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ 0 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\client\node_modules\big-integer\tsconfig.json │
│ 1 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\client\tsconfig.json                          │
│ 2 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\node_modules\fastq\test\tsconfig.json         │
│ 3 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\node_modules\jszip\tsconfig.json              │
│ 4 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\server\tsconfig.json                          │
│ 5 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\tsconfig.json                                 │
╰───┴────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
Negation Example:
```
> glob **/tsconfig.json --not **/node_modules/**
╭───┬───────────────────────────────────────────────────────────────────────────────╮
│ 0 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\client\tsconfig.json │
│ 1 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\server\tsconfig.json │
│ 2 │ C:\Users\username\source\repos\forks\vscode-nushell-lang\tsconfig.json        │
╰───┴───────────────────────────────────────────────────────────────────────────────╯
```

# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-10 06:31:34 -05:00
6c13c67528 Ensure consistent map ordering when reading YAML (#9155)
# Description

This change ensures that the ordering of map keys when reading YAML
files is consistent. Previously a `HashMap` was used to store the
mappings, but that would result in non-deterministic ordering of the
keys. Switching to an `IndexMap` fixes this.

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

# User-Facing Changes

User's can rely on consistent ordering of map keys from YAML.

# Tests + Formatting

A unit test ensuring the ordering has been added.

# After Submitting

None.
2023-05-10 06:30:55 -05:00
2a484a3e7e fix: use buffer.len() instead of cursor_pos, so the .expect() isn't useless (#9053)
# Description
Use `buffer.len()` instead of `cursor_pos`, so the `.expect()` isn't
useless.

# User-Facing Changes

# Tests + Formatting

# After Submitting
2023-05-08 13:02:01 -05:00
a78cd6e231 FIX: add a space after the default left prompt (#9074)
# Description
when running `nushell` with the `--no-config-file` option, the left
prompt does not have a space to separate the directory path from the
user input.
in this PR i add a space there to make the prompt easier to read when
using `--no-config-file`!

# User-Facing Changes
before: https://asciinema.org/a/581733
after: https://asciinema.org/a/581734

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-08 13:00:44 -05:00
a528c043fe REFACTOR: fix typos and simplify external std help (#9100)
followup to #9025
cc/ @YummyOreo 

# Description
this PR simply fixes some typos and simplifies the logic of the external
help page opening 😋

# User-Facing Changes
```
$nothing
```

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-08 12:59:52 -05:00
47f9fd3644 FIX: have consistent errors between std help and std help ... (#9101)
# Description
an example should show what happens quite clearly 😋 

> **Note**
> the ugly spanned errors you'll see below are fixed in
https://github.com/nushell/nushell/pull/9039 😌

in all cases we get
```
> std help commands does-not-exist-anywhere
Help pages from external command does-not-exist-anywhere:
No manual entry for does-not-exist-anywhere
Error:
  × std::help::command_not_found
     ╭─[help:662:1]
 662 │
 663 │     let command = ($command | str join " ")
     ·     ─┬─
     ·      ╰── command not found
 664 │
     ╰────
```
but

##  before this PR
```
> std help does-not-exist-anywhere
Help pages from external command does-not-exist-anywhere:
No manual entry for does-not-exist-anywhere
```
without any error, which makes it inconsistent with all the other `std
help` commands which give errors when not finding an item 🤔

## ✔️ with this PR
```
> std help does-not-exist-anywhere
Help pages from external command does-not-exist-anywhere:
No manual entry for does-not-exist-anywhere
Error:
  × std::help::item_not_found
     ╭─[help:740:1]
 740 │
 741 │     let item = ($item | str join " ")
     ·     ─┬─
     ·      ╰── item not found
 742 │
     ╰────
```

# User-Facing Changes
more consistent errors when using `std help` and `std help commands`, as
shown 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
```
$nothing
```
2023-05-08 12:58:49 -05:00
fe9f732c5f REFACTOR: make input list a tiny bit tighter (#9115)
related to #8963
cc/ @melMass 

# Description
just a little refactoring attempt for `input list` 😌 

i wanted to refactor even more, but `Select`, `MultiSelect` and
`FuzzySelect` do not share a common trait, i could not find a nice way
to reduce the big `if` block...

# User-Facing Changes
```
$nothing
```

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-08 12:45:55 -05:00
a5d02a0737 nu-explore: Fix repeated char issue in cmdline (#9139)
Must be fixed; (though I'd test it)

Thanks for the reference [fdncred](https://github.com/fdncred).

close #9128

---------

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-05-08 12:38:42 -05:00
7a945848de swap the date and the log level in the std log format (#9138)
related to
https://github.com/nushell/nushell/issues/8588#issuecomment-1538624565

# Description
this PR switches the date and log level in `std log` 👍 

# User-Facing Changes
the date and log level are now swapped in the `std log` format.

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-08 12:29:18 -05:00
a92949b5c3 nu-explore: Fix pipeline rendering (#9137)
Must be fixed (but I would check).

I wonder if it was a regression caused by `Value::LazyRecord`.
I mean likely the issue was before the refactoring I did.

close #9130

---------

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2023-05-08 11:17:14 -05:00
d5ae979094 Update polars to 0.28 (#9136)
# Description
Update polars to 0.28.
Luckily, it didn't require major changes.

# User-Facing Changes
None.
(Apart from the fact that certain error messages will stop breaking
table formatting)
2023-05-08 10:42:53 -05:00
388e84e7ef update nu-glob based on latest glob 0.3.1 changes (#9099)
# Description
This PR updates `nu-glob` to add the latest changes and updates from
`rust-lang/glob` [v0.3.1](https://github.com/rust-lang/glob).

With these changes you can do this type of globbing
```rust
/// - `?` matches any single character.
///
/// - `*` matches any (possibly empty) sequence of characters.
///
/// - `**` matches the current directory and arbitrary subdirectories. This
///   sequence **must** form a single path component, so both `**a` and `b**`
///   are invalid and will result in an error.  A sequence of more than two
///   consecutive `*` characters is also invalid.
///
/// - `[...]` matches any character inside the brackets.  Character sequences
///   can also specify ranges of characters, as ordered by Unicode, so e.g.
///   `[0-9]` specifies any character between 0 and 9 inclusive. An unclosed
///   bracket is invalid.
///
/// - `[!...]` is the negation of `[...]`, i.e. it matches any characters
///   **not** in the brackets.
///
/// - The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets
///   (e.g. `[?]`).  When a `]` occurs immediately following `[` or `[!` then it
///   is interpreted as being part of, rather then ending, the character set, so
///   `]` and NOT `]` can be matched by `[]]` and `[!]]` respectively.  The `-`
///   character can be specified inside a character sequence pattern by placing
///   it at the start or the end, e.g. `[abc-]`.
```
Example - with character sequences

![image](https://user-images.githubusercontent.com/343840/236266670-03bf9384-4917-4074-9687-2c1c0d8ef34a.png)

Example - with character sequence negation

![image](https://user-images.githubusercontent.com/343840/236266421-73c3ee2c-1d10-4da0-86be-0afb51b50604.png)

Example - normal globbing

![image](https://user-images.githubusercontent.com/343840/236267138-60f22228-b8d3-4bf2-911b-a80560fdfa4f.png)

Example - with character sequences

![image](https://user-images.githubusercontent.com/343840/236267475-8c38fce9-87fe-4544-9757-34d319ce55b8.png)

Not that, if you're using a character sequence by itself, you need to
enclose it in quotes, otherwise nushell will think it's a range. But if
you already have a type of a bare word already, no quotes are necessary,
as in the last example.

# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-08 09:07:01 -05:00
a5af77dd72 FEATURE: add a main command to toolkit.nu (#9135)
# Description
until now, a call to `toolkit` alone would give
```bash
Error: nu:🐚:external_command

  × External command failed
   ╭─[entry #2:1:1]
 1 │ toolkit
   · ───┬───
   ·    ╰── did you mean 'toolkit clippy'?
   ╰────
  help: No such file or directory (os error 2)
```
which i find confusing after a `use toolkit.nu` 🤔 

this PR adds a `main` command to `toolkit.nu` which runs the `help`
command on the module.

# User-Facing Changes
now
```
> use toolkit.nu
> toolkit
Usage:
  > toolkit

Subcommands:
  toolkit check pr - run all the necessary checks and tests to submit a perfect PR
  toolkit clippy - check that you're using the standard code style
  toolkit fmt - check standard code formatting and apply the changes
  toolkit setup-git-hooks - set up git hooks to run:
- `toolkit fmt --check --verbose` on `git commit`
- `toolkit fmt --check --verbose` and `toolkit clippy --verbose` on `git push`
  toolkit test - check that all the tests pass
  toolkit test stdlib - run the tests for the standard library

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

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-08 06:45:29 -05:00
a2dd948e71 FEATURE: format std log and add --short option (#9091)
# Description
- prettify formatting
- move message formatting to a private function
- allow short prefixes for loggers via `--short|-s` flag

# User-Facing Changes
- allow short prefixes for loggers via `--short|-s` 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->

---------

Co-authored-by: amtoine <stevan.antoine@gmail.com>
2023-05-08 10:13:21 +02:00
fe60fb8679 resolve standard library before ide commands (#9126)
# Description

This PR moves loading the standard library before the ide commands are
executed in hopes that the standard library will no longer give check
errors. I'm not sure if this will be a big performance impact or not.
We'll have to try it and see.

# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-07 07:29:15 -05:00
250071939b Reuse parsed modules (#9125) 2023-05-07 14:41:40 +03:00
0ea973b78b Fix exported module not found (#9121) 2023-05-06 23:55:10 +03:00
a2a346e39c Allow creating modules from directories (#9066) 2023-05-06 21:39:54 +03:00
6dc7ff2335 FEATURE: return tables with std help ... --find ... (#9040)
# Description
this PR makes `std help` commands return a table no matter the number of
matches when using `--find`.
as proposed by Darren, this would allow users to still rely on things
like
```nushell
let blah = (std help modules -f weather | get name)
```
even if there is a single match.

# User-Facing Changes
`std help ... --find ...` now returns a table as `help ... --find ...`

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-05 18:06:48 +02:00
1acc2bfd96 FEATURE: move common functionality to subroutine in build-all-windows.cmd (#9093)
# Description
- remove redundant `@` after `echo off`
- use `call :build` to simplify code

# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-04 12:23:33 -05:00
10d65b611f adds a list subcommand to input (interactive selections) (#8963)
# Description
Adds a subcommand `list` to `input` (can be migrated wherever if needed)
that allows interactive single and multi selection from an input list.

![image](https://user-images.githubusercontent.com/7041726/236072161-5954dad9-8152-4752-ae3b-b21577711fd1.png)



https://user-images.githubusercontent.com/7041726/233747242-1ca6c44b-e32c-48f1-8fa8-ae50f813be16.mp4


In case it's not clear, only the results are captured (for now the
results are also printed to stderr next to the prompt)


https://user-images.githubusercontent.com/7041726/233785814-f2c8c584-9dd4-4b26-9ae9-c819ed6aa954.mp4


# User-Facing Changes
- Adds a new command `input list`
# Tests + Formatting
Not sure how we can test interactives any ideas?

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-05-04 19:14:41 +02:00
edb61fc1d5 Try to show help pages for external commands w/ help command (#9025)
# 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.
-->
Makes in so if you run `std help <external>` it will run `man <command>`
to get help pages. This command is configurable w/ the `$env.NU_HELPER`
var.

This will close #8032

Examples:
`std help rg` will display the ripgrep help pages

Todo:
- [x] Make flags and fallback configurable
- [x] Improve the warning that it is external
- [ ] Implement `--find` for external commands

# User-Facing Changes
Users will now be able to run `std help` on external 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-04 18:34:07 +02:00
155de9f6fc Added log custom command & exported log levels (#9055)
# Description
- Log levels are now exported members of `std log`, e.g. `std log
CRITICAL_LEVEL`
- Added `std log custom` command that allows to declare custom message
format (actual message replaces `%MSG%` in the template) with
user-defined log level.


# User-Facing Changes
New possibilities included in description

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-04 18:30:15 +02:00
b82e279f9d REFACTOR: remove deprecated commands (old-alias) (#9056)
# Description
as stated in the `0.79` release note, this PR removes the `old-alias`
and `export old-alias` commands, which were deprecated before.

# User-Facing Changes
`old-alias` is gone for good 😌 

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting
already mentionned in the `0.79` release note.
2023-05-04 00:08:07 +02:00
6bbe5b6255 REFACTOR: move source out of deprecated commands (#9060)
# Description
the plan of deprecating `source` never really came to conclusion, so i
propose to move it out of the deprecated commands in this PR.
i've moved it to `nu-command::misc`, which can be changed 👍 

# User-Facing Changes
```
$nothing
```

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-04 00:02:03 +02:00
1019acb7a3 FEATURE: highlight some prompt parts (#9094)
# Description
- highlight directory separators with light green (for regular user) and
light red (for admin) colors respectively
- highlight colons and slashes in the right prompt with light magenta
- underline AM/PM in the right prompt
- use long options to enhance readability

How it looks in MATE Terminal with Tango color theme:


![image](https://user-images.githubusercontent.com/42812113/236052908-fc80def9-9117-4b87-8ce4-321b937f3339.png)



# User-Facing Changes
- highlight directory separators in light green (for regular user) and
red colors (for admin)
- highlight colons and slashes in the right prompt with light magenta
- underline AM/PM in the right prompt

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-03 16:36:27 -05:00
83b1ec83c9 feat(rm)!: use arg. spans for I/O errors (#8964)
# Description

Currently, error spans for I/O errors in an `rm` invocation always point
to the `rm` argument. This isn't ideal, because the user loses context
as to which “target” actually had a problem:


![image](https://user-images.githubusercontent.com/658538/235723366-50db727e-9ba2-4d16-afc6-6a2406c584e0.png)

Shadow the existing `span` variable in outer scope in `rm`'s
implementation for the errors that may be detected while handling I/O
results. This is desired, because all failures from this point are
target-specific, and pointing at the argument that generated the target
instead is better. The end user should now see this:


![image](https://user-images.githubusercontent.com/658538/235724345-1d2e98e0-6b20-4bf5-b8a2-8b4368cdfb05.png)

# User-Facing Changes
* When `rm` encounters I/O errors, their spans now point to the “target”
argument associated with the error, rather than the `rm` token.

# Tests + Formatting


No tests currently cover this. I'm open to adding tests, but adding as
follow-up sounds better ATM, since this wasn't covered before.

# After Submitting

Nothing needs to be done here, AFAIK. No I/O errors are currently
demonstrated in official docs, though maybe they should be?
2023-05-03 23:12:16 +02:00
d9a00a876b Change type of flag defaults to Option<Value> (#9085)
# Description
Follow-up of #8940. As @bobhy pointed out, it makes sense for the
behaviour of flags to match the one for positional arguments, where
default values are of type `Option<Value>` instead of
`Option<Expression>`.

# User-Facing Changes
The same ones from the original PR:
- Flag default values will now be parsed as constants.
- If the default value is not a constant, a parser error is displayed.

# Tests + Formatting

A [new
test](e34e2d35f4/src/tests/test_engine.rs (L338-L344))
has been added to verify the new restriction.
2023-05-03 23:09:36 +02:00
e4625acf24 support bracketed paste (#8907)
# Description

Relative: #8113, #7630
It works on non-windows system

This pr depends on https://github.com/nushell/reedline/pull/571
2023-05-03 23:08:47 +02:00
7fb48b9a2f Fix negative precision round with ints (issue #9049) (#9073)
# Description
Before this PR, `math round` ignores the input if it's an `int`. This
results in the following behaviour:
```
> 123 | math round --precision -1
123
```
When the correct result is 120.

Now `int values` are converted to `float values` before actually
rounding up the number in order to take advantage of the float
implementation.

Fixes #9049.
2023-05-03 23:07:32 +02:00
5fcbefb7b4 Feat: listen for signal on glob command (#9088)
Fixes : #9002 

listen for signal cancel .
other way is in listening parallel for ctrl+c wit Arc and mps channels
if this way is not a profit
2023-05-03 21:51:25 +02:00
345cdef113 Fix overlay's help message lead to internal error (#9087) 2023-05-03 14:08:54 +03:00
a7c1b363eb Don't run .sh files with /bin/sh (#8951)
# Description

The previous behaviour broke for me because I didn't have `sh` in my
path for my nu script. I think we shouldn't assume that just because a
file ends with `.sh` it should be executed with `sh`. `sh` might not be
available or the script might contain a hashbang for a different shell.

The idea with this PR is that nushell shouldn't assume anything about
executable files and just execute them. Later on we can think about how
non-executable files should be executed if we detect they are a script.

# User-Facing Changes

This may break some people's scripts or habits if they have wrong
assumptions about `.sh` files. We can tell them to add a hashbang and +x
bit to execute shell scripts, or prepend `bash`. If this a common
assumption something like this should be added to the book

# Tests + Formatting

I only tested manually and that did work

# After Submitting

Co-authored-by: Jelle Besseling <jelle@bigbridge.nl>
2023-05-02 17:56:35 -05:00
d45e9671d4 Suggest existing variables on not found (#8902) 2023-05-02 18:17:14 +03:00
517dc6d39e pass std bench into table -e in the example (#9075)
cc/ @fdncred 

# Description
in the examples of `std bench` there is an expanded table without
explicitely expanding it...
this PR adds a `table -e` to the `std bench` call in the example.

# User-Facing Changes
the help page of `std bench` now does make sense 😌 

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-05-01 08:18:51 -05:00
e590d3587c enable history isolation (#9063)
# Description
This PR impacts the nushell sqlite history only.

This is the first PR that enables history isolation in nushell for the
sqlite history. Hopefully, we can continue building on this.

This PR allows "history isolation" which means that other nushell
session's history won't be available in the current session when using
the uparrow/downarrow history navigation. This change only impacts the
uparrow downarrow history navigation.

What remains to be done is making ctrl+r history menu respect this
setting too. Right now, the history menu will still show you all entries
from all sessions.

The history command also shows all history items from all sessions. This
may remain unchanged since you can just filter by history session right
now.

This also fixes a bug where the session id is 0 in the sqlite history
since my April 18th reedline PR.

Closes #9064

# 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 -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-01 08:11:38 -05:00
bdaa32666a REFACTOR: have a single std loading call (#9033)
# Description
this PR moves the three individual call to `load_standard_library` in
the "Nushell branches" of `run.rs` into a single one, before the `if`,
in `main.rs`.

# User-Facing Changes
```
$nothing
```

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-04-29 12:48:32 +02:00
9804cd82f8 Fix #9038 (#9042)
close #9038

---------

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2023-04-28 12:50:42 -05:00
59b85e549c FIX: filter the std help ... by name, usage and search terms (#9035)
Should close on of the points in
- https://github.com/nushell/nushell/issues/8813

# Description
this PR uses the new `--columns` option for `find` to filter the results
of the `std help` commands on the `usage`, `name` and `search_terms`
columns.

# User-Facing Changes
the `--find` option of `std help` commands should match more closely the
built-in `help` commands output.

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-04-28 12:29:47 -05:00
dae4a9b091 FIX: give same order in std help ... as in help ... (#9034)
Should close on of the points in
- https://github.com/nushell/nushell/issues/8813

# Description
before this PR, we had a problem
```
cargo run -- -c '{
     modules: ((help modules | get name) == (std help modules | get name))
     aliases: ((help aliases | get name) == (std help aliases | get name))
     externs: ((help externs | get name) == (std help externs | get name))
     operators: ((help operators | get name) == (std help operators | get name))
     commands: ((help commands | get name) == (std help commands | get name))
}'
```
would give
```
╭───────────┬───────╮
│ modules   │ false │
│ aliases   │ true  │
│ externs   │ true  │
│ operators │ false │
│ commands  │ true  │
╰───────────┴───────╯
```

this PR removes the `name` sorting so that the orders are the same
between the `std` implementation and the built-in one.

> **Note**
> run the same `cargo run` command as above and see
> ```
> ╭───────────┬──────╮
> │ modules   │ true │
> │ aliases   │ true │
> │ externs   │ true │
> │ operators │ true │
> │ commands  │ true │
> ╰───────────┴──────╯
> ```

# User-Facing Changes
the operators in `std help ...` will be sorted just as the built-in
`help ...`.

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-04-28 09:22:23 -05:00
fb10e1dfc5 add --ide-ast for a simplistic ast for editors (#8995)
# Description
This is WIP. This generates a simplistic AST so that we can call it from
the vscode side for semantic token highlighting. The output is a
minified version of this screenshot.


![image](https://user-images.githubusercontent.com/343840/234354668-872d6267-9946-4b92-8a13-4fed45b4513a.png)

The script
```
def test [arg] {
  print $arg
  for i in (seq 1 10) {
    echo $i
  }
}
```

The simplistic AST
```json
[{"content":"def","index":0,"shape":"shape_internalcall","span":{"end":15,"start":12}},{"content":"test","index":1,"shape":"shape_string","span":{"end":20,"start":16}},{"content":"[arg]","index":2,"shape":"shape_signature","span":{"end":26,"start":21}},{"content":"{\r\n  ","index":3,"shape":"shape_closure","span":{"end":32,"start":27}},{"content":"print","index":4,"shape":"shape_internalcall","span":{"end":37,"start":32}},{"content":"$arg","index":5,"shape":"shape_variable","span":{"end":42,"start":38}},{"content":"for","index":6,"shape":"shape_internalcall","span":{"end":49,"start":46}},{"content":"i","index":7,"shape":"shape_vardecl","span":{"end":51,"start":50}},{"content":"in","index":8,"shape":"shape_keyword","span":{"end":54,"start":52}},{"content":"(","index":9,"shape":"shape_block","span":{"end":56,"start":55}},{"content":"seq","index":10,"shape":"shape_internalcall","span":{"end":59,"start":56}},{"content":"1","index":11,"shape":"shape_int","span":{"end":61,"start":60}},{"content":"10","index":12,"shape":"shape_int","span":{"end":64,"start":62}},{"content":")","index":13,"shape":"shape_block","span":{"end":65,"start":64}},{"content":"{\r\n    ","index":14,"shape":"shape_block","span":{"end":73,"start":66}},{"content":"echo","index":15,"shape":"shape_internalcall","span":{"end":77,"start":73}},{"content":"$i","index":16,"shape":"shape_variable","span":{"end":80,"start":78}},{"content":"\r\n  }","index":17,"shape":"shape_block","span":{"end":85,"start":80}},{"content":"\r\n}","index":18,"shape":"shape_closure","span":{"end":88,"start":85}}]
```

# 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
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-04-28 08:51:51 -05:00
4ca47258a0 Add --redirect-combine option to run-external (#8918)
# Description

Add option that combines both output streams to the `run-external`
command.

This allows you to do something like this:

```nushell
let res = do -i { run-external --redirect-combine <command that prints to stdout and stderr> } | complete

if $res.exit_code != 0 {
  # Only print output when command has failed.
  print "The command has failed, these are the logs:"
  print $res.stdout
}
```

# User-Facing Changes

No breaking changes, just an extra option.

# Tests + Formatting

Added a test that checks the new option

# 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: Jelle Besseling <jelle@bigbridge.nl>
2023-04-28 07:55:48 -05:00
b37662c7e1 fix: fix cursor position when cursor is at the end of the commandline (#9030)
# Description
Fix getting the cursor position, when it's at the end of the
commandline.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting

# After Submitting
2023-04-28 07:02:45 -05:00
3076378373 Slim down tests (#9021)
This PR just tidies up some tests by removing unused code:

1. If the filesystem is not touched, don't use the filesystem
playground/sandbox
2. If the filesystem is not touched, don't specify the `cwd`
3. If the command is short, don't bother wrapping it in `pipeline()`
4. If the command doesn't have quotes, don't bother with a `r#"..."#`
raw string

Part of #8670.
2023-04-28 13:25:44 +02:00
4c4c1f6147 TRIAGE: add the needs-triage to all ISSUE_TEMPLATEs (#9023)
as can be seen
[here](https://github.com/nushell/nushell/labels?q=needs-), i've created
the `needs-triage` and `needs-core-team-attention` labels.

in this PR, i made the `needs-triage` a default label to all the issue
templates 😋
2023-04-28 09:27:01 +02:00
35c8485442 FEATURE: add --expand to std clip (#8970)
# Description
i use `std clip` to copy everything from `nushell`.
however i have the auto-expand on tables enabled and when i use `clip`
on large tables, it does not copy what i see but the collapsed data => i
have to edit the line and add `| table --expand` manually, which is a
pain to do regularly 😱

in this PR, i just add `--expand` to `std clip` to automatically expand
the data before copying it 😋

# User-Facing Changes
exploring the `Cargo.toml` of `nushell` with auto-expand, one might see
```
> open Cargo.toml | get package.metadata.binstall.overrides
╭────────────────────────┬───────────────────╮
│                        │ ╭─────────┬─────╮ │
│ x86_64-pc-windows-msvc │ │ pkg-fmt │ zip │ │
│                        │ ╰─────────┴─────╯ │
╰────────────────────────┴───────────────────╯
```
but then
```
open Cargo.toml | get package.metadata.binstall.overrides | clip
```
would only copy
```
╭────────────────────────┬──────────────────╮
│ x86_64-pc-windows-msvc │ {record 1 field} │
╰────────────────────────┴──────────────────╯
```
...

now 
```
open Cargo.toml | get package.metadata.binstall.overrides | clip --expand
```
will copy the expanded record 👍 

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-04-28 09:07:38 +02:00
3268ecd116 FEATURE: add the bench command to the standard library (#8969)
Should address the first point of
- https://github.com/nushell/nushell/issues/8696.

# Description
i've seen a few appearances of this `benchmark` idea in recent works on
`nu-std`, so i thought it would be great to add it finally.

# User-Facing Changes
a new `std bench` command to measure the performance of `nushell`
closures and code blocks.

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting
```
$nothing
```
2023-04-28 09:07:23 +02:00
44493dac51 Add extern def which allows raw arguments (#8956)
# Description

Extends the `extern` syntax to allow commands that accept raw arguments.
This is mainly added to allow wrapper type scripts for external
commands.

This is an example on how this can be used:

```nushell
extern foo [...rest] { 
  print ($rest | str join ',' ) 
}
foo --bar baz -- -q -u -x
# => --bar,baz,--,-q,-u,-x
```

(It's only possible to accept a single ...varargs argument in the
signature)

# User-Facing Changes

No breaking changes, just extra possibilities.

# Tests + Formatting

Added a test for this new behaviour and ran the toolkit pr checker

# After Submitting

This is advanced functionality but it should be documented, I will open
a new PR on the book for that

Co-authored-by: Jelle Besseling <jelle@bigbridge.nl>
2023-04-28 09:06:43 +02:00
6f9b9914cf Add more examples to help use (#9024)
# Description
The `members` parameter of `use` is specified as type `any`, but it's
really a string or list of strings or `*`. So add some examples that
mention what you can specify for `members`.

Also mention `help modules` and `help std`, since you probably want to
use the standard library or another defined modules.

Sidenote: I tried to run the examples for `use` as tests like is done
for the other commands. That panics with `missing module command`. I
assume this is known.

# User-Facing Changes
`help use` now looks like this:
```nushell
Use definitions from a module, making them available in your shell.

See `help std` for the standard library module.
See `help modules` to list all available modules.

This command is a parser keyword. For details, check:
  https://www.nushell.sh/book/thinking_in_nu.html

Usage:
  > use <module> (members)

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

Parameters:
  module <string>: Module or module file
  (optional) members <any>: Which members of the module to import

Examples:
  Define a custom command in a module and call it
  > module spam { export def foo [] { "foo" } }; use spam foo; foo
  foo

  Define a custom command that participates in the environment in a module and call it
  > module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR
  BAZ

  Use a plain module name to import its definitions qualified by the module name
  > module spam { export def foo [] { "foo" }; export def bar [] { "bar" } }; use spam; (spam foo) + (spam bar)
  foobar

  Specify * to use all definitions in a module
  > module spam { export def foo [] { "foo" }; export def bar [] { "bar" } }; use spam *; (foo) + (bar)
  foobar

  To use commands with spaces, like subcommands, surround them with quotes
  > module spam { export def 'foo bar' [] { "baz" } }; use spam 'foo bar'; foo bar
  baz

  To use multiple definitions from a module, wrap them in a list
  > module spam { export def foo [] { "foo" }; export def 'foo bar' [] { "baz" } }; use spam ['foo', 'foo bar']; (foo) + (foo bar)
  foobaz
```
2023-04-27 15:58:07 -05:00
ffb9ab9eef Update rust-toolchain.toml to 1.67.1 (#9012)
# Description
This PR bumps the rust toolchain from 1.66.1 to 1.67.1

# 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
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-04-27 09:31:29 -05:00
5fe0ca418d Remove npm install instruction (#9022) 2023-04-27 18:44:51 +08:00
ecc820a8c1 Fix unexpected flattening of data by par-each (Issue #8497) (#9007)
# Description
Previously, `par-each` acted like a `flatmap`: first mapping the data,
then applying a `flatten`. This is unlike `each`, which just maps the
data. Now `par-each` works like `each` in this regard, leaving nested
data unflattened.

Fixes #8497

# User-Facing Changes
Previously:
`[1 2 3] | par-each {|e| [$e, $e] }` --> `[1,1,2,2,3,3]` 
Now:
`[1 2 3] | par-each {|e| [$e, $e] }` --> `[[1,1],[2,2],[3,3]]`

# Tests
This adds one test that verifies the lack of flattening for `par-each`.
2023-04-26 23:27:27 +02:00
6047b04208 fix compilation error (#9016)
# Description
This PR fixes a problem due to me landing an out of date PR that stopped
nushell from compiling.

# 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
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-04-26 14:38:09 -05:00
8d8b011702 Bump tabled dependency to 0.11 (#8922)
close? #8060

Quite a bit of refactoring took place.
I believe a few improvements to collapse/expand were made.

I've tried to track any performance regressions and seems like it is
fine.

I've noticed something different now with default configuration path or
something in this regard?
So I might missed something while testing because of this.

Requires some oversight.

---------

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2023-04-26 13:56:10 -05:00
07c9f681c7 Fix warning on declared config variable that originates from commands (#8891)
# Description

This PR fixes an issue described in #8890 where config variables
declared in command parameters cause the warning `use `let-env config =
...` instead of `let config = ...` to be printed.

# User-Facing Changes
The user is only warned when they define a config variable with a
warning with the type `record`.

# Tests + Formatting

I think this can only be tested manually by first trying to reproduce
#8890.
To test if the warning is still printed when it's supposed to be one can
add `let config = $env.config` to the end of the `config.nu` file.
2023-04-26 09:21:51 -05:00
c422c6cc3d Fix completion on $nu to show correct menus (#8919)
I'm still new to nushell and Rust, so please let me know if there are
any omissions and/or mistakes.

# Description

fixed #8863
`$nu` completion shows wrong completion menus at this time.
This PR fixes the problem to show the correct ones.

# User-Facing Changes

Users can use the correct menus on `$nu` completion like this
[recording](https://asciinema.org/a/KCwfpdAoMFsQODFBnb3NwmufC).

# Tests + Formatting

```
$ use toolkit.nu
$ toolkit check pr

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
```

# After Submitting

nothing
2023-04-26 09:15:27 -05:00
e251f3a0b4 Change type of parameter default values to Option<Value> (#8940)
# Description

Fixes #8939.

# User-Facing Changes

- Parameter default values will now be parsed as constants.
- If the default value is not a constant, a parser error is displayed.

# Tests + Formatting

The [only affected
test](d42c2b2dbc/src/tests/test_engine.rs (L325-L328))
has been updated to reflect the new behavior.
2023-04-26 09:14:02 -05:00
77ca73f414 allow records to have type annotations (#8914)
# Description
follow up to #8529
cleaned up version of #8892 

- the original syntax is okay
```nu
def okay [rec: record] {}
```
- you can now add type annotations for fields if you know
  them before hand
```nu
def okay [rec: record<name: string>] {}
```

- you can specify multiple fields
```nu
def okay [person: record<name: string age: int>] {}

# an optional comma is allowed
def okay [person: record<name: string, age: int>] {}
```

- if annotations are specified, any use of the command will be type
  checked against the specified type
```nu
def unwrap [result: record<ok: bool, value: any>] {}

unwrap {ok: 2, value: "value"}

# errors with

Error: nu::parser::type_mismatch

  × Type mismatch.
   ╭─[entry #4:1:1]
 1 │ unwrap {ok: 2, value: "value"}
   ·         ───────┬─────
   ·                    ╰── expected record<ok: bool, value: any>, found record<ok: int, value: string>
   ╰────
```
> here the error is in the `ok` field, since `any` is coerced into any
type
> as a result `unwrap {ok: true, value: "value"}` is okay

- the key must be a string, either quoted or unquoted
```nu
def err [rec: record<{}: list>] {}

# errors with
Error:
  × `record` type annotations key not string
   ╭─[entry #7:1:1]
 1 │ def unwrap [result: record<{}: bool, value: any>] {}
   ·                            ─┬
   ·                             ╰── must be a string
   ╰────
```

- a key doesn't have to have a type in which case it is assumed to be
`any`
```nu
def okay [person: record<name age>] {}

def okay [person: record<name: string age>] {}
```

- however, if you put a colon, you have to specify a type
```nu
def err [person: record<name: >] {}

# errors with
Error: nu::parser::parse_mismatch

  × Parse mismatch during operation.
   ╭─[entry #12:1:1]
 1 │ def unwrap [res: record<name: >] { $res }
   ·                             ┬
   ·                             ╰── expected type after colon
   ╰────
```

# User-Facing Changes
**[BREAKING CHANGES]**
- this change adds a field to `SyntaxShape::Record` so any plugins that
used it will have to update and include the field. though if you are
unsure of the type the record expects, `SyntaxShape::Record(vec![])`
will suffice
2023-04-26 08:16:55 -05:00
48c75831fc Flags and args on def (#8953)
# Description

Fixes #8916 
Fix flags and args on def which were call wrong .
Added some tests too .
2023-04-26 08:16:32 -05:00
4b8a259916 update ast to support output to json (#8962)
# Description
This PR changes the `ast` command to be able to output `--json` as well
as `nuon` (default) with "pretty" and "minified" output. I'm hoping this
functionality will be usable in the vscode extension for semantic
tokenization and highlighting.

# User-Facing Changes
There's a new `--json`/`-j` option. Prior version output of nuon is
maintained as default.

# 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
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-04-26 08:15:42 -05:00
5733a13409 support blink cursor, and fix underscore's cursorshape (#8990)
# Description
Close: #8988

Thanks to new crossterm version, nushell can support blink cursor shape.
It can be config with the following value:
1. blink_block
2. blink_line
3. blink_underscore

And original block, line, underscore will be steady. It also fixes wrong
shape of `underscore`.

# User-Facing Changes
Here is a little breaking change, before the change: `line` cursor shape
is blinking line, but after this pr, it will be `steady line`. To make a
blink line, we need to change the value to `blink_line`.

But I think it's ok, because after the change, we have a good naming
convention about the name of shape

# 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
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-04-26 08:14:50 -05:00
f9049c4c6c Move from winres to better-maintained winresource fork (#9001)
This MR closes #8986; big thanks to @WindSoilder for investigating this
and making the MR very easy.

I did manual testing to confirm that the icon etc. are still being set
correctly after this change. Also skimmed the `winresource` code, no
issues jumped out at me.
2023-04-26 14:14:55 +02:00
66b5931438 fix: set the initial repl cursor pos, fixes #8943 (#8955)
# Description
Set the initial repl cursor pos, so running `commandline --insert`
inserts at the current cursor position of the input buffer.



Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
2023-04-26 01:08:11 +02:00
503052b669 using ratatui instead of tui (#8952)
# Description

Refer to https://github.com/fdehau/tui-rs/issues/654, I found that tui
maybe un-maintained, instead, I'd suggest to use an actively fork
https://github.com/tui-rs-revival/ratatui

cc: @zhiburt 

# User-Facing Changes
NaN
2023-04-26 01:07:23 +02:00
7d6a32c5f8 Bump to 0.79.1 dev version (#8998)
# Description

For development or hotfixes
2023-04-26 01:05:23 +02:00
227 changed files with 8758 additions and 6699 deletions

View File

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

View File

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

View File

@ -2,7 +2,7 @@
name: standard library bug or feature report
about: Used to submit issues related to the nu standard library
title: ''
labels: std-library
labels: ['needs-triage', 'std-library']
assignees: ''
---

View File

@ -16,7 +16,7 @@ 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 clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library

View File

@ -46,7 +46,7 @@ jobs:
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
nu-tests:
env:
@ -84,7 +84,7 @@ jobs:
std-lib-and-python-virtualenv:
env:
NU_LOG_LEVEL: DEBUG
NU_LOG_LEVEL: DEBUG
strategy:
fail-fast: true
@ -163,7 +163,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
- name: Clippy
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
- name: Tests
run: cargo test --profile ci --package nu_plugin_*

View File

@ -59,7 +59,7 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
- Run Clippy on Nushell:
```shell
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
```
or via the `toolkit.nu` command:
```shell
@ -72,6 +72,12 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
```shell
cargo test --workspace
```
along with dataframe tests
```shell
cargo test --workspace --features=dataframe
```
or via the `toolkit.nu` command:
```shell
use toolkit.nu test
@ -139,7 +145,7 @@ This includes discarded approaches. Also we want to quickly identify regressions
### How we merge PRs
In general the maintainers **squash** all changes of your PR into a single commit when merging.
In general the maintainers **squash** all changes of your PR into a single commit when merging.
This keeps a clean enough linear history, while not forcing you to conform to a too strict style while iterating in your PR or fixing small problems. As an added benefit the commits on the `main` branch are tied to the discussion that happened in the PR through their `#1234` issue number.
@ -201,13 +207,13 @@ You can help us to make the review process a smooth experience:
- In general, added tests help us to understand which assumptions go into a particular addition/change.
- Try to also test corner cases where those assumptions might break. This can be more valuable than simply adding many similar tests.
- Commit history inside a PR during code review:
- Good **atomic commits** can help follow larger changes, but we are not pedantic.
- Good **atomic commits** can help follow larger changes, but we are not pedantic.
- We don't shame fixup commits while you try to figure out a problem. They can help others see what you tried and what didn't work. (see our [squash policy](#how-we-merge-prs))
- During active review constant **force pushing** just to amend changes can be confusing!
- GitHub's UI presents reviewers with less options to compare diffs
- fetched branches for experimentation become invalid!
- the notification a maintainer receives has a low signal-to-noise ratio
- Git pros *can* use their judgement to rebase/squash to clean up the history *if it aids the understanding* of a larger change during review
- Git pros *can* use their judgement to rebase/squash to clean up the history *if it aids the understanding* of a larger change during review
- Merge conflicts:
- In general you should take care of resolving merge conflicts.
- Use your judgement whether to `git merge main` or to `git rebase main`

464
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.79.0"
version = "0.80.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -47,25 +47,25 @@ crossterm = "0.26"
ctrlc = "3.2.1"
log = "0.4"
miette = { version = "5.7.0", features = ["fancy-no-backtrace"] }
nu-cli = { path = "./crates/nu-cli", version = "0.79.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.79.0" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.79.0" }
nu-command = { path = "./crates/nu-command", version = "0.79.0" }
nu-engine = { path = "./crates/nu-engine", version = "0.79.0" }
nu-json = { path = "./crates/nu-json", version = "0.79.0" }
nu-parser = { path = "./crates/nu-parser", version = "0.79.0" }
nu-path = { path = "./crates/nu-path", version = "0.79.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.79.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.79.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.79.0" }
nu-system = { path = "./crates/nu-system", version = "0.79.0" }
nu-table = { path = "./crates/nu-table", version = "0.79.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.79.0" }
nu-std = { path = "./crates/nu-std", version = "0.79.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.79.0" }
nu-cli = { path = "./crates/nu-cli", version = "0.80.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.80.0" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.80.0" }
nu-command = { path = "./crates/nu-command", version = "0.80.0" }
nu-engine = { path = "./crates/nu-engine", version = "0.80.0" }
nu-json = { path = "./crates/nu-json", version = "0.80.0" }
nu-parser = { path = "./crates/nu-parser", version = "0.80.0" }
nu-path = { path = "./crates/nu-path", version = "0.80.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.80.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.80.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.80.0" }
nu-system = { path = "./crates/nu-system", version = "0.80.0" }
nu-table = { path = "./crates/nu-table", version = "0.80.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.80.0" }
nu-std = { path = "./crates/nu-std", version = "0.80.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.80.0" }
nu-ansi-term = "0.47.0"
reedline = { version = "0.19.0", features = ["bashisms", "sqlite"]}
reedline = { version = "0.19.1", features = ["bashisms", "sqlite"]}
rayon = "1.7.0"
is_executable = "1.0.1"
@ -80,7 +80,7 @@ signal-hook = { version = "0.3.14", default-features = false }
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
winresource = "0.1"
[target.'cfg(target_family = "unix")'.dependencies]
nix = { version = "0.26", default-features = false, features = [
@ -92,7 +92,7 @@ nix = { version = "0.26", default-features = false, features = [
atty = "0.2"
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.79.0" }
nu-test-support = { path = "./crates/nu-test-support", version = "0.80.0" }
tempfile = "3.5.0"
assert_cmd = "2.0.2"
criterion = "0.4"

View File

@ -46,8 +46,6 @@ To quickly install Nu:
brew install nushell
# Windows
winget install nushell
# Cross Platform installation if you have node and npm installed, Note that nu plugins were not included
npm install -g nushell
```
To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.

View File

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

View File

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

View File

@ -5,26 +5,26 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.79.0"
version = "0.80.0"
[lib]
bench = false
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.79.0" }
nu-test-support = { path = "../nu-test-support", version = "0.80.0" }
rstest = { version = "0.17.0", default-features = false }
[dependencies]
nu-command = { path = "../nu-command", version = "0.79.0" }
nu-engine = { path = "../nu-engine", version = "0.79.0" }
nu-path = { path = "../nu-path", version = "0.79.0" }
nu-parser = { path = "../nu-parser", version = "0.79.0" }
nu-protocol = { path = "../nu-protocol", version = "0.79.0" }
nu-utils = { path = "../nu-utils", version = "0.79.0" }
nu-color-config = { path = "../nu-color-config", version = "0.79.0" }
nu-command = { path = "../nu-command", version = "0.80.0" }
nu-engine = { path = "../nu-engine", version = "0.80.0" }
nu-path = { path = "../nu-path", version = "0.80.0" }
nu-parser = { path = "../nu-parser", version = "0.80.0" }
nu-protocol = { path = "../nu-protocol", version = "0.80.0" }
nu-utils = { path = "../nu-utils", version = "0.80.0" }
nu-color-config = { path = "../nu-color-config", version = "0.80.0" }
nu-ansi-term = "0.47.0"
reedline = { version = "0.19.0", features = ["bashisms", "sqlite"]}
reedline = { version = "0.19.1", features = ["bashisms", "sqlite"]}
atty = "0.2.14"
chrono = { default-features = false, features = ["std"], version = "0.4.23" }

View File

@ -118,8 +118,9 @@ impl Command for Commandline {
.expect("repl cursor pos mutex");
let char_pos = buffer
.grapheme_indices(true)
.chain(std::iter::once((buffer.len(), "")))
.position(|(i, _c)| i == *cursor_pos)
.unwrap_or(buffer.len());
.expect("Cursor position isn't on a grapheme boundary");
Ok(Value::String {
val: char_pos.to_string(),
span: call.head,

View File

@ -1,5 +1,5 @@
use crate::completions::{Completer, CompletionOptions};
use nu_engine::eval_variable;
use nu_engine::{column::get_columns, eval_variable};
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
Span, Value,
@ -267,7 +267,19 @@ fn nested_suggestions(
output
}
Value::List { vals, span: _ } => {
for column_name in get_columns(vals.as_slice()) {
output.push(Suggestion {
value: column_name,
description: None,
extra: None,
span: current_span,
append_whitespace: false,
});
}
output
}
_ => output,
}
}
@ -295,6 +307,38 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
span: Span::unknown(),
};
}
Value::LazyRecord { val, span: _ } => {
for col in val.column_names() {
if col.as_bytes().to_vec() == next_sublevel {
return recursive_value(
val.get_column_value(col).unwrap_or_default(),
sublevels.into_iter().skip(1).collect(),
);
}
}
// Current sublevel value not found
return Value::Nothing {
span: Span::unknown(),
};
}
Value::List { vals, span } => {
for col in get_columns(vals.as_slice()) {
if col.as_bytes().to_vec() == next_sublevel {
return recursive_value(
Value::List { vals, span }
.get_data_by_key(&col)
.unwrap_or_default(),
sublevels.into_iter().skip(1).collect(),
);
}
}
// Current sublevel value not found
return Value::Nothing {
span: Span::unknown(),
};
}
_ => return val,
}
}

View File

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

View File

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

View File

@ -11,13 +11,13 @@ use miette::{IntoDiagnostic, Result};
use nu_color_config::StyleComputer;
use nu_command::hook::eval_hook;
use nu_command::util::get_guaranteed_cwd;
use nu_engine::{convert_env_values, eval_block};
use nu_parser::{lex, parse, trim_quotes_str};
use nu_engine::convert_env_values;
use nu_parser::{lex, trim_quotes_str};
use nu_protocol::{
config::NuCursorShape,
engine::{EngineState, Stack, StateWorkingSet},
format_duration, report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError,
Span, Spanned, Value,
report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
Value,
};
use nu_utils::utils::perf;
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
@ -43,6 +43,7 @@ pub fn evaluate_repl(
stack: &mut Stack,
nushell_path: &str,
prerun_command: Option<Spanned<String>>,
load_std_lib: Option<Spanned<String>>,
entire_start_time: Instant,
) -> Result<()> {
use nu_command::hook;
@ -105,6 +106,20 @@ pub fn evaluate_repl(
);
let config = engine_state.get_config();
if config.bracketed_paste {
// try to enable bracketed paste
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
#[cfg(not(target_os = "windows"))]
let _ = line_editor.enable_bracketed_paste();
}
// Setup history_isolation aka "history per session"
let history_isolation = config.history_isolation;
let history_session_id = if history_isolation {
Reedline::create_history_session_id()
} else {
None
};
start_time = std::time::Instant::now();
let history_path = crate::config_files::get_history_path(
@ -124,7 +139,9 @@ pub fn evaluate_repl(
SqliteBackedHistory::with_file(history_path.to_path_buf()).into_diagnostic()?,
),
};
line_editor = line_editor.with_history(history);
line_editor = line_editor
.with_history_session_id(history_session_id)
.with_history(history);
};
perf(
"setup history",
@ -137,19 +154,8 @@ pub fn evaluate_repl(
start_time = std::time::Instant::now();
let sys = sysinfo::System::new();
let show_banner = config.show_banner;
let use_ansi = config.use_ansi_coloring;
if show_banner {
let banner = get_banner(engine_state, stack);
if use_ansi {
println!("{banner}");
} else {
println!("{}", nu_utils::strip_ansi_string_likely(banner));
}
}
perf(
"get sysinfo/show banner",
"get sysinfo",
start_time,
file!(),
line!(),
@ -169,6 +175,19 @@ pub fn evaluate_repl(
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
}
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
if load_std_lib.is_none() && engine_state.get_config().show_banner {
eval_source(
engine_state,
stack,
r#"use std banner; banner"#.as_bytes(),
"show_banner",
PipelineData::empty(),
false,
);
}
loop {
let loop_start_time = std::time::Instant::now();
@ -429,16 +448,6 @@ pub fn evaluate_repl(
entry_num += 1;
if entry_num == 1 {
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
if show_banner {
println!(
"Startup Time: {}",
format_duration(engine_state.get_startup_time())
);
}
}
start_time = std::time::Instant::now();
let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration;
@ -477,6 +486,12 @@ pub fn evaluate_repl(
}
}
let mut repl_cursor = engine_state
.repl_cursor_pos
.lock()
.expect("repl cursor pos mutex");
*repl_cursor = line_editor.current_insertion_point();
drop(repl_cursor);
let mut repl_buffer = engine_state
.repl_buffer_state
.lock()
@ -710,109 +725,14 @@ pub fn evaluate_repl(
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle {
match shape {
NuCursorShape::Block => SetCursorStyle::SteadyBlock,
NuCursorShape::UnderScore => SetCursorStyle::DefaultUserShape,
NuCursorShape::Line => SetCursorStyle::BlinkingBar,
NuCursorShape::UnderScore => SetCursorStyle::SteadyUnderScore,
NuCursorShape::Line => SetCursorStyle::SteadyBar,
NuCursorShape::BlinkBlock => SetCursorStyle::BlinkingBlock,
NuCursorShape::BlinkUnderScore => SetCursorStyle::BlinkingUnderScore,
NuCursorShape::BlinkLine => SetCursorStyle::BlinkingBar,
}
}
fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
let age = match eval_string_with_input(
engine_state,
stack,
None,
"(date now) - ('2019-05-10 09:59:12-0700' | into datetime)",
) {
Ok(Value::Duration { val, .. }) => format_duration(val),
_ => "".to_string(),
};
let banner = format!(
r#"{} __ ,
{} .--()°'.' {}Welcome to {}Nushell{},
{}'|, . ,' {}based on the {}nu{} language,
{} !_-(_\ {}where all data is structured!
Please join our {}Discord{} community at {}https://discord.gg/NtAbbGn{}
Our {}GitHub{} repository is at {}https://github.com/nushell/nushell{}
Our {}Documentation{} is located at {}https://nushell.sh{}
{}Tweet{} us at {}@nu_shell{}
Learn how to remove this at: {}https://nushell.sh/book/configuration.html#remove-welcome-message{}
It's been this long since {}Nushell{}'s first commit:
{}{}
"#,
"\x1b[32m", //start line 1 green
"\x1b[32m", //start line 2
"\x1b[0m", //before welcome
"\x1b[32m", //before nushell
"\x1b[0m", //after nushell
"\x1b[32m", //start line 3
"\x1b[0m", //before based
"\x1b[32m", //before nu
"\x1b[0m", //after nu
"\x1b[32m", //start line 4
"\x1b[0m", //before where
"\x1b[35m", //before Discord purple
"\x1b[0m", //after Discord
"\x1b[35m", //before Discord URL
"\x1b[0m", //after Discord URL
"\x1b[1;32m", //before GitHub green_bold
"\x1b[0m", //after GitHub
"\x1b[1;32m", //before GitHub URL
"\x1b[0m", //after GitHub URL
"\x1b[32m", //before Documentation
"\x1b[0m", //after Documentation
"\x1b[32m", //before Documentation URL
"\x1b[0m", //after Documentation URL
"\x1b[36m", //before Tweet blue
"\x1b[0m", //after Tweet
"\x1b[1;36m", //before @nu_shell cyan_bold
"\x1b[0m", //after @nu_shell
"\x1b[32m", //before Welcome Message
"\x1b[0m", //after Welcome Message
"\x1b[32m", //before Nushell
"\x1b[0m", //after Nushell
age,
"\x1b[0m", //after banner disable
);
banner
}
// Taken from Nana's simple_eval
/// Evaluate a block of Nu code, optionally with input.
/// For example, source="$in * 2" will multiply the value in input by 2.
pub fn eval_string_with_input(
engine_state: &mut EngineState,
stack: &mut Stack,
input: Option<Value>,
source: &str,
) -> Result<Value, ShellError> {
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let output = parse(&mut working_set, None, source.as_bytes(), false);
(output, working_set.render())
};
engine_state.merge_delta(delta)?;
let input_as_pipeline_data = match input {
Some(input) => PipelineData::Value(input, None),
None => PipelineData::empty(),
};
eval_block(
engine_state,
stack,
&block,
input_as_pipeline_data,
false,
true,
)
.map(|x| x.into_value(Span::unknown()))
}
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
let exit_code = stack
.get_env_var(engine_state, "LAST_EXIT_CODE")

View File

@ -556,6 +556,100 @@ fn variables_completions() {
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.os-info
let suggestions = completer.complete("$nu.os-info.", 12);
assert_eq!(4, suggestions.len());
let expected: Vec<String> = vec![
"arch".into(),
"family".into(),
"kernel_version".into(),
"name".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope
let suggestions = completer.complete("$nu.scope.", 10);
assert_eq!(5, suggestions.len());
let expected: Vec<String> = vec![
"aliases".into(),
"commands".into(),
"engine_state".into(),
"modules".into(),
"vars".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.commands
let suggestions = completer.complete("$nu.scope.commands.", 19);
assert_eq!(15, suggestions.len());
let expected: Vec<String> = vec![
"category".into(),
"creates_scope".into(),
"examples".into(),
"extra_usage".into(),
"is_builtin".into(),
"is_custom".into(),
"is_extern".into(),
"is_keyword".into(),
"is_plugin".into(),
"is_sub".into(),
"module_name".into(),
"name".into(),
"search_terms".into(),
"signatures".into(),
"usage".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.commands.signatures
let suggestions = completer.complete("$nu.scope.commands.signatures.", 30);
assert_eq!(17, suggestions.len());
let expected: Vec<String> = vec![
"any".into(),
"binary".into(),
"bool".into(),
"datetime".into(),
"duration".into(),
"filesize".into(),
"int".into(),
"list<any>".into(),
"list<binary>".into(),
"list<number>".into(),
"list<string>".into(),
"nothing".into(),
"number".into(),
"range".into(),
"record".into(),
"string".into(),
"table".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.engine_state
let suggestions = completer.complete("$nu.scope.engine_state.", 23);
assert_eq!(6, suggestions.len());
let expected: Vec<String> = vec![
"num_blocks".into(),
"num_decls".into(),
"num_env_vars".into(),
"num_modules".into(),
"num_vars".into(),
"source_bytes".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.vars
let suggestions = completer.complete("$nu.scope.vars.", 15);
assert_eq!(3, suggestions.len());
let expected: Vec<String> = vec!["name".into(), "type".into(), "value".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for custom var
let suggestions = completer.complete("$actor.", 7);

View File

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

View File

@ -16,7 +16,11 @@ impl Command for ErrorMake {
fn signature(&self) -> Signature {
Signature::build("error make")
.input_output_types(vec![(Type::Nothing, Type::Error)])
.required("error_struct", SyntaxShape::Record, "the error to create")
.required(
"error_struct",
SyntaxShape::Record(vec![]),
"the error to create",
)
.switch(
"unspanned",
"remove the origin label from the error",

View File

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

View File

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

View File

@ -14,6 +14,7 @@ mod export_alias;
mod export_def;
mod export_def_env;
mod export_extern;
mod export_module;
mod export_use;
mod extern_;
mod for_;
@ -55,6 +56,7 @@ pub use export_alias::ExportAlias;
pub use export_def::ExportDef;
pub use export_def_env::ExportDefEnv;
pub use export_extern::ExportExtern;
pub use export_module::ExportModule;
pub use export_use::ExportUse;
pub use extern_::Extern;
pub use for_::For;

View File

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

View File

@ -14,14 +14,14 @@ impl Command for Use {
}
fn usage(&self) -> &str {
"Use definitions from a module."
"Use definitions from a module, making them available in your shell."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("module", SyntaxShape::String, "Module or module file")
.optional(
.rest(
"members",
SyntaxShape::Any,
"Which members of the module to import",
@ -30,7 +30,10 @@ impl Command for Use {
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
r#"See `help std` for the standard library module.
See `help modules` to list all available modules.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
@ -134,6 +137,26 @@ impl Command for Use {
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::test_string("BAZ")),
},
Example {
description: "Use a plain module name to import its definitions qualified by the module name",
example: r#"module spam { export def foo [] { "foo" }; export def bar [] { "bar" } }; use spam; (spam foo) + (spam bar)"#,
result: Some(Value::test_string("foobar")),
},
Example {
description: "Specify * to use all definitions in a module",
example: r#"module spam { export def foo [] { "foo" }; export def bar [] { "bar" } }; use spam *; (foo) + (bar)"#,
result: Some(Value::test_string("foobar")),
},
Example {
description: "To use commands with spaces, like subcommands, surround them with quotes",
example: r#"module spam { export def 'foo bar' [] { "baz" } }; use spam 'foo bar'; foo bar"#,
result: Some(Value::test_string("baz")),
},
Example {
description: "To use multiple definitions from a module, wrap them in a list",
example: r#"module spam { export def foo [] { "foo" }; export def 'foo bar' [] { "baz" } }; use spam ['foo', 'foo bar']; (foo) + (foo bar)"#,
result: Some(Value::test_string("foobaz")),
},
]
}
}

View File

@ -33,6 +33,7 @@ pub fn create_default_context() -> EngineState {
ExportDefEnv,
ExportExtern,
ExportUse,
ExportModule,
Extern,
For,
Help,

View File

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

View File

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

View File

@ -1,3 +1,4 @@
use crate::text_style::Alignment;
use crate::{color_record_to_nustyle, lookup_ansi_color_style, TextStyle};
use nu_ansi_term::{Color, Style};
use nu_engine::eval_block;
@ -5,7 +6,6 @@ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
CliError, IntoPipelineData, Value,
};
use tabled::alignment::AlignmentHorizontal;
use std::{
collections::HashMap,
@ -111,34 +111,28 @@ impl<'a> StyleComputer<'a> {
// Used only by the `table` command.
pub fn style_primitive(&self, value: &Value) -> TextStyle {
use Alignment::*;
let s = self.compute(&value.get_type().get_non_specified_string(), value);
match *value {
Value::Bool { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Int { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Filesize { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Duration { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Date { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Range { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Float { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::String { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Nothing { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Binary { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::CellPath { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Bool { .. } => TextStyle::with_style(Left, s),
Value::Int { .. } => TextStyle::with_style(Right, s),
Value::Filesize { .. } => TextStyle::with_style(Right, s),
Value::Duration { .. } => TextStyle::with_style(Right, s),
Value::Date { .. } => TextStyle::with_style(Left, s),
Value::Range { .. } => TextStyle::with_style(Left, s),
Value::Float { .. } => TextStyle::with_style(Right, s),
Value::String { .. } => TextStyle::with_style(Left, s),
Value::Nothing { .. } => TextStyle::with_style(Left, s),
Value::Binary { .. } => TextStyle::with_style(Left, s),
Value::CellPath { .. } => TextStyle::with_style(Left, s),
Value::Record { .. } | Value::List { .. } | Value::Block { .. } => {
TextStyle::with_style(AlignmentHorizontal::Left, s)
TextStyle::with_style(Left, s)
}
_ => TextStyle::basic_left(),
Value::Closure { .. }
| Value::CustomValue { .. }
| Value::Error { .. }
| Value::LazyRecord { .. }
| Value::MatchPattern { .. } => TextStyle::basic_left(),
}
}

View File

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

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
version = "0.79.0"
version = "0.80.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,22 +13,21 @@ version = "0.79.0"
bench = false
[dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.79.0" }
nu-color-config = { path = "../nu-color-config", version = "0.79.0" }
nu-engine = { path = "../nu-engine", version = "0.79.0" }
nu-explore = { path = "../nu-explore", version = "0.79.0" }
nu-glob = { path = "../nu-glob", version = "0.79.0" }
nu-json = { path = "../nu-json", version = "0.79.0" }
nu-parser = { path = "../nu-parser", version = "0.79.0" }
nu-path = { path = "../nu-path", version = "0.79.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.79.0" }
nu-protocol = { path = "../nu-protocol", version = "0.79.0" }
nu-system = { path = "../nu-system", version = "0.79.0" }
nu-table = { path = "../nu-table", version = "0.79.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.79.0" }
nu-utils = { path = "../nu-utils", version = "0.79.0" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.80.0" }
nu-color-config = { path = "../nu-color-config", version = "0.80.0" }
nu-engine = { path = "../nu-engine", version = "0.80.0" }
nu-explore = { path = "../nu-explore", version = "0.80.0" }
nu-glob = { path = "../nu-glob", version = "0.80.0" }
nu-json = { path = "../nu-json", version = "0.80.0" }
nu-parser = { path = "../nu-parser", version = "0.80.0" }
nu-path = { path = "../nu-path", version = "0.80.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.80.0" }
nu-protocol = { path = "../nu-protocol", version = "0.80.0" }
nu-system = { path = "../nu-system", version = "0.80.0" }
nu-table = { path = "../nu-table", version = "0.80.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.80.0" }
nu-utils = { path = "../nu-utils", version = "0.80.0" }
num-format = { version = "0.4.3" }
nu-ansi-term = "0.47.0"
# Potential dependencies for extras
@ -39,12 +38,15 @@ base64 = "0.21.0"
byteorder = "1.4.3"
bytesize = "1.2.0"
calamine = "0.19.1"
chrono = { version = "0.4.23", features = ["std", "unstable-locales"], default-features = false }
chrono = { version = "0.4.23", features = [
"std",
"unstable-locales",
], default-features = false }
chrono-humanize = "0.2.1"
chrono-tz = "0.8.1"
crossterm = "0.26"
csv = "1.2.0"
dialoguer = { default-features = false, version = "0.10.3" }
dialoguer = { default-features = false, features = ["fuzzy-select"], version = "0.10.3" }
digest = { default-features = false, version = "0.10.0" }
dtparse = "1.4.0"
encoding_rs = "0.8.30"
@ -74,12 +76,18 @@ quick-xml = "0.28"
rand = "0.8"
rayon = "1.7.0"
regex = "1.7.1"
ureq = { version = "2.6.2", default-features = false, features = ["json", "charset", "native-tls", "gzip"] }
ureq = { version = "2.6.2", default-features = false, features = [
"json",
"charset",
"native-tls",
"gzip",
] }
native-tls = "0.2.11"
roxmltree = "0.18.0"
rust-embed = "6.6.0"
same-file = "1.0.6"
serde = { version = "1.0.123", features = ["derive"] }
serde_json = "1.0"
serde_urlencoded = "0.7.0"
serde_yaml = "0.9.4"
sha2 = "0.10.0"
@ -88,7 +96,7 @@ percent-encoding = "2.2.0"
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.32.0", features = ["serde"], optional = true }
sysinfo = "0.28.2"
tabled = "0.10.0"
tabled = "0.12.0"
terminal_size = "0.2.1"
thiserror = "1.0.31"
titlecase = "2.0.0"
@ -100,6 +108,7 @@ uuid = { version = "1.3.0", features = ["v4"] }
wax = { version = "0.5.0" }
which = { version = "4.4.0", optional = true }
print-positions = "0.6.1"
os_pipe = "1.1.3"
[target.'cfg(windows)'.dependencies]
winreg = "0.50.0"
@ -119,7 +128,7 @@ features = [
"checked_arithmetic",
"concat_str",
"cross_join",
"csv-file",
"csv",
"cum_agg",
"default",
"dtype-categorical",
@ -142,7 +151,7 @@ features = [
"to_dummies",
]
optional = true
version = "0.27.2"
version = "0.29.0"
[target.'cfg(windows)'.dependencies.windows]
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
@ -151,12 +160,14 @@ version = "0.48.0"
[features]
dataframe = ["num", "polars", "sqlparser"]
plugin = ["nu-parser/plugin"]
sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
sqlite = [
"rusqlite",
] # TODO: given that rusqlite is included in reedline, should we just always include it?
trash-support = ["trash"]
which-support = ["which"]
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.79.0" }
nu-test-support = { path = "../nu-test-support", version = "0.80.0" }
mockito = "1.0.0"
dirs-next = "2.0.0"

View File

@ -179,7 +179,10 @@ fn into_duration(
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let span = match input.span() {
Some(t) => t,
None => call.head,
};
let convert_to_unit: Option<Spanned<String>> = call.get_flag(engine_state, stack, "convert")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let config = engine_state.get_config();
@ -188,14 +191,14 @@ fn into_duration(
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, &convert_to_unit, float_precision, head)
action(&v, &convert_to_unit, float_precision, span)
} else {
let mut ret = v;
for path in &column_paths {
let d = convert_to_unit.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &d, float_precision, head)),
Box::new(move |old| action(old, &d, float_precision, span)),
);
if let Err(error) = r {
return Value::Error {

View File

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

View File

@ -9,8 +9,8 @@ use nu_protocol::{
use std::{fs::File, io::BufReader, path::PathBuf};
use polars::prelude::{
CsvEncoding, CsvReader, IpcReader, JsonReader, LazyCsvReader, LazyFrame, ParallelStrategy,
ParquetReader, ScanArgsIpc, ScanArgsParquet, SerReader,
CsvEncoding, CsvReader, IpcReader, JsonReader, LazyCsvReader, LazyFileListReader, LazyFrame,
ParallelStrategy, ParquetReader, ScanArgsIpc, ScanArgsParquet, SerReader,
};
#[derive(Clone)]
@ -146,6 +146,7 @@ fn from_parquet(
row_count: None,
low_memory: false,
cloud_options: None,
use_statistics: false,
};
let df: NuLazyFrame = LazyFrame::scan_parquet(file, args)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -80,7 +80,18 @@ fn command(
)
})?;
let res = casted.strftime(&fmt).into_series();
let res = casted
.strftime(&fmt)
.map_err(|e| {
ShellError::GenericError(
"Error formatting datetime".into(),
e.to_string(),
Some(call.head),
None,
Vec::new(),
)
})?
.into_series();
NuDataFrame::try_from_series(vec![res.into_series()], call.head)
.map(|df| PipelineData::Value(NuDataFrame::into_value(df, call.head), None))

View File

@ -736,7 +736,7 @@ fn contains_series_pat(series: &Series, pat: &str, span: Span) -> Result<Value,
let casted = series.utf8();
match casted {
Ok(casted) => {
let res = casted.contains(pat);
let res = casted.contains(pat, false);
match res {
Ok(res) => {

View File

@ -277,7 +277,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
| AggExpr::First(expr)
| AggExpr::Last(expr)
| AggExpr::Mean(expr)
| AggExpr::List(expr)
| AggExpr::Implode(expr)
| AggExpr::Count(expr)
| AggExpr::Sum(expr)
| AggExpr::AggGroups(expr)
@ -426,25 +426,29 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
span,
}
}
Expr::SortBy { expr, by, reverse } => {
Expr::SortBy {
expr,
by,
descending,
} => {
let expr = expr_to_value(expr.as_ref(), span);
let by: Vec<Value> = by.iter().map(|b| expr_to_value(b, span)).collect();
let by = Value::List { vals: by, span };
let reverse: Vec<Value> = reverse
let descending: Vec<Value> = descending
.iter()
.map(|r| Value::Bool { val: *r, span })
.collect();
let reverse = Value::List {
vals: reverse,
let descending = Value::List {
vals: descending,
span,
};
let cols = vec!["expr".into(), "by".into(), "reverse".into()];
let cols = vec!["expr".into(), "by".into(), "descending".into()];
Value::Record {
cols,
vals: vec![expr, by, reverse],
vals: vec![expr, by, descending],
span,
}
}
@ -574,6 +578,21 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
span,
}
}
Expr::Cache { input, id } => {
let input = expr_to_value(input.as_ref(), span);
let id = Value::String {
val: format!("{id:?}"),
span,
};
let cols = vec!["input".into(), "id".into()];
Value::Record {
cols,
vals: vec![input, id],
span,
}
}
Expr::Window {
function,
partition_by,

View File

@ -3,8 +3,8 @@ use nu_parser::parse;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack, StateWorkingSet},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
Type, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -27,6 +27,8 @@ impl Command for Ast {
SyntaxShape::String,
"the pipeline to print the ast for",
)
.switch("json", "serialize to json", Some('j'))
.switch("minify", "minify the nuon or json output", Some('m'))
.allow_variants_without_examples(true)
.category(Category::Debug)
}
@ -39,26 +41,86 @@ impl Command for Ast {
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?;
let to_json = call.has_flag("json");
let minify = call.has_flag("minify");
let mut working_set = StateWorkingSet::new(engine_state);
let block_output = parse(&mut working_set, None, pipeline.item.as_bytes(), false);
let error_output = working_set.parse_errors.first();
let block_span = match &block_output.span {
Some(span) => span,
None => &pipeline.span,
};
if to_json {
// Get the block as json
let serde_block_str = if minify {
serde_json::to_string(&block_output)
} else {
serde_json::to_string_pretty(&block_output)
};
let block_json = match serde_block_str {
Ok(json) => json,
Err(e) => Err(ShellError::CantConvert {
to_type: "string".to_string(),
from_type: "block".to_string(),
span: *block_span,
help: Some(format!(
"Error: {e}\nCan't convert {block_output:?} to string"
)),
})?,
};
// Get the error as json
let serde_error_str = if minify {
serde_json::to_string(&error_output)
} else {
serde_json::to_string_pretty(&error_output)
};
let block_value = Value::String {
val: format!("{block_output:#?}"),
span: pipeline.span,
};
let error_value = Value::String {
val: format!("{error_output:#?}"),
span: pipeline.span,
};
let output_record = Value::Record {
cols: vec!["block".to_string(), "error".to_string()],
vals: vec![block_value, error_value],
span: pipeline.span,
};
Ok(output_record.into_pipeline_data())
let error_json = match serde_error_str {
Ok(json) => json,
Err(e) => Err(ShellError::CantConvert {
to_type: "string".to_string(),
from_type: "error".to_string(),
span: *block_span,
help: Some(format!(
"Error: {e}\nCan't convert {error_output:?} to string"
)),
})?,
};
// Create a new output record, merging the block and error
let output_record = Value::Record {
cols: vec!["block".to_string(), "error".to_string()],
vals: vec![
Value::string(block_json, *block_span),
Value::string(error_json, Span::test_data()),
],
span: pipeline.span,
};
Ok(output_record.into_pipeline_data())
} else {
let block_value = Value::String {
val: if minify {
format!("{block_output:?}")
} else {
format!("{block_output:#?}")
},
span: pipeline.span,
};
let error_value = Value::String {
val: if minify {
format!("{error_output:?}")
} else {
format!("{error_output:#?}")
},
span: pipeline.span,
};
let output_record = Value::Record {
cols: vec!["block".to_string(), "error".to_string()],
vals: vec![block_value, error_value],
span: pipeline.span,
};
Ok(output_record.into_pipeline_data())
}
}
fn examples(&self) -> Vec<Example> {
@ -78,6 +140,17 @@ impl Command for Ast {
example: "ast 'for x in 1..10 { echo $x '",
result: None,
},
Example {
description:
"Print the ast of a pipeline with an error, as json, in a nushell table",
example: "ast 'for x in 1..10 { echo $x ' --json | get block | from json",
result: None,
},
Example {
description: "Print the ast of a pipeline with an error, as json, minified",
example: "ast 'for x in 1..10 { echo $x ' -j -m",
result: None,
},
]
}
}

View File

@ -1,123 +1,193 @@
use nu_protocol::Value;
use nu_table::{string_width, string_wrap};
use tabled::{
builder::Builder,
peaker::PriorityMax,
width::{MinWidth, Wrap},
Style,
grid::config::ColoredConfig,
settings::{peaker::PriorityMax, width::Wrap, Settings, Style},
Table,
};
use self::{
global_horizontal_char::SetHorizontalCharOnFirstRow, peak2::Peak2,
table_column_width::get_first_cell_width, truncate_table::TruncateTable,
width_increase::IncWidth,
use crate::debug::inspect_table::{
global_horizontal_char::SetHorizontalChar, set_widths::SetWidths,
};
pub fn build_table(value: Value, description: String, termsize: usize) -> String {
let (head, mut data) = util::collect_input(value);
let count_columns = head.len();
data.insert(0, head);
let mut val_table = Builder::from(data).build();
let val_table_width = val_table.total_width();
let mut desc = description;
let mut desc_width = string_width(&desc);
let mut desc_table_width = get_total_width_2_column_table(11, desc_width);
let desc = vec![vec![String::from("description"), description]];
let cfg = Table::default().with(Style::modern()).get_config().clone();
let mut widths = get_data_widths(&data, count_columns);
truncate_data(&mut data, &mut widths, &cfg, termsize);
let mut desc_table = Builder::from(desc).build();
let desc_table_width = desc_table.total_width();
let val_table_width = get_total_width2(&widths, &cfg);
if val_table_width < desc_table_width {
increase_widths(&mut widths, desc_table_width - val_table_width);
increase_data_width(&mut data, &widths);
}
if val_table_width > desc_table_width {
increase_string_width(&mut desc, val_table_width);
}
if desc_table_width > termsize {
let delete_width = desc_table_width - termsize;
if delete_width >= desc_width {
// we can't fit in a description; we consider it's no point in showing then?
return String::new();
}
desc_width -= delete_width;
desc = string_wrap(&desc, desc_width, false);
desc_table_width = termsize;
}
add_padding_to_widths(&mut widths);
#[allow(clippy::manual_clamp)]
let width = val_table_width.max(desc_table_width).min(termsize);
desc_table
.with(Style::rounded().off_bottom())
.with(Wrap::new(width).priority::<PriorityMax>())
.with(MinWidth::new(width).priority::<Peak2>());
let mut desc_table = Table::from_iter([[String::from("description"), desc]]);
desc_table.with(Style::rounded().remove_bottom().remove_horizontals());
val_table
.with(Style::rounded().top_left_corner('├').top_right_corner('┤'))
.with(TruncateTable(width))
.with(Wrap::new(width).priority::<PriorityMax>())
.with(IncWidth(width));
// we use only 1, cause left border considered 0 position
let count_split_lines = 1;
let desc_width = get_first_cell_width(&mut desc_table) + count_split_lines;
val_table.with(SetHorizontalCharOnFirstRow::new('┼', '┴', desc_width));
let mut val_table = Table::from_iter(data);
val_table.with(
Settings::default()
.with(Style::rounded().corner_top_left('├').corner_top_right('┤'))
.with(SetWidths(widths))
.with(Wrap::new(width).priority::<PriorityMax>())
.with(SetHorizontalChar::new('┼', '┴', 11 + 2 + 1)),
);
format!("{desc_table}\n{val_table}")
}
mod truncate_table {
use tabled::{
papergrid::{
records::{Records, RecordsMut, Resizable},
width::{CfgWidthFunction, WidthEstimator},
Estimate,
},
TableOption,
};
pub struct TruncateTable(pub usize);
impl<R> TableOption<R> for TruncateTable
where
R: Records + RecordsMut<String> + Resizable,
{
fn change(&mut self, table: &mut tabled::Table<R>) {
let width = table.total_width();
if width <= self.0 {
return;
}
let count_columns = table.get_records().count_columns();
if count_columns < 1 {
return;
}
let mut evaluator = WidthEstimator::default();
evaluator.estimate(table.get_records(), table.get_config());
let columns_width: Vec<_> = evaluator.into();
const SPLIT_LINE_WIDTH: usize = 1;
let mut width = 0;
let mut i = 0;
for w in columns_width {
width += w + SPLIT_LINE_WIDTH;
if width >= self.0 {
break;
}
i += 1;
}
if i == 0 && count_columns > 0 {
i = 1;
} else if i + 1 == count_columns {
// we want to left at least 1 column
i -= 1;
}
let count_columns = table.get_records().count_columns();
let y = count_columns - i;
let mut column = count_columns;
for _ in 0..y {
column -= 1;
table.get_records_mut().remove_column(column);
}
table.get_records_mut().push_column();
let width_ctrl = CfgWidthFunction::from_cfg(table.get_config());
let last_column = table.get_records().count_columns() - 1;
for row in 0..table.get_records().count_rows() {
table
.get_records_mut()
.set((row, last_column), String::from(""), &width_ctrl)
}
fn get_data_widths(data: &[Vec<String>], count_columns: usize) -> Vec<usize> {
let mut widths = vec![0; count_columns];
for row in data {
for col in 0..count_columns {
let text = &row[col];
let width = string_width(text);
widths[col] = std::cmp::max(widths[col], width);
}
}
widths
}
fn add_padding_to_widths(widths: &mut [usize]) {
for width in widths {
*width += 2;
}
}
fn increase_widths(widths: &mut [usize], need: usize) {
let all = need / widths.len();
let mut rest = need - all * widths.len();
for width in widths {
*width += all;
if rest > 0 {
*width += 1;
rest -= 1;
}
}
}
fn increase_data_width(data: &mut Vec<Vec<String>>, widths: &[usize]) {
for row in data {
for (col, max_width) in widths.iter().enumerate() {
let text = &mut row[col];
increase_string_width(text, *max_width);
}
}
}
fn increase_string_width(text: &mut String, total: usize) {
let width = string_width(text);
let rest = total - width;
if rest > 0 {
text.extend(std::iter::repeat(' ').take(rest));
}
}
fn get_total_width_2_column_table(col1: usize, col2: usize) -> usize {
const PAD: usize = 1;
const SPLIT_LINE: usize = 1;
SPLIT_LINE + PAD + col1 + PAD + SPLIT_LINE + PAD + col2 + PAD + SPLIT_LINE
}
fn truncate_data(
data: &mut Vec<Vec<String>>,
widths: &mut Vec<usize>,
cfg: &ColoredConfig,
expected_width: usize,
) {
const SPLIT_LINE_WIDTH: usize = 1;
const PAD: usize = 2;
let total_width = get_total_width2(widths, cfg);
if total_width <= expected_width {
return;
}
let mut width = 0;
let mut peak_count = 0;
for column_width in widths.iter() {
let next_width = width + *column_width + SPLIT_LINE_WIDTH + PAD;
if next_width >= expected_width {
break;
}
width = next_width;
peak_count += 1;
}
debug_assert!(peak_count < widths.len());
let left_space = expected_width - width;
let has_space_for_truncation_column = left_space > PAD;
if !has_space_for_truncation_column {
peak_count -= 1;
}
remove_columns(data, peak_count);
widths.drain(peak_count..);
push_empty_column(data);
widths.push(1);
}
fn remove_columns(data: &mut Vec<Vec<String>>, peak_count: usize) {
if peak_count == 0 {
for row in data {
row.clear();
}
} else {
for row in data {
row.drain(peak_count..);
}
}
}
fn get_total_width2(widths: &[usize], cfg: &ColoredConfig) -> usize {
let pad = 2;
let total = widths.iter().sum::<usize>() + pad * widths.len();
let countv = cfg.count_vertical(widths.len());
let margin = cfg.get_margin();
total + countv + margin.left.size + margin.right.size
}
fn push_empty_column(data: &mut Vec<Vec<String>>) {
let empty_cell = String::from("");
for row in data {
row.push(empty_cell.clone());
}
}
mod util {
@ -223,135 +293,74 @@ mod util {
}
}
mod style_no_left_right_1st {
use tabled::{papergrid::records::Records, Table, TableOption};
struct StyleOffLeftRightFirstLine;
impl<R> TableOption<R> for StyleOffLeftRightFirstLine
where
R: Records,
{
fn change(&mut self, table: &mut Table<R>) {
let shape = table.shape();
let cfg = table.get_config_mut();
let mut b = cfg.get_border((0, 0), shape);
b.left = Some(' ');
cfg.set_border((0, 0), b);
let mut b = cfg.get_border((0, shape.1 - 1), shape);
b.right = Some(' ');
cfg.set_border((0, 0), b);
}
}
}
mod peak2 {
use tabled::peaker::Peaker;
pub struct Peak2;
impl Peaker for Peak2 {
fn create() -> Self {
Self
}
fn peak(&mut self, _: &[usize], _: &[usize]) -> Option<usize> {
Some(1)
}
}
}
mod table_column_width {
use tabled::{
papergrid::{records::Records, width::CfgWidthFunction},
Table,
};
pub fn get_first_cell_width<R: Records>(table: &mut Table<R>) -> usize {
let mut opt = GetFirstCellWidth(0);
table.with(&mut opt);
opt.0
}
struct GetFirstCellWidth(pub usize);
impl<R: Records> tabled::TableOption<R> for GetFirstCellWidth {
fn change(&mut self, table: &mut tabled::Table<R>) {
let w = table
.get_records()
.get_width((0, 0), CfgWidthFunction::default());
let pad = table
.get_config()
.get_padding(tabled::papergrid::Entity::Cell(0, 0));
let pad = pad.left.size + pad.right.size;
self.0 = w + pad;
}
}
}
mod global_horizontal_char {
use tabled::{
papergrid::{records::Records, width::WidthEstimator, Estimate, Offset::Begin},
Table, TableOption,
grid::{
config::{ColoredConfig, Offset},
dimension::{CompleteDimensionVecRecords, Dimension},
records::{ExactRecords, Records},
},
settings::TableOption,
};
pub struct SetHorizontalCharOnFirstRow {
c1: char,
c2: char,
pos: usize,
pub struct SetHorizontalChar {
intersection: char,
split: char,
index: usize,
}
impl SetHorizontalCharOnFirstRow {
pub fn new(c1: char, c2: char, pos: usize) -> Self {
Self { c1, c2, pos }
impl SetHorizontalChar {
pub fn new(intersection: char, split: char, index: usize) -> Self {
Self {
intersection,
split,
index,
}
}
}
impl<R> TableOption<R> for SetHorizontalCharOnFirstRow
where
R: Records,
impl<R: Records + ExactRecords> TableOption<R, CompleteDimensionVecRecords<'_>, ColoredConfig>
for SetHorizontalChar
{
fn change(&mut self, table: &mut Table<R>) {
if table.is_empty() {
fn change(
self,
records: &mut R,
cfg: &mut ColoredConfig,
dimension: &mut CompleteDimensionVecRecords<'_>,
) {
let count_columns = records.count_columns();
let count_rows = records.count_rows();
if count_columns == 0 || count_rows == 0 {
return;
}
let shape = table.shape();
let widths = get_widths(dimension, records.count_columns());
let mut evaluator = WidthEstimator::default();
evaluator.estimate(table.get_records(), table.get_config());
let widths: Vec<_> = evaluator.into();
let has_vertical = table.get_config().has_vertical(0, shape.1);
if has_vertical && self.pos == 0 {
let mut border = table.get_config().get_border((0, 0), shape);
border.left_top_corner = Some(self.c1);
table.get_config_mut().set_border((0, 0), border);
let has_vertical = cfg.has_vertical(0, count_columns);
if has_vertical && self.index == 0 {
let mut border = cfg.get_border((0, 0), (count_rows, count_columns));
border.left_top_corner = Some(self.intersection);
cfg.set_border((0, 0), border);
return;
}
let mut i = 1;
#[allow(clippy::needless_range_loop)]
for (col, width) in widths.into_iter().enumerate() {
if self.pos < i + width {
let o = self.pos - i;
table
.get_config_mut()
.override_horizontal_border((0, col), self.c2, Begin(o));
if self.index < i + width {
let o = self.index - i;
cfg.set_horizontal_char((0, col), self.split, Offset::Begin(o));
return;
}
i += width;
let has_vertical = table.get_config().has_vertical(col, shape.1);
let has_vertical = cfg.has_vertical(col, count_columns);
if has_vertical {
if self.pos == i {
let mut border = table.get_config().get_border((0, col), shape);
border.right_top_corner = Some(self.c1);
table.get_config_mut().set_border((0, col), border);
if self.index == i {
let mut border = cfg.get_border((0, col), (count_rows, count_columns));
border.right_top_corner = Some(self.intersection);
cfg.set_border((0, col), border);
return;
}
@ -360,96 +369,33 @@ mod global_horizontal_char {
}
}
}
}
mod width_increase {
use tabled::{
object::Cell,
papergrid::{
records::{Records, RecordsMut},
width::WidthEstimator,
Entity, Estimate, GridConfig,
},
peaker::PriorityNone,
Modify, Width,
};
use tabled::{peaker::Peaker, Table, TableOption};
#[derive(Debug)]
pub struct IncWidth(pub usize);
impl<R> TableOption<R> for IncWidth
where
R: Records + RecordsMut<String>,
{
fn change(&mut self, table: &mut Table<R>) {
if table.is_empty() {
return;
}
let (widths, total_width) =
get_table_widths_with_total(table.get_records(), table.get_config());
if total_width >= self.0 {
return;
}
let increase_list =
get_increase_list(widths, self.0, total_width, PriorityNone::default());
for (col, width) in increase_list.into_iter().enumerate() {
for row in 0..table.get_records().count_rows() {
let pad = table.get_config().get_padding(Entity::Cell(row, col));
let width = width - pad.left.size - pad.right.size;
table.with(Modify::new(Cell(row, col)).with(Width::increase(width)));
}
}
}
}
fn get_increase_list<F>(
mut widths: Vec<usize>,
total_width: usize,
mut width: usize,
mut peaker: F,
) -> Vec<usize>
where
F: Peaker,
{
while width != total_width {
let col = match peaker.peak(&[], &widths) {
Some(col) => col,
None => break,
};
widths[col] += 1;
width += 1;
fn get_widths(dims: &CompleteDimensionVecRecords<'_>, count_columns: usize) -> Vec<usize> {
let mut widths = vec![0; count_columns];
for (col, width) in widths.iter_mut().enumerate() {
*width = dims.get_width(col);
}
widths
}
}
fn get_table_widths_with_total<R>(records: R, cfg: &GridConfig) -> (Vec<usize>, usize)
where
R: Records,
{
let mut evaluator = WidthEstimator::default();
evaluator.estimate(&records, cfg);
let total_width = get_table_total_width(&records, cfg, &evaluator);
let widths = evaluator.into();
mod set_widths {
use tabled::{
grid::{config::ColoredConfig, dimension::CompleteDimensionVecRecords},
settings::TableOption,
};
(widths, total_width)
}
pub struct SetWidths(pub Vec<usize>);
pub(crate) fn get_table_total_width<W, R>(records: R, cfg: &GridConfig, ctrl: &W) -> usize
where
W: Estimate<R>,
R: Records,
{
ctrl.total()
+ cfg.count_vertical(records.count_columns())
+ cfg.get_margin().left.size
+ cfg.get_margin().right.size
impl<R> TableOption<R, CompleteDimensionVecRecords<'_>, ColoredConfig> for SetWidths {
fn change(
self,
_: &mut R,
_: &mut ColoredConfig,
dims: &mut CompleteDimensionVecRecords<'_>,
) {
dims.set_widths(self.0);
}
}
}

View File

@ -103,6 +103,7 @@ pub fn create_default_context() -> EngineState {
// Misc
bind_command! {
Source,
Tutor,
};
@ -256,6 +257,7 @@ pub fn create_default_context() -> EngineState {
Clear,
Du,
Input,
InputList,
Kill,
Sleep,
TermSize,
@ -275,12 +277,7 @@ pub fn create_default_context() -> EngineState {
// Shells
bind_command! {
Enter,
Exit,
GotoShell,
NextShell,
PrevShell,
Shells,
};
// Formats
@ -437,13 +434,10 @@ pub fn create_default_context() -> EngineState {
// Deprecated
bind_command! {
ExportOldAlias,
HashBase64,
LPadDeprecated,
MathEvalDeprecated,
OldAlias,
RPadDeprecated,
Source,
StrCollectDeprecated,
StrDatetimeDeprecated,
StrDecimalDeprecated,

View File

@ -24,5 +24,6 @@ pub fn deprecated_commands() -> HashMap<String, String> {
("str rpad".to_string(), "fill".to_string()),
("benchmark".to_string(), "timeit".to_string()),
("str collect".to_string(), "str join".to_string()),
("old-alias".to_string(), "alias".to_string()),
])
}

View File

@ -1,59 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct ExportOldAlias;
impl Command for ExportOldAlias {
fn name(&self) -> &str {
"export old-alias"
}
fn usage(&self) -> &str {
"Define an alias and export it from a module."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export old-alias")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "export an alias of ll to ls -l, from a module",
example: "export old-alias ll = ls -l",
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["aka", "abbr", "module"]
}
}

View File

@ -1,12 +1,9 @@
mod collect;
mod deprecated_commands;
mod export_old_alias;
mod hash_base64;
mod lpad;
mod math_eval;
mod old_alias;
mod rpad;
mod source;
mod str_datetime;
mod str_decimal;
mod str_find_replace;
@ -14,13 +11,10 @@ mod str_int;
pub use collect::StrCollectDeprecated;
pub use deprecated_commands::*;
pub use export_old_alias::ExportOldAlias;
pub use hash_base64::HashBase64;
pub use lpad::LPadDeprecated;
pub use math_eval::SubCommand as MathEvalDeprecated;
pub use old_alias::OldAlias;
pub use rpad::RPadDeprecated;
pub use source::Source;
pub use str_datetime::StrDatetimeDeprecated;
pub use str_decimal::StrDecimalDeprecated;
pub use str_find_replace::StrFindReplaceDeprecated;

View File

@ -1,68 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct OldAlias;
impl Command for OldAlias {
fn name(&self) -> &str {
"old-alias"
}
fn usage(&self) -> &str {
"Alias a command (with optional flags) to a new name."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("old-alias")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn search_terms(&self) -> Vec<&str> {
vec!["abbr", "aka", "fn", "func", "function"]
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Alias ll to ls -l",
example: "old-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: "old-alias customs = ($nu.scope.commands | where is_custom | get command)",
result: Some(Value::nothing(Span::test_data())),
},
]
}
}

View File

@ -71,6 +71,7 @@ pub(crate) fn gen_command(
arg_keep_raw: vec![false; number_of_args],
redirect_stdout: false,
redirect_stderr: false,
redirect_combine: false,
env_vars: env_vars_str,
trim_end_newline: false,
}

View File

@ -23,7 +23,7 @@ impl Command for LoadEnv {
.allow_variants_without_examples(true)
.optional(
"update",
SyntaxShape::Record,
SyntaxShape::Record(vec![]),
"the record to use for updates",
)
.category(Category::FileSystem)

View File

@ -1,5 +1,4 @@
use crate::filesystem::cd_query::query;
use crate::{get_current_shell, get_shells};
#[cfg(unix)]
use libc::gid_t;
use nu_engine::{current_dir, CallExt};
@ -164,23 +163,6 @@ impl Command for Cd {
val: path.clone(),
span,
};
let cwd = Value::string(cwd.to_string_lossy(), call.head);
let mut shells = get_shells(engine_state, stack, cwd);
let current_shell = get_current_shell(engine_state, stack);
shells[current_shell] = path_value.clone();
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") {
stack.add_env_var("OLDPWD".into(), oldpwd)

View File

@ -1,12 +1,15 @@
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use nu_engine::env::current_dir;
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Spanned,
SyntaxShape, Type, Value,
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
Spanned, SyntaxShape, Type, Value,
};
use wax::{Glob as WaxGlob, WalkBehavior};
use wax::{Glob as WaxGlob, WalkBehavior, WalkEntry};
#[derive(Clone)]
pub struct Glob;
@ -41,6 +44,12 @@ impl Command for Glob {
"Whether to filter out symlinks from the returned paths",
Some('S'),
)
.named(
"not",
SyntaxShape::String,
"Pattern to exclude from the results",
Some('n'),
)
.category(Category::FileSystem)
}
@ -101,6 +110,12 @@ impl Command for Glob {
example: r#"glob "[A-Z]*" --no-file --no-symlink"#,
result: None,
},
Example {
description: "Search for files named tsconfig.json that are not in node_modules directories",
example: r#"glob **/tsconfig.json --not **/node_modules/**"#,
result: None,
},
]
}
@ -115,14 +130,15 @@ impl Command for Glob {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let ctrlc = engine_state.ctrlc.clone();
let span = call.head;
let path = current_dir(engine_state, stack)?;
let glob_pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
let depth = call.get_flag(engine_state, stack, "depth")?;
let no_dirs = call.has_flag("no-dir");
let no_files = call.has_flag("no-file");
let no_symlinks = call.has_flag("no-symlink");
let not_pattern: Option<Spanned<String>> = call.get_flag(engine_state, stack, "not")?;
if glob_pattern.item.is_empty() {
return Err(ShellError::GenericError(
@ -153,31 +169,80 @@ impl Command for Glob {
}
};
#[allow(clippy::needless_collect)]
let glob_results: Vec<Value> = glob
.walk_with_behavior(
path,
WalkBehavior {
depth: folder_depth,
..Default::default()
},
)
.flatten()
.filter(|entry| {
let file_type = entry.file_type();
let (not_pat, not_span) = if let Some(not_pat) = not_pattern.clone() {
(not_pat.item, not_pat.span)
} else {
(String::new(), Span::test_data())
};
!(no_dirs && file_type.is_dir()
|| no_files && file_type.is_file()
|| no_symlinks && file_type.is_symlink())
})
.map(|entry| Value::String {
val: entry.into_path().to_string_lossy().to_string(),
span,
})
.collect();
Ok(glob_results
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
Ok(if not_pattern.is_some() {
let glob_results = glob
.walk_with_behavior(
path,
WalkBehavior {
depth: folder_depth,
..Default::default()
},
)
.not([not_pat.as_str()])
.map_err(|err| {
ShellError::GenericError(
"error with glob's not pattern".to_string(),
format!("{err}"),
Some(not_span),
None,
Vec::new(),
)
})?
.flatten();
let result = glob_to_value(ctrlc, glob_results, no_dirs, no_files, no_symlinks, span)?;
result
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone())
} else {
let glob_results = glob
.walk_with_behavior(
path,
WalkBehavior {
depth: folder_depth,
..Default::default()
},
)
.flatten();
let result = glob_to_value(ctrlc, glob_results, no_dirs, no_files, no_symlinks, span)?;
result
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone())
})
}
}
fn glob_to_value<'a>(
ctrlc: Option<Arc<AtomicBool>>,
glob_results: impl Iterator<Item = WalkEntry<'a>>,
no_dirs: bool,
no_files: bool,
no_symlinks: bool,
span: Span,
) -> Result<Vec<Value>, ShellError> {
let mut result: Vec<Value> = Vec::new();
for entry in glob_results {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
result.clear();
return Err(ShellError::InterruptedByUser { span: None });
}
let file_type = entry.file_type();
if !(no_dirs && file_type.is_dir()
|| no_files && file_type.is_file()
|| no_symlinks && file_type.is_symlink())
{
result.push(Value::String {
val: entry.into_path().to_string_lossy().to_string(),
span,
});
}
}
Ok(result)
}

View File

@ -143,7 +143,7 @@ impl Command for Ls {
} else if is_empty_dir(current_dir(engine_state, stack)?) {
return Ok(Value::list(vec![], call_span).into_pipeline_data());
} else {
(PathBuf::from("./*"), call_span, false)
(PathBuf::from("*"), call_span, false)
}
}
};

View File

@ -318,8 +318,8 @@ fn rm(
}
all_targets
.into_keys()
.map(move |f| {
.into_iter()
.map(move |(f, span)| {
let is_empty = || match f.read_dir() {
Ok(mut p) => p.next().is_none(),
Err(_) => false,

View File

@ -124,7 +124,7 @@ impl Command for Find {
}),
},
Example {
description: "Find value in records",
description: "Find value in records using regex",
example: r#"[[version name]; ['0.1.0' nushell] ['0.1.1' fish] ['0.2.0' zsh]] | find -r "nu""#,
result: Some(Value::List {
vals: vec![Value::test_record(
@ -137,6 +137,51 @@ impl Command for Find {
span: Span::test_data(),
}),
},
Example {
description: "Find inverted values in records using regex",
example: r#"[[version name]; ['0.1.0' nushell] ['0.1.1' fish] ['0.2.0' zsh]] | find -r "nu" --invert"#,
result: Some(Value::List {
vals: vec![
Value::test_record(
vec!["version", "name"],
vec![
Value::test_string("0.1.1"),
Value::test_string("fish".to_string()),
],
),
Value::test_record(
vec!["version", "name"],
vec![
Value::test_string("0.2.0"),
Value::test_string("zsh".to_string()),
],
),
],
span: Span::test_data(),
}),
},
Example {
description: "Find value in list using regex",
example: r#"[["Larry", "Moe"], ["Victor", "Marina"]] | find -r "rr""#,
result: Some(Value::List {
vals: vec![Value::List {
vals: vec![Value::test_string("Larry"), Value::test_string("Moe")],
span: Span::test_data(),
}],
span: Span::test_data(),
}),
},
Example {
description: "Find inverted values in records using regex",
example: r#"[["Larry", "Moe"], ["Victor", "Marina"]] | find -r "rr" --invert"#,
result: Some(Value::List {
vals: vec![Value::List {
vals: vec![Value::test_string("Victor"), Value::test_string("Marina")],
span: Span::test_data(),
}],
span: Span::test_data(),
}),
},
Example {
description: "Remove ANSI sequences from result",
example: "[[foo bar]; [abc 123] [def 456]] | find 123 | get bar | ansi strip",
@ -144,29 +189,23 @@ impl Command for Find {
},
Example {
description: "Find and highlight text in specific columns",
example: "[[col1 col2 col3]; [moe larry curly] [larry curly moe]] | find moe -c [col1 col3]",
example:
"[[col1 col2 col3]; [moe larry curly] [larry curly moe]] | find moe -c [col1]",
result: Some(Value::List {
vals: vec![
Value::test_record(
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
vec![
Value::test_string("\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m".to_string()),
Value::test_string("larry".to_string()),
Value::test_string("curly".to_string()),
]
),
Value::test_record(
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
vec![
Value::test_string("larry".to_string()),
Value::test_string("curly".to_string()),
Value::test_string("\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m".to_string()),
]
),
],
vals: vec![Value::test_record(
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
vec![
Value::test_string(
"\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m"
.to_string(),
),
Value::test_string("larry".to_string()),
Value::test_string("curly".to_string()),
],
)],
span: Span::test_data(),
}),
}
},
]
}
@ -229,27 +268,8 @@ fn find_with_regex(
input.filter(
move |value| match value {
Value::String { val, .. } => re.is_match(val.as_str()).unwrap_or(false) != invert,
Value::Record { cols: _, vals, .. } => {
let matches: Vec<bool> = vals
.iter()
.map(|v| {
re.is_match(v.into_string(" ", &config).as_str())
.unwrap_or(false)
!= invert
})
.collect();
matches.iter().any(|b| *b)
}
Value::List { vals, .. } => {
let matches: Vec<bool> = vals
.iter()
.map(|v| {
re.is_match(v.into_string(" ", &config).as_str())
.unwrap_or(false)
!= invert
})
.collect();
matches.iter().any(|b| *b)
Value::Record { vals, .. } | Value::List { vals, .. } => {
values_match_find(vals, &re, &config, invert)
}
_ => false,
},
@ -257,6 +277,20 @@ fn find_with_regex(
)
}
fn values_match_find(values: &[Value], re: &Regex, config: &Config, invert: bool) -> bool {
match invert {
true => !record_matches_regex(values, re, config),
false => record_matches_regex(values, re, config),
}
}
fn record_matches_regex(values: &[Value], re: &Regex, config: &Config) -> bool {
values.iter().any(|v| {
re.is_match(v.into_string(" ", config).as_str())
.unwrap_or(false)
})
}
#[allow(clippy::too_many_arguments)]
fn highlight_terms_in_record_with_search_columns(
search_cols: &Vec<String>,
@ -274,61 +308,39 @@ fn highlight_terms_in_record_with_search_columns(
search_cols.to_vec()
};
let mut output = vec![];
let mut potential_output = vec![];
let mut found_a_hit = false;
// We iterate every column in the record and every search term for matches
for (cur_col, val) in cols.iter().zip(vals) {
let val_str = val.into_string("", config);
let lower_val = val.into_string("", config).to_lowercase();
let mut term_added_to_output = false;
for term in terms {
let term_str = term.into_string("", config);
let lower_term = term.into_string("", config).to_lowercase();
if lower_val.contains(&lower_term) && cols_to_search.contains(cur_col) {
found_a_hit = true;
term_added_to_output = true;
if config.use_ls_colors {
// Get the original LS_COLORS color
let style = ls_colors.style_for_path(val_str.clone());
let ansi_style = style
.map(LsColors_Style::to_nu_ansi_term_style)
.unwrap_or_default();
let ls_colored_val = ansi_style.paint(&val_str).to_string();
let ansi_term_style = style
.map(to_nu_ansi_term_style)
.unwrap_or_else(|| string_style);
let hi =
match highlight_search_string(&ls_colored_val, &term_str, &ansi_term_style)
{
Ok(hi) => hi,
Err(_) => string_style.paint(term_str.to_string()).to_string(),
};
potential_output.push(Value::String {
val: hi,
span: *span,
});
} else {
// No LS_COLORS support, so just use the original value
let hi = match highlight_search_string(&val_str, &term_str, &string_style) {
Ok(hi) => hi,
Err(_) => string_style.paint(term_str.to_string()).to_string(),
let output_value =
if contains_ignore_case(&val_str, &term_str) && cols_to_search.contains(cur_col) {
let (value_to_highlight, highlight_string_style) = if config.use_ls_colors {
// Get the original LS_COLORS color
get_colored_value_and_string_style(ls_colors, &val_str, &string_style)
} else {
// No LS_COLORS support, so just use the original value
(val_str.clone(), string_style)
};
output.push(Value::String {
val: hi,
span: *span,
});
}
}
}
if !term_added_to_output {
potential_output.push(val.clone());
}
}
if found_a_hit {
output.append(&mut potential_output);
let highlighted_str = match highlight_search_string(
&value_to_highlight,
&term_str,
&highlight_string_style,
) {
Ok(highlighted_str) => highlighted_str,
Err(_) => string_style.paint(term_str).to_string(),
};
Value::String {
val: highlighted_str,
span: *span,
}
} else {
val.clone()
};
output.push(output_value);
}
}
Value::Record {
@ -338,6 +350,28 @@ fn highlight_terms_in_record_with_search_columns(
}
}
fn get_colored_value_and_string_style(
ls_colors: &LsColors,
val_str: &str,
string_style: &Style,
) -> (String, Style) {
let style = ls_colors.style_for_path(val_str);
let ansi_style = style
.map(LsColors_Style::to_nu_ansi_term_style)
.unwrap_or_default();
let ls_colored_val = ansi_style.paint(val_str).to_string();
let ansi_term_style = style
.map(to_nu_ansi_term_style)
.unwrap_or_else(|| *string_style);
(ls_colored_val, ansi_term_style)
}
fn contains_ignore_case(string: &str, substring: &str) -> bool {
string.to_lowercase().contains(&substring.to_lowercase())
}
fn find_with_rest_and_highlight(
engine_state: &EngineState,
stack: &mut Stack,
@ -361,7 +395,6 @@ fn find_with_rest_and_highlight(
}
})
.collect::<Vec<Value>>();
let columns_to_search: Option<Vec<String>> = call.get_flag(&engine_state, stack, "columns")?;
let style_computer = StyleComputer::from_config(&engine_state, stack);
// Currently, search results all use the same style.
@ -375,11 +408,13 @@ fn find_with_rest_and_highlight(
};
let ls_colors = get_ls_colors(ls_colors_env_str);
let cols_to_search = match columns_to_search {
let cols_to_search_in_map = match call.get_flag(&engine_state, stack, "columns")? {
Some(cols) => cols,
None => vec![],
};
let cols_to_search_in_filter = cols_to_search_in_map.clone();
match input {
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(_, _) => input
@ -387,7 +422,7 @@ fn find_with_rest_and_highlight(
move |mut x| match &mut x {
Value::Record { cols, vals, span } => {
highlight_terms_in_record_with_search_columns(
&cols_to_search,
&cols_to_search_in_map,
cols,
vals,
span,
@ -403,69 +438,14 @@ fn find_with_rest_and_highlight(
)?
.filter(
move |value| {
let lower_value = if let Ok(span) = value.span() {
Value::string(value.into_string("", &filter_config).to_lowercase(), span)
} else {
value.clone()
};
lower_terms.iter().any(|term| match value {
Value::Bool { .. }
| Value::Int { .. }
| Value::Filesize { .. }
| Value::Duration { .. }
| Value::Date { .. }
| Value::Range { .. }
| Value::Float { .. }
| Value::Block { .. }
| Value::Closure { .. }
| Value::Nothing { .. }
| Value::Error { .. } => lower_value
.eq(span, term, span)
.map_or(false, |val| val.is_true()),
Value::String { .. }
| Value::List { .. }
| Value::CellPath { .. }
| Value::CustomValue { .. } => term
.r#in(span, &lower_value, span)
.map_or(false, |val| val.is_true()),
Value::Record { vals, .. } => vals.iter().any(|val| {
if let Ok(span) = val.span() {
let lower_val = Value::string(
val.into_string("", &filter_config).to_lowercase(),
Span::test_data(),
);
term.r#in(span, &lower_val, span)
.map_or(false, |aval| aval.is_true())
} else {
term.r#in(span, val, span)
.map_or(false, |aval| aval.is_true())
}
}),
Value::LazyRecord { val, .. } => match val.collect() {
Ok(val) => match val {
Value::Record { vals, .. } => vals.iter().any(|val| {
if let Ok(span) = val.span() {
let lower_val = Value::string(
val.into_string("", &filter_config).to_lowercase(),
Span::test_data(),
);
term.r#in(span, &lower_val, span)
.map_or(false, |aval| aval.is_true())
} else {
term.r#in(span, val, span)
.map_or(false, |aval| aval.is_true())
}
}),
_ => false,
},
Err(_) => false,
},
Value::Binary { .. } => false,
Value::MatchPattern { .. } => false,
}) != invert
value_should_be_printed(
value,
&filter_config,
&lower_terms,
&span,
&cols_to_search_in_filter,
invert,
)
},
ctrlc,
),
@ -474,7 +454,7 @@ fn find_with_rest_and_highlight(
.map(move |mut x| match &mut x {
Value::Record { cols, vals, span } => {
highlight_terms_in_record_with_search_columns(
&cols_to_search,
&cols_to_search_in_map,
cols,
vals,
span,
@ -487,69 +467,14 @@ fn find_with_rest_and_highlight(
_ => x,
})
.filter(move |value| {
let lower_value = if let Ok(span) = value.span() {
Value::string(value.into_string("", &filter_config).to_lowercase(), span)
} else {
value.clone()
};
lower_terms.iter().any(|term| match value {
Value::Bool { .. }
| Value::Int { .. }
| Value::Filesize { .. }
| Value::Duration { .. }
| Value::Date { .. }
| Value::Range { .. }
| Value::Float { .. }
| Value::Block { .. }
| Value::Closure { .. }
| Value::Nothing { .. }
| Value::Error { .. } => lower_value
.eq(span, term, span)
.map_or(false, |value| value.is_true()),
Value::String { .. }
| Value::List { .. }
| Value::CellPath { .. }
| Value::CustomValue { .. } => term
.r#in(span, &lower_value, span)
.map_or(false, |value| value.is_true()),
Value::Record { vals, .. } => vals.iter().any(|val| {
if let Ok(span) = val.span() {
let lower_val = Value::string(
val.into_string("", &filter_config).to_lowercase(),
Span::test_data(),
);
term.r#in(span, &lower_val, span)
.map_or(false, |value| value.is_true())
} else {
term.r#in(span, val, span)
.map_or(false, |value| value.is_true())
}
}),
Value::LazyRecord { val, .. } => match val.collect() {
Ok(val) => match val {
Value::Record { vals, .. } => vals.iter().any(|val| {
if let Ok(span) = val.span() {
let lower_val = Value::string(
val.into_string("", &filter_config).to_lowercase(),
Span::test_data(),
);
term.r#in(span, &lower_val, span)
.map_or(false, |value| value.is_true())
} else {
term.r#in(span, val, span)
.map_or(false, |value| value.is_true())
}
}),
_ => false,
},
Err(_) => false,
},
Value::Binary { .. } => false,
Value::MatchPattern { .. } => false,
}) != invert
value_should_be_printed(
value,
&filter_config,
&lower_terms,
&span,
&cols_to_search_in_filter,
invert,
)
}),
ctrlc.clone(),
)
@ -607,6 +532,96 @@ fn find_with_rest_and_highlight(
}
}
fn value_should_be_printed(
value: &Value,
filter_config: &Config,
lower_terms: &[Value],
span: &Span,
columns_to_search: &Vec<String>,
invert: bool,
) -> bool {
let lower_value = if let Ok(span) = value.span() {
Value::string(value.into_string("", filter_config).to_lowercase(), span)
} else {
value.clone()
};
let mut match_found = lower_terms.iter().any(|term| match value {
Value::Bool { .. }
| Value::Int { .. }
| Value::Filesize { .. }
| Value::Duration { .. }
| Value::Date { .. }
| Value::Range { .. }
| Value::Float { .. }
| Value::Block { .. }
| Value::Closure { .. }
| Value::Nothing { .. }
| Value::Error { .. } => term_equals_value(term, &lower_value, span),
Value::String { .. }
| Value::List { .. }
| Value::CellPath { .. }
| Value::CustomValue { .. } => term_contains_value(term, &lower_value, span),
Value::Record { cols, vals, .. } => {
record_matches_term(cols, vals, columns_to_search, filter_config, term, span)
}
Value::LazyRecord { val, .. } => match val.collect() {
Ok(val) => match val {
Value::Record { cols, vals, .. } => {
record_matches_term(&cols, &vals, columns_to_search, filter_config, term, span)
}
_ => false,
},
Err(_) => false,
},
Value::Binary { .. } => false,
Value::MatchPattern { .. } => false,
});
if invert {
match_found = !match_found;
}
match_found
}
fn term_contains_value(term: &Value, value: &Value, span: &Span) -> bool {
term.r#in(*span, value, *span)
.map_or(false, |value| value.is_true())
}
fn term_equals_value(term: &Value, value: &Value, span: &Span) -> bool {
term.eq(*span, value, *span)
.map_or(false, |value| value.is_true())
}
fn record_matches_term(
cols: &[String],
vals: &[Value],
columns_to_search: &Vec<String>,
filter_config: &Config,
term: &Value,
span: &Span,
) -> bool {
let cols_to_search = if columns_to_search.is_empty() {
cols.to_vec()
} else {
columns_to_search.to_vec()
};
cols.iter().zip(vals).any(|(col, val)| {
if !cols_to_search.contains(col) {
return false;
}
let lower_val = if val.span().is_ok() {
Value::string(
val.into_string("", filter_config).to_lowercase(),
Span::test_data(),
)
} else {
(*val).clone()
};
term_contains_value(term, &lower_val, span)
})
}
fn to_nu_ansi_term_style(style: &LsColors_Style) -> Style {
fn to_nu_ansi_term_color(color: &LsColors_Color) -> Color {
match *color {

View File

@ -114,8 +114,10 @@ impl Command for ParEach {
let max_threads = threads.unwrap_or(0);
let metadata = input.metadata();
let ctrlc = engine_state.ctrlc.clone();
let outer_ctrlc = engine_state.ctrlc.clone();
let block_id = capture_block.block_id;
let mut stack = stack.captures_to_stack(&capture_block.captures);
let span = call.head;
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
@ -146,16 +148,15 @@ impl Command for ParEach {
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(chain_error_with_input(error, val_span)),
}
.into_pipeline_data(),
},
}
})
.collect::<Vec<_>>()
.into_iter()
.flatten()
.into_pipeline_data(ctrlc)
})),
PipelineData::Value(Value::List { vals: val, .. }, ..) => Ok(create_pool(max_threads)?
@ -181,16 +182,14 @@ impl Command for ParEach {
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(chain_error_with_input(error, val_span)),
}
.into_pipeline_data(),
},
}
})
.collect::<Vec<_>>()
.into_iter()
.flatten()
.into_pipeline_data(ctrlc)
})),
PipelineData::ListStream(stream, ..) => Ok(create_pool(max_threads)?.install(|| {
@ -216,16 +215,14 @@ impl Command for ParEach {
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(chain_error_with_input(error, val_span)),
}
.into_pipeline_data(),
},
}
})
.collect::<Vec<_>>()
.into_iter()
.flatten()
.into_pipeline_data(ctrlc)
})),
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::empty()),
@ -242,7 +239,6 @@ impl Command for ParEach {
return Value::Error {
error: Box::new(err),
}
.into_pipeline_data()
}
};
@ -264,16 +260,14 @@ impl Command for ParEach {
redirect_stdout,
redirect_stderr,
) {
Ok(v) => v,
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(error),
}
.into_pipeline_data(),
},
}
})
.collect::<Vec<_>>()
.into_iter()
.flatten()
.into_pipeline_data(ctrlc)
})),
// This match allows non-iterables to be accepted,
@ -297,6 +291,7 @@ impl Command for ParEach {
)
}
}
.and_then(|x| x.filter(|v| !v.is_nothing(), outer_ctrlc))
.map(|res| res.set_metadata(metadata))
}
}

View File

@ -1,3 +1,4 @@
use indexmap::IndexMap;
use itertools::Itertools;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
@ -6,7 +7,6 @@ use nu_protocol::{
Value,
};
use serde::de::Deserialize;
use std::collections::HashMap;
#[derive(Clone)]
pub struct FromYaml;
@ -113,7 +113,8 @@ fn convert_yaml_value_to_nu_value(
}
serde_yaml::Value::Mapping(t) => {
let mut collected = Spanned {
item: HashMap::new(),
// Using an IndexMap ensures consistent ordering
item: IndexMap::new(),
span,
};
@ -339,6 +340,68 @@ mod test {
test_examples(FromYaml {})
}
#[test]
fn test_consistent_mapping_ordering() {
let test_yaml = "- a: b
b: c
- a: g
b: h";
// Before the fix this test is verifying, the ordering of columns in the resulting
// table was non-deterministic. It would take a few executions of the YAML conversion to
// see this ordering difference. This loop should be far more than enough to catch a regression.
for ii in 1..1000 {
let actual = from_yaml_string_to_value(
String::from(test_yaml),
Span::test_data(),
Span::test_data(),
);
let expected: Result<Value, ShellError> = Ok(Value::List {
vals: vec![
Value::Record {
cols: vec!["a".to_string(), "b".to_string()],
vals: vec![Value::test_string("b"), Value::test_string("c")],
span: Span::test_data(),
},
Value::Record {
cols: vec!["a".to_string(), "b".to_string()],
vals: vec![Value::test_string("g"), Value::test_string("h")],
span: Span::test_data(),
},
],
span: Span::test_data(),
});
// Unfortunately the eq function for Value doesn't compare well enough to detect
// ordering errors in List columns or values.
assert!(actual.is_ok());
let actual = actual.ok().unwrap();
let expected = expected.ok().unwrap();
let actual_vals = actual.as_list().unwrap();
let expected_vals = expected.as_list().unwrap();
assert_eq!(expected_vals.len(), actual_vals.len(), "iteration {ii}");
for jj in 0..expected_vals.len() {
let actual_record = actual_vals[jj].as_record().unwrap();
let expected_record = expected_vals[jj].as_record().unwrap();
let actual_columns = actual_record.0;
let expected_columns = expected_record.0;
assert_eq!(
expected_columns, actual_columns,
"record {jj}, iteration {ii}"
);
let actual_vals = actual_record.1;
let expected_vals = expected_record.1;
assert_eq!(expected_vals, actual_vals, "record {jj}, iteration {ii}")
}
}
}
#[test]
fn test_convert_yaml_value_to_nu_value_for_tagged_values() {
struct TestCase {

View File

@ -75,11 +75,33 @@ impl Command for SubCommand {
span: Span::test_data(),
}),
},
Example {
description: "Apply negative precision to a list of numbers",
example: "[123, 123.3, -123.4] | math round -p -1",
result: Some(Value::List {
vals: vec![
Value::test_int(120),
Value::test_int(120),
Value::test_int(-120),
],
span: Span::test_data(),
}),
},
]
}
}
fn operate(value: Value, head: Span, precision: Option<i64>) -> Value {
// We treat int values as float values in order to avoid code repetition in the match closure
let value = if let Value::Int { val, span } = value {
Value::Float {
val: val as f64,
span,
}
} else {
value
};
match value {
Value::Float { val, span } => match precision {
Some(precision_number) => Value::Float {
@ -92,7 +114,6 @@ fn operate(value: Value, head: Span, precision: Option<i64>) -> Value {
span,
},
},
Value::Int { .. } => value,
Value::Error { .. } => value,
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {

View File

@ -1,3 +1,5 @@
mod source;
mod tutor;
pub use source::Source;
pub use tutor::Tutor;

View File

@ -0,0 +1,241 @@
use dialoguer::{console::Term, Select};
use dialoguer::{FuzzySelect, MultiSelect};
use nu_ansi_term::Color;
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
Value,
};
use std::fmt::{Display, Formatter};
enum InteractMode {
Single(Option<usize>),
Multi(Option<Vec<usize>>),
}
#[derive(Clone)]
struct Options {
name: String,
value: Value,
}
impl Display for Options {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
#[derive(Clone)]
pub struct InputList;
const INTERACT_ERROR: &str = "Interact error, could not process options";
impl Command for InputList {
fn name(&self) -> &str {
"input list"
}
fn signature(&self) -> Signature {
Signature::build("input list")
.input_output_types(vec![(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
)])
.optional("prompt", SyntaxShape::String, "the prompt to display")
.switch(
"multi",
"Use multiple results, you can press a to toggle all options on/off",
Some('m'),
)
.switch("fuzzy", "Use a fuzzy select.", Some('f'))
.allow_variants_without_examples(true)
.category(Category::Platform)
}
fn usage(&self) -> &str {
"Interactive list selection."
}
fn extra_usage(&self) -> &str {
"Abort with esc or q."
}
fn search_terms(&self) -> Vec<&str> {
vec!["prompt", "ask", "menu"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let prompt: Option<String> = call.opt(engine_state, stack, 0)?;
let options: Vec<Options> = match input {
PipelineData::Value(Value::Range { .. }, ..)
| PipelineData::Value(Value::List { .. }, ..)
| PipelineData::ListStream { .. }
| PipelineData::Value(Value::Record { .. }, ..) => input
.into_iter()
.map_while(move |x| {
if let Ok(val) = x.as_string() {
Some(Options {
name: val,
value: x,
})
} else if let Ok(record) = x.as_record() {
let mut options = Vec::new();
for (col, val) in record.0.iter().zip(record.1.iter()) {
if let Ok(val) = val.as_string() {
options.push(format!(
" {}{}{}: {} |\t",
Color::Cyan.prefix(),
col,
Color::Cyan.suffix(),
&val
));
}
}
Some(Options {
name: options.join(""),
value: x,
})
} else {
None
}
})
.collect(),
_ => {
return Err(ShellError::TypeMismatch {
err_message: "expected a list or table".to_string(),
span: head,
})
}
};
let prompt = prompt.unwrap_or_default();
if options.is_empty() {
return Err(ShellError::TypeMismatch {
err_message: "expected a list or table, it can also be a problem with the an inner type of your list.".to_string(),
span: head,
});
}
if call.has_flag("multi") && call.has_flag("fuzzy") {
return Err(ShellError::TypeMismatch {
err_message: "Fuzzy search is not supported for multi select".to_string(),
span: head,
});
}
// could potentially be used to map the use theme colors at some point
// let theme = dialoguer::theme::ColorfulTheme {
// active_item_style: Style::new().fg(Color::Cyan).bold(),
// ..Default::default()
// };
let ans: InteractMode = if call.has_flag("multi") {
let mut multi_select = MultiSelect::new(); //::with_theme(&theme);
InteractMode::Multi(
if !prompt.is_empty() {
multi_select.with_prompt(&prompt)
} else {
&mut multi_select
}
.items(&options)
.report(false)
.interact_on_opt(&Term::stderr())
.map_err(|err| ShellError::IOError(format!("{}: {}", INTERACT_ERROR, err)))?,
)
} else if call.has_flag("fuzzy") {
let mut fuzzy_select = FuzzySelect::new(); //::with_theme(&theme);
InteractMode::Single(
if !prompt.is_empty() {
fuzzy_select.with_prompt(&prompt)
} else {
&mut fuzzy_select
}
.items(&options)
.default(0)
.report(false)
.interact_on_opt(&Term::stderr())
.map_err(|err| ShellError::IOError(format!("{}: {}", INTERACT_ERROR, err)))?,
)
} else {
let mut select = Select::new(); //::with_theme(&theme);
InteractMode::Single(
if !prompt.is_empty() {
select.with_prompt(&prompt)
} else {
&mut select
}
.items(&options)
.default(0)
.report(false)
.interact_on_opt(&Term::stderr())
.map_err(|err| ShellError::IOError(format!("{}: {}", INTERACT_ERROR, err)))?,
)
};
Ok(match ans {
InteractMode::Multi(res) => match res {
Some(opts) => Value::List {
vals: opts.iter().map(|s| options[*s].value.clone()).collect(),
span: head,
},
None => Value::List {
vals: vec![],
span: head,
},
},
InteractMode::Single(res) => match res {
Some(opt) => options[opt].value.clone(),
None => Value::String {
val: "".to_string(),
span: head,
},
},
}
.into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Return a single value from a list",
example: r#"[1 2 3 4 5] | input list 'Rate it'"#,
result: None,
},
Example {
description: "Return multiple values from a list",
example: r#"[Banana Kiwi Pear Peach Strawberry] | input list -m 'Add fruits to the basket'"#,
result: None,
},
Example {
description: "Return a single record from a table with fuzzy search",
example: r#"ls | input list -f 'Select the target'"#,
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(InputList {})
}
}

View File

@ -0,0 +1,5 @@
mod input_;
mod list;
pub use input_::Input;
pub use list::InputList;

View File

@ -12,6 +12,7 @@ pub use clear::Clear;
pub use dir_info::{DirBuilder, DirInfo, FileInfo};
pub use du::Du;
pub use input::Input;
pub use input::InputList;
pub use kill::Kill;
pub use sleep::Sleep;
pub use term_size::TermSize;

View File

@ -1,103 +0,0 @@
use super::{get_current_shell, get_shells};
use nu_engine::{current_dir, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct Enter;
impl Command for Enter {
fn name(&self) -> &str {
"enter"
}
fn signature(&self) -> Signature {
Signature::build("enter")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"path",
SyntaxShape::Filepath,
"the path to enter as a new shell",
)
.category(Category::Shells)
}
fn usage(&self) -> &str {
"Enters a new shell at the given path."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let new_path: Value = call.req(engine_state, stack, 0)?;
let path_span = new_path.span()?;
let new_path = new_path.as_path()?;
let cwd = current_dir(engine_state, stack)?;
let new_path = nu_path::canonicalize_with(new_path, &cwd)?;
if !new_path.exists() {
return Err(ShellError::DirectoryNotFound(path_span, None));
}
if !new_path.is_dir() {
return Err(ShellError::DirectoryNotFoundCustom(
"not a directory".to_string(),
path_span,
));
}
let new_path = Value::string(new_path.to_string_lossy(), call.head);
let cwd = Value::string(cwd.to_string_lossy(), call.head);
let mut shells = get_shells(engine_state, stack, cwd);
let mut current_shell = get_current_shell(engine_state, stack);
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
if current_shell + 1 > shells.len() {
shells.push(new_path.clone());
current_shell = shells.len();
} else {
shells.insert(current_shell + 1, new_path.clone());
current_shell += 1;
}
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
stack.add_env_var("PWD".into(), new_path);
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Enter a new shell at path '../dir-foo'",
example: r#"enter ../dir-foo"#,
result: None,
}]
}
}

View File

@ -1,10 +1,7 @@
use super::{get_current_shell, get_last_shell, get_shells};
use nu_engine::{current_dir, CallExt};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Exit;
@ -22,11 +19,6 @@ impl Command for Exit {
SyntaxShape::Int,
"Exit code to return immediately with",
)
.switch(
"now",
"Exit out of all shells immediately (exiting Nu)",
Some('n'),
)
.category(Category::Shells)
}
@ -51,66 +43,14 @@ impl Command for Exit {
std::process::exit(exit_code as i32);
}
if call.has_flag("now") {
std::process::exit(0);
}
let cwd = current_dir(engine_state, stack)?;
let cwd = Value::string(cwd.to_string_lossy(), call.head);
let mut shells = get_shells(engine_state, stack, cwd);
let mut current_shell = get_current_shell(engine_state, stack);
let mut last_shell = get_last_shell(engine_state, stack);
shells.remove(current_shell);
if current_shell <= last_shell {
last_shell = 0;
}
if current_shell == shells.len() && !shells.is_empty() {
current_shell -= 1;
}
if shells.is_empty() {
std::process::exit(0);
} else {
let new_path = shells[current_shell].clone();
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::int(last_shell as i64, call.head),
);
stack.add_env_var("PWD".into(), new_path);
Ok(PipelineData::empty())
}
std::process::exit(0);
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Exit the current shell",
example: "exit",
result: None,
},
Example {
description: "Exit all shells (exiting Nu)",
example: "exit --now",
result: None,
},
]
vec![Example {
description: "Exit the current shell",
example: "exit",
result: None,
}]
}
}

View File

@ -1,97 +0,0 @@
use super::{list_shells, switch_shell, SwitchTo};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct GotoShell;
impl Command for GotoShell {
fn name(&self) -> &str {
"g"
}
fn signature(&self) -> Signature {
Signature::build("g")
.input_output_types(vec![
(Type::Nothing, Type::Nothing),
(Type::Nothing, Type::Table(vec![])),
])
.optional(
"shell_number",
SyntaxShape::OneOf(vec![SyntaxShape::Int, SyntaxShape::String]),
"shell number to change to",
)
.category(Category::Shells)
}
fn usage(&self) -> &str {
"Switch to a given shell, or list all shells if no given shell number."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let new_shell: Option<Value> = call.opt(engine_state, stack, 0)?;
match new_shell {
Some(shell_span) => match &shell_span {
Value::String { val, span } => {
if val == "-" {
switch_shell(engine_state, stack, call, *span, SwitchTo::Last)
} else {
Err(ShellError::TypeMismatch {
err_message: "int or '-'".into(),
span: *span,
})
}
}
Value::Int { val, span } => switch_shell(
engine_state,
stack,
call,
*span,
SwitchTo::Nth(*val as usize),
),
_ => Err(ShellError::TypeMismatch {
err_message: "int or '-'".into(),
span: call.head,
}),
},
None => list_shells(engine_state, stack, call.head),
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Lists all open shells",
example: r#"g"#,
result: None,
},
Example {
description: "Make two directories and enter new shells for them, use `g` to jump to the specific shell",
example: r#"mkdir foo bar; enter foo; enter ../bar; g 1"#,
result: None,
},
Example {
description: "Use `shells` to show all the opened shells and run `g 2` to jump to the third one",
example: r#"shells; g 2"#,
result: None,
},
Example {
description: "Make two directories and enter new shells for them, use `g -` to jump to the last used shell",
example: r#"mkdir foo bar; enter foo; enter ../bar; g -"#,
result: None,
},
]
}
}

View File

@ -1,147 +1,3 @@
mod enter;
mod exit;
mod g;
mod n;
mod p;
mod shells_;
pub use enter::Enter;
pub use exit::Exit;
pub use g::GotoShell;
pub use n::NextShell;
use nu_engine::current_dir;
use nu_protocol::ast::Call;
use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, ShellError, Span, Value};
pub use p::PrevShell;
pub use shells_::Shells;
enum SwitchTo {
Next,
Prev,
Last,
Nth(usize),
}
pub fn get_shells(engine_state: &EngineState, stack: &mut Stack, cwd: Value) -> Vec<Value> {
let shells = stack.get_env_var(engine_state, "NUSHELL_SHELLS");
let shells = if let Some(v) = shells {
v.as_list()
.map(|x| x.to_vec())
.unwrap_or_else(|_| vec![cwd])
} else {
vec![cwd]
};
shells
}
pub fn get_current_shell(engine_state: &EngineState, stack: &mut Stack) -> usize {
let current_shell = stack.get_env_var(engine_state, "NUSHELL_CURRENT_SHELL");
if let Some(v) = current_shell {
v.as_integer().unwrap_or_default() as usize
} else {
0
}
}
fn get_last_shell(engine_state: &EngineState, stack: &mut Stack) -> usize {
let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL");
if let Some(v) = last_shell {
v.as_integer().unwrap_or_default() as usize
} else {
0
}
}
fn switch_shell(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
span: Span,
switch_to: SwitchTo,
) -> Result<PipelineData, ShellError> {
let cwd = current_dir(engine_state, stack)?;
let cwd = Value::string(cwd.to_string_lossy(), call.head);
let shells = get_shells(engine_state, stack, cwd);
let current_shell = get_current_shell(engine_state, stack);
let new_shell = match switch_to {
SwitchTo::Next => {
let mut new_shell = current_shell + 1;
if new_shell == shells.len() {
new_shell = 0;
}
new_shell
}
SwitchTo::Prev => {
if current_shell == 0 {
shells.len() - 1
} else {
current_shell - 1
}
}
SwitchTo::Last => get_last_shell(engine_state, stack),
SwitchTo::Nth(n) => n,
};
let new_path = shells
.get(new_shell)
.ok_or(ShellError::NotFound { span })?
.to_owned();
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(new_shell as i64, call.head),
);
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
stack.add_env_var("PWD".into(), new_path);
Ok(PipelineData::empty())
}
fn list_shells(
engine_state: &EngineState,
stack: &mut Stack,
span: Span,
) -> Result<PipelineData, ShellError> {
let cwd = current_dir(engine_state, stack)?;
let cwd = Value::String {
val: cwd.to_string_lossy().to_string(),
span,
};
let shells = get_shells(engine_state, stack, cwd);
let current_shell = get_current_shell(engine_state, stack);
Ok(shells
.into_iter()
.enumerate()
.map(move |(idx, val)| Value::Record {
cols: vec!["active".to_string(), "path".to_string()],
vals: vec![
Value::Bool {
val: idx == current_shell,
span,
},
val,
],
span,
})
.into_pipeline_data(None))
}

View File

@ -1,49 +0,0 @@
use super::{switch_shell, SwitchTo};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct NextShell;
impl Command for NextShell {
fn name(&self) -> &str {
"n"
}
fn signature(&self) -> Signature {
Signature::build("n")
.category(Category::Shells)
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
}
fn usage(&self) -> &str {
"Switch to the next shell."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
switch_shell(engine_state, stack, call, call.head, SwitchTo::Next)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Make two directories and enter new shells for them, use `n` to jump to the next shell",
example: r#"mkdir foo bar; enter foo; enter ../bar; n"#,
result: None,
},
Example {
description: "Run `n` several times and note the changes of current directory",
example: r#"n"#,
result: None,
},
]
}
}

View File

@ -1,49 +0,0 @@
use super::{switch_shell, SwitchTo};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct PrevShell;
impl Command for PrevShell {
fn name(&self) -> &str {
"p"
}
fn signature(&self) -> Signature {
Signature::build("p")
.category(Category::Shells)
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
}
fn usage(&self) -> &str {
"Switch to the previous shell."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
switch_shell(engine_state, stack, call, call.head, SwitchTo::Prev)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Make two directories and enter new shells for them, use `p` to jump to the previous shell",
example: r#"mkdir foo bar; enter foo; enter ../bar; p"#,
result: None,
},
Example {
description: "Run `p` several times and note the changes of current directory",
example: r#"p"#,
result: None,
},
]
}
}

View File

@ -1,49 +0,0 @@
use super::list_shells;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct Shells;
impl Command for Shells {
fn name(&self) -> &str {
"shells"
}
fn signature(&self) -> Signature {
Signature::build("shells")
.category(Category::Shells)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
}
fn usage(&self) -> &str {
"Lists all open shells."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
list_shells(engine_state, stack, call.head)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Enter a new shell at parent path '..' and show all opened shells",
example: r#"enter ..; shells"#,
result: None,
},
Example {
description: "Show currently active shell",
example: r#"shells | where active == true"#,
result: None,
},
]
}
}

View File

@ -67,6 +67,7 @@ fn exec(
let redirect_stdout = call.has_flag("redirect-stdout");
let redirect_stderr = call.has_flag("redirect-stderr");
let redirect_combine = call.has_flag("redirect-combine");
let trim_end_newline = call.has_flag("trim-end-newline");
let external_command = create_external_command(
@ -75,6 +76,7 @@ fn exec(
call,
redirect_stdout,
redirect_stderr,
redirect_combine,
trim_end_newline,
)?;

View File

@ -1,6 +1,4 @@
use crate::hook::eval_hook;
use fancy_regex::Regex;
use itertools::Itertools;
use nu_engine::env_to_strings;
use nu_engine::CallExt;
use nu_protocol::{
@ -11,6 +9,7 @@ use nu_protocol::{
SyntaxShape, Type, Value,
};
use nu_system::ForegroundProcess;
use os_pipe::PipeReader;
use pathdiff::diff_paths;
use std::collections::HashMap;
use std::io::{BufRead, BufReader, Read, Write};
@ -41,6 +40,11 @@ impl Command for External {
.input_output_types(vec![(Type::Any, Type::Any)])
.switch("redirect-stdout", "redirect stdout to the pipeline", None)
.switch("redirect-stderr", "redirect stderr to the pipeline", None)
.switch(
"redirect-combine",
"redirect both stdout and stderr combined to the pipeline (collected in stdout)",
None,
)
.switch("trim-end-newline", "trimming end newlines", None)
.required("command", SyntaxShape::String, "external command to run")
.rest("args", SyntaxShape::Any, "arguments for external command")
@ -56,14 +60,25 @@ impl Command for External {
) -> Result<PipelineData, ShellError> {
let redirect_stdout = call.has_flag("redirect-stdout");
let redirect_stderr = call.has_flag("redirect-stderr");
let redirect_combine = call.has_flag("redirect-combine");
let trim_end_newline = call.has_flag("trim-end-newline");
if redirect_combine && (redirect_stdout || redirect_stderr) {
return Err(ShellError::ExternalCommand {
label: "Cannot use --redirect-combine with --redirect-stdout or --redirect-stderr"
.into(),
help: "use either --redirect-combine or redirect a single output stream".into(),
span: call.head,
});
}
let command = create_external_command(
engine_state,
stack,
call,
redirect_stdout,
redirect_stderr,
redirect_combine,
trim_end_newline,
)?;
@ -93,6 +108,7 @@ pub fn create_external_command(
call: &Call,
redirect_stdout: bool,
redirect_stderr: bool,
redirect_combine: bool,
trim_end_newline: bool,
) -> Result<ExternalCommand, ShellError> {
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
@ -149,6 +165,7 @@ pub fn create_external_command(
arg_keep_raw,
redirect_stdout,
redirect_stderr,
redirect_combine,
env_vars: env_vars_str,
trim_end_newline,
})
@ -161,6 +178,7 @@ pub struct ExternalCommand {
pub arg_keep_raw: Vec<bool>,
pub redirect_stdout: bool,
pub redirect_stderr: bool,
pub redirect_combine: bool,
pub env_vars: HashMap<String, String>,
pub trim_end_newline: bool,
}
@ -177,10 +195,10 @@ impl ExternalCommand {
let ctrlc = engine_state.ctrlc.clone();
let mut fg_process = ForegroundProcess::new(
self.create_process(&input, false, head)?,
engine_state.pipeline_externals_state.clone(),
);
#[allow(unused_mut)]
let (cmd, mut reader) = self.create_process(&input, false, head)?;
let mut fg_process =
ForegroundProcess::new(cmd, engine_state.pipeline_externals_state.clone());
// mut is used in the windows branch only, suppress warning on other platforms
#[allow(unused_mut)]
let mut child;
@ -211,8 +229,10 @@ impl ExternalCommand {
.any(|&cmd| command_name_upper == cmd);
if looks_like_cmd_internal {
let (cmd, new_reader) = self.create_process(&input, true, head)?;
reader = new_reader;
let mut cmd_process = ForegroundProcess::new(
self.create_process(&input, true, head)?,
cmd,
engine_state.pipeline_externals_state.clone(),
);
child = cmd_process.spawn();
@ -242,9 +262,11 @@ impl ExternalCommand {
item: file_name.to_string_lossy().to_string(),
span: self.name.span,
};
let (cmd, new_reader) = new_command
.create_process(&input, true, head)?;
reader = new_reader;
let mut cmd_process = ForegroundProcess::new(
new_command
.create_process(&input, true, head)?,
cmd,
engine_state.pipeline_externals_state.clone(),
);
child = cmd_process.spawn();
@ -419,6 +441,7 @@ impl ExternalCommand {
let commandname = self.name.item.clone();
let redirect_stdout = self.redirect_stdout;
let redirect_stderr = self.redirect_stderr;
let redirect_combine = self.redirect_combine;
let span = self.name.span;
let output_ctrlc = ctrlc.clone();
let stderr_ctrlc = ctrlc.clone();
@ -441,6 +464,12 @@ impl ExternalCommand {
.to_string(), span }
})?;
read_and_redirect_message(stdout, stdout_tx, ctrlc)
} else if redirect_combine {
let stdout = reader.ok_or_else(|| {
ShellError::ExternalCommand { label: "Error taking combined stdout and stderr from external".to_string(), help: "Combined redirects need access to reader pipe of an external command"
.to_string(), span }
})?;
read_and_redirect_message(stdout, stdout_tx, ctrlc)
}
@ -516,7 +545,7 @@ impl ExternalCommand {
let exit_code_receiver = ValueReceiver::new(exit_code_rx);
Ok(PipelineData::ExternalStream {
stdout: if redirect_stdout {
stdout: if redirect_stdout || redirect_combine {
Some(RawStream::new(
Box::new(stdout_receiver),
output_ctrlc.clone(),
@ -553,7 +582,7 @@ impl ExternalCommand {
input: &PipelineData,
use_cmd: bool,
span: Span,
) -> Result<CommandSys, ShellError> {
) -> Result<(CommandSys, Option<PipeReader>), ShellError> {
let mut process = if let Some(d) = self.env_vars.get("PWD") {
let mut process = if use_cmd {
self.spawn_cmd_command(d)
@ -583,13 +612,22 @@ impl ExternalCommand {
// If the external is not the last command, its output will get piped
// either as a string or binary
if self.redirect_stdout {
process.stdout(Stdio::piped());
}
let reader = if self.redirect_combine {
let (reader, writer) = os_pipe::pipe()?;
let writer_clone = writer.try_clone()?;
process.stdout(writer);
process.stderr(writer_clone);
Some(reader)
} else {
if self.redirect_stdout {
process.stdout(Stdio::piped());
}
if self.redirect_stderr {
process.stderr(Stdio::piped());
}
if self.redirect_stderr {
process.stderr(Stdio::piped());
}
None
};
// If there is an input from the pipeline. The stdin from the process
// is piped so it can be used to send the input information
@ -597,7 +635,7 @@ impl ExternalCommand {
process.stdin(Stdio::piped());
}
Ok(process)
Ok((process, reader))
}
fn create_command(&self, cwd: &str) -> Result<CommandSys, ShellError> {
@ -611,8 +649,6 @@ impl ExternalCommand {
} else {
self.spawn_simple_command(cwd)
}
} else if self.name.item.ends_with(".sh") {
Ok(self.spawn_sh_command())
} else {
self.spawn_simple_command(cwd)
}
@ -658,19 +694,6 @@ impl ExternalCommand {
process
}
/// Spawn a sh command with `sh -c args...`
pub fn spawn_sh_command(&self) -> std::process::Command {
let joined_and_escaped_arguments = self
.args
.iter()
.map(|arg| shell_arg_escape(&arg.item))
.join(" ");
let cmd_with_args = vec![self.name.item.clone(), joined_and_escaped_arguments].join(" ");
let mut process = std::process::Command::new("sh");
process.arg("-c").arg(cmd_with_args);
process
}
}
fn trim_expand_and_apply_arg(
@ -756,23 +779,6 @@ fn suggest_command(attempted_command: &str, engine_state: &EngineState) -> Optio
}
}
fn has_unsafe_shell_characters(arg: &str) -> bool {
let re: Regex = Regex::new(r"[^\w@%+=:,./-]").expect("regex to be valid");
re.is_match(arg).unwrap_or(false)
}
fn shell_arg_escape(arg: &str) -> String {
match arg {
"" => String::from("''"),
s if !has_unsafe_shell_characters(s) => String::from(s),
_ => {
let single_quotes_escaped = arg.split('\'').join("'\"'\"'");
format!("'{single_quotes_escaped}'")
}
}
}
/// This function returns a tuple with 3 items:
/// 1st item: trimmed string.
/// 2nd item: a boolean value indicate if it's ok to run glob expansion.

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,20 @@ param:string #My cool attractive param
})
}
#[test]
fn def_errors_with_no_space_between_params_and_name_1() {
let actual = nu!("def test-command[] {}");
assert!(actual.err.contains("expected space"));
}
#[test]
fn def_errors_with_no_space_between_params_and_name_2() {
let actual = nu!("def-env test-command() {}");
assert!(actual.err.contains("expected space"));
}
#[test]
fn def_errors_with_multiple_short_flags() {
let actual = nu!("def test-command [ --long(-l)(-o) ] {}");
@ -141,3 +155,11 @@ fn def_with_paren_params() {
assert_eq!(actual.out, "3");
}
#[test]
fn extern_with_block() {
let actual =
nu!("extern foo [...rest] { print ($rest | str join ',' ) }; foo --bar baz -- -q -u -x");
assert_eq!(actual.out, "--bar,baz,--,-q,-u,-x");
}

View File

@ -1,73 +0,0 @@
use nu_test_support::fs::{files_exist_at, Stub::EmptyFile};
use nu_test_support::nu;
use nu_test_support::playground::Playground;
use std::path::Path;
#[test]
fn knows_the_filesystems_entered() {
Playground::setup("enter_test_1", |dirs, sandbox| {
sandbox
.within("red_pill")
.with_files(vec![
EmptyFile("andres.nu"),
EmptyFile("jtnu"),
EmptyFile("yehuda.nu"),
])
.within("blue_pill")
.with_files(vec![
EmptyFile("bash.nxt"),
EmptyFile("korn.nxt"),
EmptyFile("powedsh.nxt"),
])
.mkdir("expected");
let red_pill_dir = dirs.test().join("red_pill");
let blue_pill_dir = dirs.test().join("blue_pill");
let expected = dirs.test().join("expected");
let expected_recycled = expected.join("recycled");
nu!(
cwd: dirs.test(),
"
enter expected
mkdir recycled
enter ../red_pill
mv jtnu ../expected
enter ../blue_pill
cp *.nxt ../expected/recycled
p
p
mv ../red_pill/yehuda.nu .
n
mv andres.nu ../expected/andres.nu
exit
cd ..
rm red_pill --recursive
exit
n
rm blue_pill --recursive
exit
"
);
assert!(!red_pill_dir.exists());
assert!(files_exist_at(
vec![
Path::new("andres.nu"),
Path::new("jtnu"),
Path::new("yehuda.nu"),
],
expected
));
assert!(!blue_pill_dir.exists());
assert!(files_exist_at(
vec![
Path::new("bash.nxt"),
Path::new("korn.nxt"),
Path::new("powedsh.nxt"),
],
expected_recycled
));
})
}

View File

@ -1,123 +1,96 @@
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
use nu_test_support::nu;
#[test]
fn find_with_list_search_with_string() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[moe larry curly] | find moe | get 0
"#
));
let actual = nu!("[moe larry curly] | find moe | get 0");
assert_eq!(actual.out, "moe");
}
#[test]
fn find_with_list_search_with_char() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[moe larry curly] | find l | to json -r
"#
));
let actual = nu!("[moe larry curly] | find l | to json -r");
assert_eq!(actual.out, r#"["larry","curly"]"#);
}
#[test]
fn find_with_list_search_with_number() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[1 2 3 4 5] | find 3 | get 0
"#
));
let actual = nu!("[1 2 3 4 5] | find 3 | get 0");
assert_eq!(actual.out, "3");
}
#[test]
fn find_with_string_search_with_string() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo Cargo.toml | find toml
"#
));
let actual = nu!("echo Cargo.toml | find toml");
assert_eq!(actual.out, "Cargo.toml");
}
#[test]
fn find_with_string_search_with_string_not_found() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[moe larry curly] | find shemp | is-empty
"#
));
let actual = nu!("[moe larry curly] | find shemp | is-empty");
assert_eq!(actual.out, "true");
}
#[test]
fn find_with_filepath_search_with_string() {
Playground::setup("filepath_test_1", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
]);
let actual =
nu!(r#"["amigos.txt","arepas.clu","los.txt","tres.txt"] | find arep | to json -r"#);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| find arep
| to json -r
"#
));
assert_eq!(actual.out, r#"["arepas.clu"]"#);
})
assert_eq!(actual.out, r#"["arepas.clu"]"#);
}
#[test]
fn find_with_filepath_search_with_multiple_patterns() {
Playground::setup("filepath_test_2", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
]);
let actual =
nu!(r#"["amigos.txt","arepas.clu","los.txt","tres.txt"] | find arep ami | to json -r"#);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| find arep ami
| to json -r
"#
));
assert_eq!(actual.out, r#"["amigos.txt","arepas.clu"]"#);
})
assert_eq!(actual.out, r#"["amigos.txt","arepas.clu"]"#);
}
#[test]
fn find_takes_into_account_linebreaks_in_string() {
let actual = nu!(
cwd: ".", pipeline(
r#"
"atest\nanothertest\nnohit\n" | find a | length
"#
));
let actual = nu!(r#""atest\nanothertest\nnohit\n" | find a | length"#);
assert_eq!(actual.out, "2");
}
#[test]
fn find_with_regex_in_table_keeps_row_if_one_column_matches() {
let actual = nu!(
"[[name nickname]; [Maurice moe] [Laurence larry]] | find --regex ce | get name | to json -r"
);
assert_eq!(actual.out, r#"["Maurice","Laurence"]"#);
}
#[test]
fn inverted_find_with_regex_in_table_keeps_row_if_none_of_the_columns_matches() {
let actual = nu!(
"[[name nickname]; [Maurice moe] [Laurence larry]] | find --regex moe --invert | get name | to json -r"
);
assert_eq!(actual.out, r#"["Laurence"]"#);
}
#[test]
fn find_in_table_only_keep_rows_with_matches_on_selected_columns() {
let actual = nu!(
"[[name nickname]; [Maurice moe] [Laurence larry]] | find r --columns [nickname] | get name | to json -r"
);
assert!(actual.out.contains("Laurence"));
assert!(!actual.out.contains("Maurice"));
}
#[test]
fn inverted_find_in_table_keeps_row_if_none_of_the_selected_columns_matches() {
let actual = nu!(
"[[name nickname]; [Maurice moe] [Laurence larry]] | find r --columns [nickname] --invert | get name | to json -r"
);
assert_eq!(actual.out, r#"["Maurice"]"#);
}

View File

@ -1,91 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn list_shells() {
let actual = nu!(
cwd: ".", pipeline(
r#"g | get path | length "#
));
assert_eq!(actual.out, "1");
}
#[test]
fn enter_shell() {
let actual = nu!(
cwd: ".", pipeline(
r#"g 0"#
));
assert!(actual.err.is_empty());
}
#[test]
fn enter_not_exist_shell() {
let actual = nu!(
cwd: ".", pipeline(
r#"g 1"#
));
assert!(actual.err.contains("Not found"));
}
#[test]
fn switch_to_last_used_shell_1() {
Playground::setup("switch_last_used_shell_1", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; g 0; g -;g | get active.2"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn switch_to_last_used_shell_2() {
Playground::setup("switch_last_used_shell_2", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; n; g -;g | get active.2"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn switch_to_last_used_shell_3() {
Playground::setup("switch_last_used_shell_3", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; p; g -;g | get active.2"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn switch_to_last_used_shell_4() {
Playground::setup("switch_last_used_shell_4", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; g 2; exit; g -;g | get active.0"#
));
assert_eq!(actual.out, "true");
})
}

View File

@ -94,37 +94,17 @@ fn summarizes_by_values() {
#[test]
fn help() {
Playground::setup("histogram_test_3", |dirs, _sandbox| {
let help_command = nu!(
cwd: dirs.test(), pipeline(
r#"
help histogram
"#
));
let help_command = nu!("help histogram");
let help_short = nu!("histogram -h");
let help_long = nu!("histogram --help");
let help_short = nu!(
cwd: dirs.test(), pipeline(
r#"
histogram -h
"#
));
let help_long = nu!(
cwd: dirs.test(), pipeline(
r#"
histogram --help
"#
));
assert_eq!(help_short.out, help_command.out);
assert_eq!(help_long.out, help_command.out);
})
assert_eq!(help_short.out, help_command.out);
assert_eq!(help_long.out, help_command.out);
}
#[test]
fn count() {
let actual = nu!(
cwd: ".", pipeline(
let actual = nu!(pipeline(
r#"
echo [[bit]; [1] [0] [0] [0] [0] [0] [0] [1] [1]]
| histogram bit --percentage-type relative

View File

@ -2,20 +2,35 @@ use nu_test_support::nu;
#[test]
fn can_round_very_large_numbers() {
let actual = nu!(
cwd: ".",
"echo 18.1372544780074142289927665486772012345 | math round"
);
let actual = nu!("18.1372544780074142289927665486772012345 | math round");
assert_eq!(actual.out, "18")
}
#[test]
fn can_round_very_large_numbers_with_precision() {
let actual = nu!(
cwd: ".",
"echo 18.13725447800741422899276654867720121457878988 | math round -p 10"
);
let actual = nu!("18.13725447800741422899276654867720121457878988 | math round -p 10");
assert_eq!(actual.out, "18.137254478")
}
#[test]
fn can_round_integer_with_negative_precision() {
let actual = nu!("123 | math round -p -1");
assert_eq!(actual.out, "120")
}
#[test]
fn can_round_float_with_negative_precision() {
let actual = nu!("123.3 | math round -p -1");
assert_eq!(actual.out, "120")
}
#[test]
fn fails_with_wrong_input_type() {
let actual = nu!("\"not_a_number\" | math round");
assert!(actual.err.contains("Input type not supported"))
}

View File

@ -18,7 +18,6 @@ mod drop;
mod each;
mod echo;
mod empty;
mod enter;
mod error_make;
mod every;
#[cfg(not(windows))]
@ -30,7 +29,6 @@ mod first;
mod flatten;
mod for_;
mod format;
mod g;
mod get;
mod glob;
mod group_by;
@ -54,11 +52,10 @@ mod merge;
mod mkdir;
mod move_;
mod mut_;
mod n;
mod network;
mod nu_check;
mod open;
mod p;
mod par_each;
mod parse;
mod path;
mod platform;
@ -83,7 +80,6 @@ mod select;
mod semicolon;
mod seq;
mod seq_char;
mod shells;
mod skip;
mod sort;
mod sort_by;

View File

@ -1,31 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn switch_to_next_shell_1() {
Playground::setup("switch_to_next_shell_1", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; n; g | get active.0"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn switch_to_next_shell_2() {
Playground::setup("switch_to_next_shell_2", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; n; n; g | get active.1"#
));
assert_eq!(actual.out, "true");
})
}

View File

@ -1,31 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn switch_to_prev_shell_1() {
Playground::setup("switch_to_next_shell_1", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; p; g | get active.1"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn switch_to_prev_shell_2() {
Playground::setup("switch_to_next_shell_2", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; p; p; p; g | get active.2"#
));
assert_eq!(actual.out, "true");
})
}

View File

@ -0,0 +1,12 @@
use nu_test_support::{nu, pipeline};
#[test]
fn par_each_does_not_flatten_nested_structures() {
// This is a regression test for issue #8497
let actual = nu!(
cwd: ".", pipeline(
r#"[1 2 3] | par-each { |it| [$it, $it] } | sort | to json --raw"#
));
assert_eq!(actual.out, "[[1,1],[2,2],[3,3]]");
}

View File

@ -2,7 +2,7 @@ use nu_test_support::{nu, pipeline};
#[test]
fn regular_columns() {
let actual = nu!(cwd: ".", pipeline(
let actual = nu!(pipeline(
r#"
echo [
[first_name, last_name, rusty_at, type];
@ -22,15 +22,14 @@ fn regular_columns() {
#[test]
fn skip_cell_rejection() {
let actual = nu!(cwd: ".", pipeline(
r#"[ {a: 1, b: 2,c:txt}, { a:val } ] | reject a | get c?.0"#));
let actual = nu!("[ {a: 1, b: 2,c:txt}, { a:val } ] | reject a | get c?.0");
assert_eq!(actual.out, "txt");
}
#[test]
fn complex_nested_columns() {
let actual = nu!(cwd: ".", pipeline(
let actual = nu!(pipeline(
r#"
{
"nu": {
@ -63,7 +62,7 @@ fn complex_nested_columns() {
#[test]
fn ignores_duplicate_columns_rejected() {
let actual = nu!(cwd: ".", pipeline(
let actual = nu!(pipeline(
r#"
echo [
["first name", "last name"];
@ -82,60 +81,36 @@ fn ignores_duplicate_columns_rejected() {
#[test]
fn reject_record_from_raw_eval() {
let actual = nu!(
cwd: ".", pipeline(
r#"
{"a": 3} | reject a | describe
"#
)
);
let actual = nu!(r#"{"a": 3} | reject a | describe"#);
assert!(actual.out.contains("record"));
}
#[test]
fn reject_table_from_raw_eval() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[{"a": 3}] | reject a
"#
)
);
let actual = nu!(r#"[{"a": 3}] | reject a"#);
assert!(actual.out.contains("record 0 fields"));
}
#[test]
fn reject_nested_field() {
let actual = nu!(
cwd: ".", pipeline(
r#"
{a:{b:3,c:5}} | reject a.b | debug
"#
)
);
let actual = nu!("{a:{b:3,c:5}} | reject a.b | debug");
assert_eq!(actual.out, "{a: {c: 5}}");
}
#[test]
fn reject_two_identical_elements() {
let actual = nu!(
cwd: ".", pipeline(
r#"[[a, a]; [1, 2]] | reject a"#
)
);
let actual = nu!("[[a, a]; [1, 2]] | reject a");
assert!(actual.out.contains("record 0 fields"));
}
#[test]
fn reject_large_vec_with_two_identical_elements() {
let actual = nu!(
cwd: ".", pipeline(
r#"[[a, b, c, d, e, a]; [1323, 23, 45, 100, 2, 2423]] | reject a"#
)
);
let actual = nu!("[[a, b, c, d, e, a]; [1323, 23, 45, 100, 2, 2423]] | reject a");
assert!(!actual.out.contains("1323"));
assert!(!actual.out.contains("2423"));
assert!(actual.out.contains('b'));

View File

@ -320,3 +320,18 @@ fn quotes_trimmed_when_shelling_out() {
assert_eq!(actual.out, "foo");
}
#[test]
fn redirect_combine() {
Playground::setup("redirect_combine", |dirs, _| {
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
run-external --redirect-combine sh [-c 'echo Foo; echo >&2 Bar']
"#
));
// Lines are collapsed in the nu! macro
assert_eq!(actual.out, "FooBar");
});
}

View File

@ -1,5 +1,5 @@
use nu_test_support::nu;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
fn semicolon_allows_lhs_to_complete() {
@ -18,12 +18,7 @@ fn semicolon_allows_lhs_to_complete() {
#[test]
fn semicolon_lhs_error_stops_processing() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
where 1 1; echo done
"#
));
let actual = nu!("where 1 1; echo done");
assert!(!actual.out.contains("done"));
}

View File

@ -1,25 +1,15 @@
use nu_test_support::{nu, pipeline};
use nu_test_support::nu;
#[test]
fn float_in_seq_leads_to_lists_of_floats() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
seq 1.0 0.5 6 | describe
"#
));
let actual = nu!("seq 1.0 0.5 6 | describe");
assert_eq!(actual.out, "list<float> (stream)");
}
#[test]
fn ints_in_seq_leads_to_lists_of_ints() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
seq 1 2 6 | describe
"#
));
let actual = nu!("seq 1 2 6 | describe");
assert_eq!(actual.out, "list<int> (stream)");
}

View File

@ -1,25 +1,15 @@
use nu_test_support::{nu, pipeline};
use nu_test_support::nu;
#[test]
fn fails_when_first_arg_is_multiple_chars() {
let actual = nu!(
cwd: ".", pipeline(
r#"
seq char aa z
"#
));
let actual = nu!("seq char aa z");
assert!(actual.err.contains("should be 1 character long"));
}
#[test]
fn fails_when_second_arg_is_multiple_chars() {
let actual = nu!(
cwd: ".", pipeline(
r#"
seq char a zz
"#
));
let actual = nu!("seq char a zz");
assert!(actual.err.contains("should be 1 character long"));
}

View File

@ -1,31 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn list_shells_1() {
Playground::setup("list_shells_1", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; g| get active.2"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn list_shells_2() {
Playground::setup("list_shells_2", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; shells| get active.2"#
));
assert_eq!(actual.out, "true");
})
}

View File

@ -35,12 +35,7 @@ fn sort_primitive_values() {
#[test]
fn sort_different_types() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
[a, 1, b, 2, c, 3, [4, 5, 6], d, 4, [1, 2, 3]] | sort | to json --raw
"#
));
let actual = nu!("[a, 1, b, 2, c, 3, [4, 5, 6], d, 4, [1, 2, 3]] | sort | to json --raw");
let json_output = r#"[1,2,3,4,"a","b","c","d",[1,2,3],[4,5,6]]"#;
assert_eq!(actual.out, json_output);
@ -48,20 +43,14 @@ fn sort_different_types() {
#[test]
fn sort_natural() {
let actual = nu!(
cwd: ".", pipeline(
r#"['1' '2' '3' '4' '5' '10' '100'] | sort -n | to nuon"#
));
let actual = nu!("['1' '2' '3' '4' '5' '10' '100'] | sort -n | to nuon");
assert_eq!(actual.out, r#"["1", "2", "3", "4", "5", "10", "100"]"#);
}
#[test]
fn sort_record_natural() {
let actual = nu!(
cwd: ".", pipeline(
r#"{10:0,99:0,1:0,9:0,100:0} | sort -n | to nuon"#
));
let actual = nu!("{10:0,99:0,1:0,9:0,100:0} | sort -n | to nuon");
assert_eq!(
actual.out,
@ -71,50 +60,35 @@ fn sort_record_natural() {
#[test]
fn sort_record_insensitive() {
let actual = nu!(
cwd: ".", pipeline(
r#"{abe:1,zed:2,ABE:3} | sort -i | to nuon"#
));
let actual = nu!("{abe:1,zed:2,ABE:3} | sort -i | to nuon");
assert_eq!(actual.out, r#"{abe: 1, ABE: 3, zed: 2}"#);
}
#[test]
fn sort_record_insensitive_reverse() {
let actual = nu!(
cwd: ".", pipeline(
r#"{abe:1,zed:2,ABE:3} | sort -ir | to nuon"#
));
let actual = nu!("{abe:1,zed:2,ABE:3} | sort -ir | to nuon");
assert_eq!(actual.out, r#"{zed: 2, ABE: 3, abe: 1}"#);
}
#[test]
fn sort_record_values_natural() {
let actual = nu!(
cwd: ".", pipeline(
r#"{1:"1",2:"2",4:"100",3:"10"} | sort -vn | to nuon"#
));
let actual = nu!(r#"{1:"1",2:"2",4:"100",3:"10"} | sort -vn | to nuon"#);
assert_eq!(actual.out, r#"{"1": "1", "2": "2", "3": "10", "4": "100"}"#);
}
#[test]
fn sort_record_values_insensitive() {
let actual = nu!(
cwd: ".", pipeline(
r#"{1:abe,2:zed,3:ABE} | sort -vi | to nuon"#
));
let actual = nu!("{1:abe,2:zed,3:ABE} | sort -vi | to nuon");
assert_eq!(actual.out, r#"{"1": abe, "3": ABE, "2": zed}"#);
}
#[test]
fn sort_record_values_insensitive_reverse() {
let actual = nu!(
cwd: ".", pipeline(
r#"{1:abe,2:zed,3:ABE} | sort -vir | to nuon"#
));
let actual = nu!("{1:abe,2:zed,3:ABE} | sort -vir | to nuon");
assert_eq!(actual.out, r#"{"2": zed, "3": ABE, "1": abe}"#);
}

View File

@ -117,19 +117,14 @@ fn ls_sort_by_type_name_insensitive() {
#[test]
fn no_column_specified_fails() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
[2 0 1] | sort-by
"#
));
let actual = nu!("[2 0 1] | sort-by");
assert!(actual.err.contains("missing parameter"));
}
#[test]
fn fail_on_non_iterator() {
let actual = nu!(cwd: ".", pipeline("1 | sort-by"));
let actual = nu!("1 | sort-by");
assert!(actual.err.contains("only_supports_this_input_type"));
}

View File

@ -154,11 +154,11 @@ fn table_collapse_none() {
assert_eq!(
actual.out,
concat!(
" a b c ",
" 1 2 3 ",
" 4 5 1 ",
" 2 ",
" 3 ",
" a b c ",
" 1 2 3 ",
" 4 5 1 ",
" 2 ",
" 3 ",
)
);
}
@ -232,11 +232,20 @@ fn table_collapse_hearts() {
}
#[test]
fn table_collapse_doesnot_support_width_control() {
fn table_collapse_does_wrapping_for_long_strings() {
let actual = nu!(
r#"[[a]; [11111111111111111111111111111111111111111111111111111111111111111111111111111111]] | table --collapse"#
);
assert_eq!(actual.out, "Couldn't fit table into 80 columns!");
assert_eq!(
actual.out,
"╭────────────────────────────────╮\
│ a │\
├────────────────────────────────┤\
│ 111111111111111109312339230430 │\
│ 179149313814687359833671239329 │\
│ 01313323321729744896.0000 │\
╰────────────────────────────────╯"
);
}
#[test]
@ -1795,6 +1804,526 @@ fn table_expande_with_no_header_internally_1() {
);
}
#[test]
fn test_collapse_big_0() {
Playground::setup("test_expand_big_0", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
[package]
authors = ["The Nushell Project Developers"]
default-run = "nu"
description = "A new type of shell"
documentation = "https://www.nushell.sh/book/"
edition = "2021"
exclude = ["images"]
homepage = "https://www.nushell.sh"
license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.74.1"
# 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]
members = [
"crates/nu-cli",
"crates/nu-engine",
"crates/nu-parser",
"crates/nu-system",
"crates/nu-command",
"crates/nu-protocol",
"crates/nu-plugin",
"crates/nu_plugin_inc",
"crates/nu_plugin_gstat",
"crates/nu_plugin_example",
"crates/nu_plugin_query",
"crates/nu_plugin_custom_values",
"crates/nu-utils",
]
[dependencies]
chrono = { version = "0.4.23", features = ["serde"] }
crossterm = "0.24.0"
ctrlc = "3.2.1"
log = "0.4"
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
nu-ansi-term = "0.46.0"
nu-cli = { path = "./crates/nu-cli", version = "0.74.1" }
nu-engine = { path = "./crates/nu-engine", version = "0.74.1" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"] }
rayon = "1.6.1"
is_executable = "1.0.1"
simplelog = "0.12.0"
time = "0.3.12"
[target.'cfg(not(target_os = "windows"))'.dependencies]
# Our dependencies don't use OpenSSL on Windows
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
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]
nu-test-support = { path = "./crates/nu-test-support", version = "0.74.1" }
tempfile = "3.2.0"
assert_cmd = "2.0.2"
criterion = "0.4"
pretty_assertions = "1.0.0"
serial_test = "0.10.0"
hamcrest2 = "0.3.0"
rstest = { version = "0.15.0", default-features = false }
itertools = "0.10.3"
[features]
plugin = [
"nu-plugin",
"nu-cli/plugin",
"nu-parser/plugin",
"nu-command/plugin",
"nu-protocol/plugin",
"nu-engine/plugin",
]
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
extra = ["default"]
default = ["plugin", "which-support", "trash-support", "sqlite"]
stable = ["default"]
wasi = []
# 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"]
# Stable (Default)
which-support = ["nu-command/which-support"]
trash-support = ["nu-command/trash-support"]
# Main nu binary
[[bin]]
name = "nu"
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" }
# Criterion benchmarking setup
# Run all benchmarks with `cargo bench`
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
[[bench]]
name = "benchmarks"
harness = false
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
"open sample.toml | table --collapse"
));
_print_lines(&actual.out, 80);
let expected = join_lines([
"╭──────────────────┬─────────┬─────────────────────────────────────────────────╮",
"│ bench │ harness │ name │",
"│ ├─────────┼─────────────────────────────────────────────────┤",
"│ │ false │ benchmarks │",
"├──────────────────┼──────┬──┴─────────────────────────────────────────────────┤",
"│ bin │ name │ path │",
"│ ├──────┼────────────────────────────────────────────────────┤",
"│ │ nu │ src/main.rs │",
"├──────────────────┼──────┴────────┬──────────┬────────────────────────────────┤",
"│ dependencies │ chrono │ features │ serde │",
"│ │ ├──────────┼────────────────────────────────┤",
"│ │ │ version │ 0.4.23 │",
"│ ├───────────────┼──────────┴────────────────────────────────┤",
"│ │ crossterm │ 0.24.0 │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ ctrlc │ 3.2.1 │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ is_executable │ 1.0.1 │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ log │ 0.4 │",
"│ ├───────────────┼──────────┬────────────────────────────────┤",
"│ │ miette │ features │ fancy-no-backtrace │",
"│ │ ├──────────┼────────────────────────────────┤",
"│ │ │ version │ 5.5.0 │",
"│ ├───────────────┼──────────┴────────────────────────────────┤",
"│ │ nu-ansi-term │ 0.46.0 │",
"│ ├───────────────┼─────────┬─────────────────────────────────┤",
"│ │ nu-cli │ path │ ./crates/nu-cli │",
"│ │ ├─────────┼─────────────────────────────────┤",
"│ │ │ version │ 0.74.1 │",
"│ ├───────────────┼─────────┼─────────────────────────────────┤",
"│ │ nu-engine │ path │ ./crates/nu-engine │",
"│ │ ├─────────┼─────────────────────────────────┤",
"│ │ │ version │ 0.74.1 │",
"│ ├───────────────┼─────────┴─────────────────────────────────┤",
"│ │ rayon │ 1.6.1 │",
"│ ├───────────────┼──────────┬────────────────────────────────┤",
"│ │ reedline │ features │ bashisms │",
"│ │ │ ├────────────────────────────────┤",
"│ │ │ │ sqlite │",
"│ │ ├──────────┼────────────────────────────────┤",
"│ │ │ version │ 0.14.0 │",
"│ ├───────────────┼──────────┴────────────────────────────────┤",
"│ │ simplelog │ 0.12.0 │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ time │ 0.3.12 │",
"├──────────────────┼───────────────┴───┬───────────────────────────────────────┤",
"│ dev-dependencies │ assert_cmd │ 2.0.2 │",
"│ ├───────────────────┼───────────────────────────────────────┤",
"│ │ criterion │ 0.4 │",
"│ ├───────────────────┼───────────────────────────────────────┤",
"│ │ hamcrest2 │ 0.3.0 │",
"│ ├───────────────────┼───────────────────────────────────────┤",
"│ │ itertools │ 0.10.3 │",
"│ ├───────────────────┼─────────┬─────────────────────────────┤",
"│ │ nu-test-support │ path │ ./crates/nu-test-support │",
"│ │ ├─────────┼─────────────────────────────┤",
"│ │ │ version │ 0.74.1 │",
"│ ├───────────────────┼─────────┴─────────────────────────────┤",
"│ │ pretty_assertions │ 1.0.0 │",
"│ ├───────────────────┼──────────────────┬────────────────────┤",
"│ │ rstest │ default-features │ false │",
"│ │ ├──────────────────┼────────────────────┤",
"│ │ │ version │ 0.15.0 │",
"│ ├───────────────────┼──────────────────┴────────────────────┤",
"│ │ serial_test │ 0.10.0 │",
"│ ├───────────────────┼───────────────────────────────────────┤",
"│ │ tempfile │ 3.2.0 │",
"├──────────────────┼───────────────────┴─┬─────────────────────────────────────┤",
"│ features │ default │ plugin │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ which-support │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ trash-support │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ sqlite │",
"│ ├─────────────────────┼─────────────────────────────────────┤",
"│ │ extra │ default │",
"│ ├─────────────────────┼─────────────────────────────────────┤",
"│ │ plugin │ nu-plugin │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ nu-cli/plugin │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ nu-parser/plugin │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ nu-command/plugin │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ nu-protocol/plugin │",
"│ │ ├─────────────────────────────────────┤",
"│ │ │ nu-engine/plugin │",
"│ ├─────────────────────┼─────────────────────────────────────┤",
"│ │ stable │ default │",
"│ ├─────────────────────┼─────────────────────────────────────┤",
"│ │ static-link-openssl │ dep:openssl │",
"│ ├─────────────────────┼─────────────────────────────────────┤",
"│ │ trash-support │ nu-command/trash-support │",
"│ ├─────────────────────┼─────────────────────────────────────┤",
"│ │ wasi │ │",
"│ ├─────────────────────┼─────────────────────────────────────┤",
"│ │ which-support │ nu-command/which-support │",
"├──────────────────┼───────────────┬─────┴─────────────────────────────────────┤",
"│ package │ authors │ The Nushell Project Developers │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ default-run │ nu │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ description │ A new type of shell │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ documentation │ https://www.nushell.sh/book/ │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ edition │ 2021 │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ exclude │ images │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ homepage │ https://www.nushell.sh │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ license │ MIT │",
"│ ├───────────────┼──────────┬───────────┬────────────────────┤",
"│ │ metadata │ binstall │ overrides │ ... │",
"│ │ │ ├───────────┼────────────────────┤",
"│ │ │ │ pkg-fmt │ tgz │",
"│ │ │ ├───────────┼────────────────────┤",
"│ │ │ │ pkg-url │ { repo }/releases/ │",
"│ │ │ │ │ download/{ v │",
"│ │ │ │ │ ersion │",
"│ │ │ │ │ }/{ name }-{ vers │",
"│ │ │ │ │ ion }- │",
"│ │ │ │ │ { target }.{ │",
"│ │ │ │ │ archive-format } │",
"│ ├───────────────┼──────────┴───────────┴────────────────────┤",
"│ │ name │ nu │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ repository │ https://github.com/nushell/nushell │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ rust-version │ 1.60 │",
"│ ├───────────────┼───────────────────────────────────────────┤",
"│ │ version │ 0.74.1 │",
"├──────────────────┼───────────┬───┴──────┬────────┬───────────────────────────┤",
"│ patch │ crates-io │ reedline │ branch │ main │",
"│ │ │ ├────────┼───────────────────────────┤",
"│ │ │ │ git │ https://github.com/nushel │",
"│ │ │ │ │ l/reedline.git │",
"├──────────────────┼───────────┴──────────┴────────┴─┬──────────────┬──────────┤",
"│ target │ cfg(not(target_os = \"windows\")) │ dependencies │ ... │",
"│ │ │ ├──────────┤",
"│ │ │ │ ... │",
"│ ├─────────────────────────────────┼──────────────┼──────────┤",
"│ │ cfg(target_family = \"unix\") │ dependencies │ ... │",
"│ │ │ ├──────────┤",
"│ │ │ │ ... │",
"│ ├─────────────────────────────────┼──────────────┴──────────┤",
"│ │ cfg(windows) │ ... │",
"├──────────────────┼─────────┬───────────────────────┴─────────────────────────┤",
"│ workspace │ members │ crates/nu-cli │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu-engine │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu-parser │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu-system │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu-command │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu-protocol │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu-plugin │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_inc │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_gstat │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_example │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_query │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_custom_values │",
"│ │ ├─────────────────────────────────────────────────┤",
"│ │ │ crates/nu-utils │",
"╰──────────────────┴─────────┴─────────────────────────────────────────────────╯",
]);
assert_eq!(actual.out, expected);
let actual = nu!(
cwd: dirs.test(), pipeline(
"open sample.toml | table --collapse --width=160"
));
_print_lines(&actual.out, 111);
let expected = join_lines([
"╭──────────────────┬─────────┬────────────────────────────────────────────────────────────────────────────────╮",
"│ bench │ harness │ name │",
"│ ├─────────┼────────────────────────────────────────────────────────────────────────────────┤",
"│ │ false │ benchmarks │",
"├──────────────────┼──────┬──┴────────────────────────────────────────────────────────────────────────────────┤",
"│ bin │ name │ path │",
"│ ├──────┼───────────────────────────────────────────────────────────────────────────────────┤",
"│ │ nu │ src/main.rs │",
"├──────────────────┼──────┴────────┬──────────┬───────────────────────────────────────────────────────────────┤",
"│ dependencies │ chrono │ features │ serde │",
"│ │ ├──────────┼───────────────────────────────────────────────────────────────┤",
"│ │ │ version │ 0.4.23 │",
"│ ├───────────────┼──────────┴───────────────────────────────────────────────────────────────┤",
"│ │ crossterm │ 0.24.0 │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ ctrlc │ 3.2.1 │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ is_executable │ 1.0.1 │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ log │ 0.4 │",
"│ ├───────────────┼──────────┬───────────────────────────────────────────────────────────────┤",
"│ │ miette │ features │ fancy-no-backtrace │",
"│ │ ├──────────┼───────────────────────────────────────────────────────────────┤",
"│ │ │ version │ 5.5.0 │",
"│ ├───────────────┼──────────┴───────────────────────────────────────────────────────────────┤",
"│ │ nu-ansi-term │ 0.46.0 │",
"│ ├───────────────┼─────────┬────────────────────────────────────────────────────────────────┤",
"│ │ nu-cli │ path │ ./crates/nu-cli │",
"│ │ ├─────────┼────────────────────────────────────────────────────────────────┤",
"│ │ │ version │ 0.74.1 │",
"│ ├───────────────┼─────────┼────────────────────────────────────────────────────────────────┤",
"│ │ nu-engine │ path │ ./crates/nu-engine │",
"│ │ ├─────────┼────────────────────────────────────────────────────────────────┤",
"│ │ │ version │ 0.74.1 │",
"│ ├───────────────┼─────────┴────────────────────────────────────────────────────────────────┤",
"│ │ rayon │ 1.6.1 │",
"│ ├───────────────┼──────────┬───────────────────────────────────────────────────────────────┤",
"│ │ reedline │ features │ bashisms │",
"│ │ │ ├───────────────────────────────────────────────────────────────┤",
"│ │ │ │ sqlite │",
"│ │ ├──────────┼───────────────────────────────────────────────────────────────┤",
"│ │ │ version │ 0.14.0 │",
"│ ├───────────────┼──────────┴───────────────────────────────────────────────────────────────┤",
"│ │ simplelog │ 0.12.0 │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ time │ 0.3.12 │",
"├──────────────────┼───────────────┴───┬──────────────────────────────────────────────────────────────────────┤",
"│ dev-dependencies │ assert_cmd │ 2.0.2 │",
"│ ├───────────────────┼──────────────────────────────────────────────────────────────────────┤",
"│ │ criterion │ 0.4 │",
"│ ├───────────────────┼──────────────────────────────────────────────────────────────────────┤",
"│ │ hamcrest2 │ 0.3.0 │",
"│ ├───────────────────┼──────────────────────────────────────────────────────────────────────┤",
"│ │ itertools │ 0.10.3 │",
"│ ├───────────────────┼─────────┬────────────────────────────────────────────────────────────┤",
"│ │ nu-test-support │ path │ ./crates/nu-test-support │",
"│ │ ├─────────┼────────────────────────────────────────────────────────────┤",
"│ │ │ version │ 0.74.1 │",
"│ ├───────────────────┼─────────┴────────────────────────────────────────────────────────────┤",
"│ │ pretty_assertions │ 1.0.0 │",
"│ ├───────────────────┼──────────────────┬───────────────────────────────────────────────────┤",
"│ │ rstest │ default-features │ false │",
"│ │ ├──────────────────┼───────────────────────────────────────────────────┤",
"│ │ │ version │ 0.15.0 │",
"│ ├───────────────────┼──────────────────┴───────────────────────────────────────────────────┤",
"│ │ serial_test │ 0.10.0 │",
"│ ├───────────────────┼──────────────────────────────────────────────────────────────────────┤",
"│ │ tempfile │ 3.2.0 │",
"├──────────────────┼───────────────────┴─┬────────────────────────────────────────────────────────────────────┤",
"│ features │ default │ plugin │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ which-support │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ trash-support │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ sqlite │",
"│ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤",
"│ │ extra │ default │",
"│ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤",
"│ │ plugin │ nu-plugin │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ nu-cli/plugin │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ nu-parser/plugin │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ nu-command/plugin │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ nu-protocol/plugin │",
"│ │ ├────────────────────────────────────────────────────────────────────┤",
"│ │ │ nu-engine/plugin │",
"│ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤",
"│ │ stable │ default │",
"│ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤",
"│ │ static-link-openssl │ dep:openssl │",
"│ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤",
"│ │ trash-support │ nu-command/trash-support │",
"│ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤",
"│ │ wasi │ │",
"│ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤",
"│ │ which-support │ nu-command/which-support │",
"├──────────────────┼───────────────┬─────┴────────────────────────────────────────────────────────────────────┤",
"│ package │ authors │ The Nushell Project Developers │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ default-run │ nu │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ description │ A new type of shell │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ documentation │ https://www.nushell.sh/book/ │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ edition │ 2021 │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ exclude │ images │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ homepage │ https://www.nushell.sh │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ license │ MIT │",
"│ ├───────────────┼──────────┬───────────┬────────────────────────┬─────────┬────────────────┤",
"│ │ metadata │ binstall │ overrides │ x86_64-pc-windows-msvc │ pkg-fmt │ zip │",
"│ │ │ ├───────────┼────────────────────────┴─────────┴────────────────┤",
"│ │ │ │ pkg-fmt │ tgz │",
"│ │ │ ├───────────┼───────────────────────────────────────────────────┤",
"│ │ │ │ pkg-url │ { repo }/releases/download/{ v │",
"│ │ │ │ │ ersion }/{ name }-{ version }- │",
"│ │ │ │ │ { target }.{ archive-format } │",
"│ ├───────────────┼──────────┴───────────┴───────────────────────────────────────────────────┤",
"│ │ name │ nu │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ repository │ https://github.com/nushell/nushell │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ rust-version │ 1.60 │",
"│ ├───────────────┼──────────────────────────────────────────────────────────────────────────┤",
"│ │ version │ 0.74.1 │",
"├──────────────────┼───────────┬───┴──────┬────────┬──────────────────────────────────────────────────────────┤",
"│ patch │ crates-io │ reedline │ branch │ main │",
"│ │ │ ├────────┼──────────────────────────────────────────────────────────┤",
"│ │ │ │ git │ https://github.com/nushell/reedline.git │",
"├──────────────────┼───────────┴──────────┴────────┴─┬──────────────┬─────────────┬──────────┬────────────────┤",
"│ target │ cfg(not(target_os = \"windows\")) │ dependencies │ openssl │ features │ vendored │",
"│ │ │ │ ├──────────┼────────────────┤",
"│ │ │ │ │ optional │ true │",
"│ │ │ │ ├──────────┼────────────────┤",
"│ │ │ │ │ version │ 0.10.38 │",
"│ │ │ ├─────────────┼──────────┴───────┬────────┤",
"│ │ │ │ signal-hook │ default-features │ false │",
"│ │ │ │ ├──────────────────┼────────┤",
"│ │ │ │ │ version │ 0.3.14 │",
"│ ├─────────────────────────────────┼──────────────┼──────┬──────┴──────────────────┴────────┤",
"│ │ cfg(target_family = \"unix\") │ dependencies │ atty │ 0.2 │",
"│ │ │ ├──────┼──────────────────┬───────────────┤",
"│ │ │ │ nix │ default-features │ false │",
"│ │ │ │ ├──────────────────┼───────────────┤",
"│ │ │ │ │ features │ signal │",
"│ │ │ │ │ ├───────────────┤",
"│ │ │ │ │ │ process │",
"│ │ │ │ │ ├───────────────┤",
"│ │ │ │ │ │ fs │",
"│ │ │ │ │ ├───────────────┤",
"│ │ │ │ │ │ term │",
"│ │ │ │ ├──────────────────┼───────────────┤",
"│ │ │ │ │ version │ 0.25 │",
"│ ├─────────────────────────────────┼──────────────┴─────┬┴───────┬──────────┴───────────────┤",
"│ │ cfg(windows) │ build-dependencies │ winres │ 0.1 │",
"├──────────────────┼─────────┬───────────────────────┴────────────────────┴────────┴──────────────────────────┤",
"│ workspace │ members │ crates/nu-cli │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu-engine │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu-parser │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu-system │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu-command │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu-protocol │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu-plugin │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_inc │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_gstat │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_example │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_query │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu_plugin_custom_values │",
"│ │ ├────────────────────────────────────────────────────────────────────────────────┤",
"│ │ │ crates/nu-utils │",
"╰──────────────────┴─────────┴────────────────────────────────────────────────────────────────────────────────╯",
]);
assert_eq!(actual.out, expected);
})
}
fn join_lines(lines: impl IntoIterator<Item = impl AsRef<str>>) -> String {
lines
.into_iter()

View File

@ -1,37 +1,22 @@
use nu_test_support::{nu, pipeline};
use nu_test_support::nu;
#[test]
fn row() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[[key value]; [foo 1] [foo 2]] | transpose -r | debug
"#
));
let actual = nu!("[[key value]; [foo 1] [foo 2]] | transpose -r | debug");
assert!(actual.out.contains("foo: 1"));
}
#[test]
fn row_but_last() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[[key value]; [foo 1] [foo 2]] | transpose -r -l | debug
"#
));
let actual = nu!("[[key value]; [foo 1] [foo 2]] | transpose -r -l | debug");
assert!(actual.out.contains("foo: 2"));
}
#[test]
fn row_but_all() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[[key value]; [foo 1] [foo 2]] | transpose -r -a | debug
"#
));
let actual = nu!("[[key value]; [foo 1] [foo 2]] | transpose -r -a | debug");
assert!(actual.out.contains("foo: [1, 2]"));
}

View File

@ -23,7 +23,6 @@ fn removes_duplicate_rows() {
open los_tres_caballeros.csv
| uniq
| length
"#
));
@ -53,7 +52,6 @@ fn uniq_values() {
| select type
| uniq
| length
"#
));
@ -125,7 +123,6 @@ fn nested_json_structures() {
open nested_json_structures.json
| uniq
| length
"#
));
assert_eq!(actual.out, "3");
@ -134,13 +131,11 @@ fn nested_json_structures() {
#[test]
fn uniq_when_keys_out_of_order() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
[{"a": "a", "b": [1,2,3]}, {"b": [1,2,3], "a": "a"}]
| uniq
| length
"#
));
@ -149,8 +144,7 @@ fn uniq_when_keys_out_of_order() {
#[test]
fn uniq_counting() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
["A", "B", "A"]
| wrap item
@ -163,10 +157,9 @@ fn uniq_counting() {
));
assert_eq!(actual.out, "2");
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
echo ["A", "B", "A"]
["A", "B", "A"]
| wrap item
| uniq --count
| flatten
@ -180,89 +173,41 @@ fn uniq_counting() {
#[test]
fn uniq_unique() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [1 2 3 4 1 5]
| uniq --unique
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [2 3 4 5]
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
let actual = nu!("[1 2 3 4 1 5] | uniq --unique");
let expected = nu!("[2 3 4 5]");
assert_eq!(actual.out, expected.out);
}
#[test]
fn uniq_simple_vals_ints() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [1 2 3 4 1 5]
| uniq
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [1 2 3 4 5]
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
let actual = nu!("[1 2 3 4 1 5] | uniq");
let expected = nu!("[1 2 3 4 5]");
assert_eq!(actual.out, expected.out);
}
#[test]
fn uniq_simple_vals_strs() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [A B C A]
| uniq
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [A B C]
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
let actual = nu!("[A B C A] | uniq");
let expected = nu!("[A B C]");
assert_eq!(actual.out, expected.out);
}
#[test]
fn table() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
[[fruit day]; [apple monday] [apple friday] [Apple friday] [apple monday] [pear monday] [orange tuesday]]
| uniq
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [[fruit day]; [apple monday] [apple friday] [Apple friday] [pear monday] [orange tuesday]]
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
let expected = nu!("[[fruit day]; [apple monday] [apple friday] [Apple friday] [pear monday] [orange tuesday]]");
assert_eq!(actual.out, expected.out);
}
#[test]
fn table_with_ignore_case() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
[[origin, people];
[World, (
@ -284,8 +229,7 @@ fn table_with_ignore_case() {
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
let expected = nu!(pipeline(
r#"
echo [[origin, people];
[World, (
@ -302,8 +246,5 @@ fn table_with_ignore_case() {
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
assert_eq!(actual.out, expected.out);
assert_eq!(actual.out, expected.out);
}

View File

@ -22,7 +22,6 @@ fn removes_duplicate_rows() {
open los_tres_caballeros.csv
| uniq-by last_name
| length
"#
));
@ -32,30 +31,15 @@ fn removes_duplicate_rows() {
#[test]
fn uniq_when_keys_out_of_order() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
[{"a": "a", "b": [1,2,3]}, {"b": [1,2,3,4], "a": "a"}]
| uniq-by a
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [{"a": "a", "b": [1,2,3]}]
"#
));
let actual = nu!(r#"[{"a": "a", "b": [1,2,3]}, {"b": [1,2,3,4], "a": "a"}] | uniq-by a"#);
let expected = nu!(r#"[{"a": "a", "b": [1,2,3]}]"#);
print!("{}", actual.out);
print!("{}", expected.out);
assert_eq!(actual.out, expected.out);
assert_eq!(actual.out, expected.out);
}
#[test]
fn uniq_counting() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
["A", "B", "A"]
| wrap item
@ -68,10 +52,9 @@ fn uniq_counting() {
));
assert_eq!(actual.out, "2");
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
echo ["A", "B", "A"]
["A", "B", "A"]
| wrap item
| uniq-by item --count
| flatten
@ -85,8 +68,7 @@ fn uniq_counting() {
#[test]
fn uniq_unique() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
echo [1 2 3 4 1 5]
| wrap item
@ -94,29 +76,20 @@ fn uniq_unique() {
| get item
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [2 3 4 5]
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
let expected = nu!("[2 3 4 5]");
assert_eq!(actual.out, expected.out);
}
#[test]
fn table() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
[[fruit day]; [apple monday] [apple friday] [Apple friday] [apple monday] [pear monday] [orange tuesday]]
| uniq-by fruit
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
let expected = nu!(pipeline(
r#"
echo [[fruit day]; [apple monday] [Apple friday] [pear monday] [orange tuesday]]
"#
@ -135,29 +108,24 @@ fn uniq_by_empty() {
#[test]
fn uniq_by_multiple_columns() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
[[fruit day]; [apple monday] [apple friday] [Apple friday] [apple monday] [pear monday] [orange tuesday]]
| uniq-by fruit day
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
let expected = nu!(pipeline(
r#"
echo [[fruit day]; [apple monday] [apple friday] [Apple friday] [pear monday] [orange tuesday]]
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
assert_eq!(actual.out, expected.out);
}
#[test]
fn table_with_ignore_case() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
let actual = nu!(pipeline(
r#"
[[origin, people];
[World, (
@ -179,8 +147,7 @@ fn table_with_ignore_case() {
"#
));
let expected = nu!(
cwd: "tests/fixtures/formats", pipeline(
let expected = nu!(pipeline(
r#"
echo [[origin, people];
[World, (
@ -197,33 +164,19 @@ fn table_with_ignore_case() {
"#
));
print!("{}", actual.out);
print!("{}", expected.out);
assert_eq!(actual.out, expected.out);
assert_eq!(actual.out, expected.out);
}
#[test]
fn missing_parameter() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
[11 22 33] | uniq-by
"#
));
let actual = nu!("[11 22 33] | uniq-by");
assert!(actual.err.contains("missing parameter"));
}
#[test]
fn wrong_column() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
[[fruit day]; [apple monday] [apple friday]]
| uniq-by column1
"#
));
let actual = nu!("[[fruit day]; [apple monday] [apple friday]] | uniq-by column1");
assert!(actual.err.contains("cannot find column 'column1'"));
}

View File

@ -16,9 +16,7 @@ fn sets_the_column() {
#[test]
fn doesnt_convert_record_to_table() {
let actual = nu!(
cwd: ".", r#"{a:1} | update a 2 | to nuon"#
);
let actual = nu!("{a:1} | update a 2 | to nuon");
assert_eq!(actual.out, "{a: 2}");
}
@ -83,44 +81,27 @@ fn upsert_column_missing() {
#[test]
fn update_list() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[1, 2, 3] | update 1 abc | to json -r
"#
));
let actual = nu!("[1, 2, 3] | update 1 abc | to json -r");
assert_eq!(actual.out, r#"[1,"abc",3]"#);
}
#[test]
fn update_past_end_list() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[1, 2, 3] | update 5 abc | to json -r
"#
));
let actual = nu!("[1, 2, 3] | update 5 abc | to json -r");
assert!(actual.err.contains("too large"));
}
#[test]
fn update_nonexistent_column() {
let actual = nu!(
cwd: ".", pipeline(
r#"{a:1} | update b 2"#
));
let actual = nu!("{a:1} | update b 2");
assert!(actual.err.contains("cannot find column 'b'"));
}
#[test]
fn update_uses_enumerate_index() {
let actual = nu!(
cwd: ".", pipeline(
r#"[[a]; [7] [6]] | enumerate | update item.a {|el| $el.index + 1 + $el.item.a } | flatten | to nuon"#
));
);
assert_eq!(actual.out, "[[index, a]; [0, 8], [1, 8]]");
}

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