Compare commits

...

335 Commits

Author SHA1 Message Date
17a265b197 Version bump for 0.75 release (#7902)
- [x] Are we ready for the release
- [x] Upgrade to upcoming `reedline 0.15`
2023-01-31 21:00:59 +01:00
3fabc8e1e6 update type check so that ++ with lists works better (#7926)
closes https://github.com/nushell/nushell/issues/7913
2023-01-31 21:11:05 +02:00
517ef7cde7 Remove deprecated where -b parameter (#7927) 2023-01-31 21:05:28 +02:00
ad14b763f9 Pin reedline to new 0.15 for release (#7918)
# Description

See release notes:

https://github.com/nushell/reedline/releases/tag/v0.15.0
2023-01-30 22:59:15 +01:00
f74694d5a3 Let redirection keep exit code (#7848)
# Description

Fixes: #7828

We delegate to `save` command to finish redirection, then if it runs to
success, the relative exit code is set to 0. To fix it, in redirection
context, we take exit_code stream before sending it to `save` command,
than manually returns `PipelineData::ExternalStream` to make nushell set
relative code properly.

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-30 16:49:31 +01:00
1ea39abcff Apply more recent/nightly clippy lints (#7916)
# Description

- Use inline format strings in dataframe code
- Fix manual `.is_ascii_digit()` check
- Remove unnecessary `.into_iter()` calls
2023-01-30 14:06:36 +01:00
7402589775 Bump trash to 3.0.1 (#7914)
# Description

Avoids duplication of `windows` crate and friends as it updates to the
most recent `windows 0.44` version.

# User-Facing Changes

None intended
2023-01-30 11:58:56 +01:00
e0cd5a714a Bump serial_test from 0.10.0 to 1.0.0 (#7910) 2023-01-30 02:47:51 +00:00
c6eea5de6b Bump typetag from 0.1.8 to 0.2.5 (#7908) 2023-01-30 02:46:31 +00:00
809416e3f0 Bump roxmltree from 0.16.0 to 0.17.0 (#7909) 2023-01-30 02:43:11 +00:00
040d812343 Bump windows from 0.43.0 to 0.44.0 (#7911) 2023-01-30 02:17:18 +00:00
72465e6724 Bump chrono-tz from 0.6.3 to 0.8.1 (#7907) 2023-01-30 01:38:39 +00:00
ab480856a5 Use variable names directly in the format strings (#7906)
# Description

Lint: `clippy::uninlined_format_args`

More readable in most situations.
(May be slightly confusing for modifier format strings
https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters)

Alternative to #7865

# User-Facing Changes

None intended

# Tests + Formatting

(Ran `cargo +stable clippy --fix --workspace -- -A clippy::all -D
clippy::uninlined_format_args` to achieve this. Depends on Rust `1.67`)
2023-01-29 19:37:54 -06:00
6ae497eedc Remove unused nu-test-support in nu-table (#7905)
Unused dev-dependency
2023-01-29 23:36:46 +01:00
JT
421bc828ef Use clippy-recommended simplification (#7904)
# Description

Just a couple clippy-recommended simplifications.

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-29 23:31:35 +01:00
ed65886ae5 Update reedline for pre-release testing (#7903)
# Description

Let's check before shipping `reedline 0.15`



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-29 22:33:49 +01:00
8c7e2dbdf9 make parse -r columns return 0-indexed uncapitalised (#7897)
# Description

Fixes #7886.

```
/home/gabriel/CodingProjects/nushell〉'A|B|C' | parse -r '(\w)\|(\w)\|(\w)'                                                                                             01/29/2023 01:08:29 PM
╭───┬──────────┬──────────┬──────────╮
│ # │ capture0 │ capture1 │ capture2 │
├───┼──────────┼──────────┼──────────┤
│ 0 │ A        │ B        │ C        │
╰───┴──────────┴──────────┴──────────╯
```

# User-Facing Changes
Columns automatically named by `parse -r` are now 0-indexec and
uncapitalised.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-29 07:34:34 -06:00
afb4209f10 make help commands search term don't generate $nothing (#7896)
# Description

Relative:
`https://github.com/nushell/nushell/pull/7889#issuecomment-1407503567`

Make `search_terms` return empty string rather than nothing, so some
other command can handle it better

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-28 18:57:26 -06:00
1d8775d237 mention do in complete command's doc (#7884)
# Description

Closes: #7841

# User-Facing Changes

new complete doc:
```
Complete the external piped in, collecting outputs and exit code

To collect stderr messages and exit_code, external piped in need to wrapped with `do`

Usage:
  > complete

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

Signatures:
  <any> | complete -> <record>

Examples:
  Run the external completion
  > ^external arg1 | complete

  Run external completion, collects stderr and exit_code
  > do { ^external arg1 } | complete
```

# Tests + Formatting

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

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

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

# After Submitting

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

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-01-28 16:42:05 -06:00
8787ec9fe8 Add Github Actions workflow to check for typos (#7892)
- Add Github Actions workflow to check typos
- Fix existing typos
2023-01-29 10:22:56 +13:00
1f810cd26a Re-enable some good tests, remove some bad tests (#7875)
I tackled some of the disabled `FIXME`/`#[ignore]` tests. Most were
straightforward to re-enable, and a few of them did not deserve to be
re-enabled.

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-01-28 14:56:47 -06:00
f4d7d19370 Name threads (#7879)
I noticed that [it's pretty easy to name threads in
Rust](https://doc.rust-lang.org/std/thread/#naming-threads). We might as
well do this; it's a nice quality of life improvement when you're
profiling something and the developers took the time to give threads
names.

Also added/cleaned up some comments while I was in the area.
2023-01-28 21:40:52 +01:00
e616b2e247 Support extended unicode escapes in strings: "\u{10fff}" (#7883)
# Description

Support extended unicode escapes in strings with same syntax as Rust:
`"\u{6e}"`.

# User-Facing Changes

New syntax in string literals, `\u{NNNNNN}`, to go along with the
existing `\uNNNN`.
New syntax accepts 1-6 hex digits and rejects values greater than
0x10FFFF (max Unicode char)..

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

Won't break existing scripts, since this is new syntax.  

We might consider deprecating `char -u`, since users can now embed
unicode chars > 0xFFFF with the new escape.

# Tests + Formatting

Several unit tests and one integration test added.

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

# After Submitting

- [ ] If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-29 09:25:53 +13:00
2a39332d51 Fix panic when assigning value to $env (#7894) 2023-01-28 21:17:32 +02:00
3c6b10c6b2 Fix wrong VarId of $in variable (#7893)
Fixes https://github.com/nushell/nushell/issues/7872
2023-01-28 19:55:29 +02:00
2a9226a55c refactor: use input_handler::operate in ansi_strip command (#7888)
# Description

While investigating `do --ignore-errors` issue, just found that
`ansi_strip` command using a custom `operate` function(which is not
needed), this pr is just a refactor

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-28 08:11:21 -06:00
3d65fd7cc4 Expose filtering by file type in glob (#7834)
# Description

Add flags for filtering the output of `glob` by file type. I find myself
occasionally wanting to do this, and getting a file's
[file_type](https://docs.rs/wax/latest/wax/struct.WalkEntry.html#method.file_type)
is presumably fast to do as it doesn't have to go through the fallible
metadata method.

The design of the signature does concern me; it's not as readable as a
filter or "include" type list would be. They have to be filtered one by
one, which can be annoying if you only want files `-D -S`, or only want
folders `-F -S`, or only want symlinks `--butwhy?`. I considered
SyntaxShape::Keyword for this but I'll just defer to comments on this PR
if they pop up.

I'd also like to bring up performance since including these flags
technically incurs a `.filter` penalty on all glob calls, which could be
optimized out if we added a branch for the no-filters case. But in
reality I'd expect the file system to be the bottleneck and the flags to
be pretty branch predictor friendly, so eh

# User-Facing Changes
Three new flags when using `glob` and a slightly more cluttered help
page. No breaking changes, I hope.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-28 07:50:12 -06:00
JT
36ddbfdc85 Add 'number' command for enumeration (#7871)
# Description

This adds a `number` command that will enumerate the input, and add an
`index` and `item` record for each item. The `index` is the number of
the item in the input stream, and `item` is the original value of the
item.

```
> ls | number | get 14
╭───────┬────────────────────────────╮
│ index │ 14                         │
│       │ ╭──────────┬─────────────╮ │
│ item  │ │ name     │ crates      │ │
│       │ │ type     │ dir         │ │
│       │ │ size     │ 832 B       │ │
│       │ │ modified │ 2 weeks ago │ │
│       │ ╰──────────┴─────────────╯ │
╰───────┴────────────────────────────╯
```

# User-Facing Changes

This adds a `number` command.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-28 06:45:57 +13:00
76292ef10c Clean up cd.rs (#7876)
Some general cleanup of `cd.rs`; the permission checking code was a
little hard to follow. Reworded comments and variable names,
reorganized+renamed the module used for Unix file permissions.
2023-01-27 15:02:38 +01:00
9ae2e528c5 Remove 🆖 comments (#7877)
Noticed several instances of commented out code that should just be
deleted. Also a comment on `eval_external` that was incorrect. All gone
now.
2023-01-27 08:48:31 -05:00
2849e28c2b Extract gather_commandline_args (#7868)
- Extract gathering of command line arguments
- Simplify the function a bit
2023-01-26 17:56:55 -06:00
9d0e52b94d with the release of rust 1.67, let's bump to 1.66.1 (#7866)
# Description

This PR bumps the required rust version to 1.66.1.

# User-Facing Changes


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-26 15:31:17 -06:00
731f5f8523 nu-commands/table (table -e) Recognize limited space better (#7861)
fix #7858

Once again we here 😞 

~~I am thinking is there some files with not flat structure we could use
to test table -e?
I mean it is clear it was a while ago were we had to create at least
some tests.
Do you have anything in mind (or maybe commands which is consistent
across systems)?~~

Take care

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2023-01-26 14:06:17 -06:00
9d6d43ee55 Fix do swallowing all output when ignoring errors (#7859)
https://github.com/nushell/nushell/pull/7204#issuecomment-1404363845
2023-01-26 13:00:48 +01:00
f9e99048c4 convert SyntaxShape::Table into the corresponding Type (#7781)
# Description

Small fix. Related: #7699.

# User-Facing Changes

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-25 16:17:41 -06:00
e1df8d14b4 improve doc about flatten (#7856)
# Description

Relative #7210 

Improve doc to clarify current behavior.

To flatten all nested levels, we can use the following custom
command(maybe making it into our lib):
```
def flatten_all_nested [input_table: any] {
    mut input = $input_table

    mut flattened = ($input | flatten --all)
    while $input != $flattened {
        $input = $flattened
        $flattened = ($input | flatten --all)
    }
    $flattened
}
```

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-25 10:10:30 -08:00
b9419e0f36 To csv fix (#7850)
# Description

Fixes #7800 . 
`to csv` and `to tsv` no longer:
- accept anything but records and tables as input,
- accept lists that are not tables,
- accept tables and records with values that are not primitives (other
lists, tables and records).

# User-Facing Changes

Using `to csv` and `to tsv` on any of inputs mentioned above will result
in `cant_convert` error.

# Tests + Formatting

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

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

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

# After Submitting

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

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-01-25 08:42:53 -06:00
e03c354e89 add decimal to SyntaxShape (#7852)
# Description

This adds the `SyntaxShape::Decimal` so you can create custom commands
with `decimal` types such as:
```shell
def cmd [x:decimal] { echo $x }
```

/cc @kurokirasama

Internally this is a little messy since we have `Type::Float` and
`SyntaxShape::Decimal`. I originally named it `float` and
`SyntaxShape::Float` but since we have `into decimal` and `1.1 |
describe` reports `decimal`, I decided to change the SyntaxShape.

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-25 06:43:22 -06:00
2e44e4d33c Fix the build after #7204 (#7857)
Fix the build after merging
https://github.com/nushell/nushell/pull/7204. It sat for a bit too long
and I should have rerun CI before merging it, my bad.
2023-01-24 22:12:15 -08:00
5cbaabeeab Fix pipeline stall in do during capture and remove excessive redirections (#7204)
Currently, if you run `do -i { sudo apt upgrade }`, stdin gets swallowed
and doesn't let you respond yes/no to the upgrade question. This PR
fixes that, but runs into https://github.com/nushell/nushell/issues/7205
so the tests fail.

Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
2023-01-25 00:24:38 -05:00
d64e381085 add some startup performance metrics (#7851)
# Description

This PR changes the old performance logging with `Instant` timers. I'm
not sure if this is the best way to do it but it does help reveal where
time is being spent on startup. This is what it looks like when you
launch nushell with `cargo run -- --log-level info`. I'm using the
`info` log level exclusively for performance monitoring at this point.

![image](https://user-images.githubusercontent.com/343840/214372903-fdfa9c99-b846-47f3-8faf-bd6ed98df3a9.png)
## After Startup

Since you're in the repl, you can continue running commands. Here's the
output of `ls`, for instance.

![image](https://user-images.githubusercontent.com/343840/214373035-4d2f6e2d-5c1d-43d3-b997-51d79d496ba3.png)
Note that the above screenshots are in debug mode, so they're much
slower than release.

# User-Facing Changes



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-24 14:28:59 -06:00
41306aa7e0 Reduce again the number of match calls (#7815)
- Reduce the number of match calls (see commit messages)
- A few miscellaneous improvements
2023-01-24 12:23:42 +01:00
0bb2e47c98 Incorrect parsing of unbalanced braces based on issue 6914 (#7621) 2023-01-24 10:05:46 +02:00
ef660be285 print nushell startup time (#7831)
# Description

This PR shows the startup time and decreases the banner. This startup
time output can be disabled with the `show_banner: false` setting in the
config. This is the startup in debug mode.

![image](https://user-images.githubusercontent.com/343840/213955410-f319f8d4-1f96-47ae-8366-1c564a08d3e4.png)

On my mac in release mode
```
Startup Time: 368ms 429µs 83ns
```
On my mac without a config as `nu --config foo --env-config foo`
```
Startup Time: 11ms 663µs 791ns
```

I could really go either way on this. If people don't like this change,
we don't have to merge it.

# User-Facing Changes

Startup Time

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-23 12:57:40 -06:00
4bac90a3b2 Bump rayon from 1.5.3 to 1.6.1 (#7836)
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.5.3 to 1.6.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rayon-rs/rayon/blob/master/RELEASES.md">rayon's
changelog</a>.</em></p>
<blockquote>
<h1>Release rayon 1.6.1 (2022-12-09)</h1>
<ul>
<li>Simplified <code>par_bridge</code> to only pull one item at a time
from the iterator,
without batching. Threads that are waiting for iterator items will now
block
appropriately rather than spinning CPU. (Thanks <a
href="https://github.com/njaard"><code>@​njaard</code></a>!)</li>
<li>Added protection against recursion in <code>par_bridge</code>, so
iterators that also
invoke rayon will not cause mutex recursion deadlocks.</li>
</ul>
<h1>Release rayon-core 1.10.1 (2022-11-18)</h1>
<ul>
<li>Fixed a race condition with threads going to sleep while a broadcast
starts.</li>
</ul>
<h1>Release rayon 1.6.0 / rayon-core 1.10.0 (2022-11-18)</h1>
<ul>
<li>The minimum supported <code>rustc</code> is now 1.56.</li>
<li>The new <code>IndexedParallelIterator::fold_chunks</code> and
<code>fold_chunks_with</code> methods
work like <code>ParallelIterator::fold</code> and <code>fold_with</code>
with fixed-size chunks of
items. This may be useful for predictable batching performance, without
the
allocation overhead of
<code>IndexedParallelIterator::chunks</code>.</li>
<li>New &quot;broadcast&quot; methods run a given function on all
threads in the pool.
These run at a sort of reduced priority after each thread has exhausted
their
local work queue, but before they attempt work-stealing from other
threads.
<ul>
<li>The global <code>broadcast</code> function and
<code>ThreadPool::broadcast</code> method will
block until completion, returning a <code>Vec</code> of all return
values.</li>
<li>The global <code>spawn_broadcast</code> function and methods on
<code>ThreadPool</code>, <code>Scope</code>,
and <code>ScopeFifo</code> will run detached, without blocking the
current thread.</li>
</ul>
</li>
<li>Panicking methods now use <code>#[track_caller]</code> to report the
caller's location.</li>
<li>Fixed a truncated length in <code>vec::Drain</code> when given an
empty range.</li>
</ul>
<h2>Contributors</h2>
<p>Thanks to all of the contributors for this release!</p>
<ul>
<li><a href="https://github.com/cuviper"><code>@​cuviper</code></a></li>
<li><a
href="https://github.com/idanmuze"><code>@​idanmuze</code></a></li>
<li><a href="https://github.com/JoeyBF"><code>@​JoeyBF</code></a></li>
<li><a
href="https://github.com/JustForFun88"><code>@​JustForFun88</code></a></li>
<li><a
href="https://github.com/kianmeng"><code>@​kianmeng</code></a></li>
<li><a
href="https://github.com/kornelski"><code>@​kornelski</code></a></li>
<li><a
href="https://github.com/ritchie46"><code>@​ritchie46</code></a></li>
<li><a
href="https://github.com/ryanrussell"><code>@​ryanrussell</code></a></li>
<li><a
href="https://github.com/steffahn"><code>@​steffahn</code></a></li>
<li><a
href="https://github.com/TheIronBorn"><code>@​TheIronBorn</code></a></li>
<li><a
href="https://github.com/willcrozi"><code>@​willcrozi</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="401678ee55"><code>401678e</code></a>
Merge <a
href="https://github-redirect.dependabot.com/rayon-rs/rayon/issues/709">#709</a></li>
<li><a
href="33e9843413"><code>33e9843</code></a>
Release rayon 1.2.1 / rayon-core 1.6.1</li>
<li><a
href="dd874ac5d4"><code>dd874ac</code></a>
Bump crate versions and dependencies</li>
<li><a
href="0c6338d267"><code>0c6338d</code></a>
Reduce Option complexity in demo cpu_time</li>
<li><a
href="be99e500bf"><code>be99e50</code></a>
cargo fmt</li>
<li><a
href="9b4d9798de"><code>9b4d979</code></a>
Avoid mem::uninitialized in the demo cpu_time</li>
<li><a
href="5a466434ab"><code>5a46643</code></a>
Avoid mem::uninitialized in par_sort_unstable</li>
<li><a
href="73b1061a23"><code>73b1061</code></a>
Merge <a
href="https://github-redirect.dependabot.com/rayon-rs/rayon/issues/705">#705</a></li>
<li><a
href="54c0b0dc0c"><code>54c0b0d</code></a>
Make sure that compat-Cargo.lock is fresh</li>
<li><a
href="4fd13b0334"><code>4fd13b0</code></a>
Regenerate compat-Cargo.lock</li>
<li>Additional commits viewable in <a
href="https://github.com/rayon-rs/rayon/compare/v1.5.3...rayon-core-v1.6.1">compare
view</a></li>
</ul>
</details>
<br />


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

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

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

---

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

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


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-22 23:52:37 -05:00
4182fc203e Bump actions-rust-lang/setup-rust-toolchain from 1.3.4 to 1.3.5 (#7840)
Bumps
[actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain)
from 1.3.4 to 1.3.5.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/CHANGELOG.md">actions-rust-lang/setup-rust-toolchain's
changelog</a>.</em></p>
<blockquote>
<h2>[1.3.5] - 2023-01-21</h2>
<h3>Changed</h3>
<ul>
<li>Use the newly stabilized setting to enable sparse registry access.
This speeds up access to the crate registry and is in addition to the
unstable nightly env var.
<a
href="https://github-redirect.dependabot.com/rust-lang/cargo/pull/11224">rust-lang/cargo#11224</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bc88fd0b3e"><code>bc88fd0</code></a>
Enable sparse registry access after stabilization</li>
<li>See full diff in <a
href="https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.3.4...v1.3.5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions-rust-lang/setup-rust-toolchain&package-manager=github_actions&previous-version=1.3.4&new-version=1.3.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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

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

---

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

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


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-22 23:51:52 -05:00
10e36c4233 Bump sysinfo from 0.26.4 to 0.27.7 (#7839) 2023-01-23 03:09:15 +00:00
bef397228f Bump miette from 5.3.0 to 5.5.0 (#7838) 2023-01-23 01:56:28 +00:00
5cf47767d7 Bump shadow-rs from 0.16.3 to 0.20.0 (#7837) 2023-01-23 01:55:51 +00:00
8f2d2535dc Bump scraper from 0.13.0 to 0.14.0 (#7835) 2023-01-23 01:54:50 +00:00
2aae8e6382 Bump regex from 1.6.0 to 1.7.1 (#7833) 2023-01-23 01:44:10 +00:00
ba12b0de0d Fix command name lookup for known externals (#7830)
Fixes https://github.com/nushell/nushell/issues/7822
2023-01-22 21:40:18 +02:00
3552d03f6c Allow main command to define top-level module command (#7764) 2023-01-22 21:34:15 +02:00
8d5165c449 Feat/7725 url join (#7823)
# Description

Added command: `url join`.
Closes: #7725 

# User-Facing Changes

```
Converts a record to url

Search terms: scheme, username, password, hostname, port, path, query, fragment

Usage:
  > url join

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

Signatures:
  <record> | url join -> <string>

Examples:
  Outputs a url representing the contents of this record
  > {
          "scheme": "http",
          "username": "",
          "password": "",
          "host": "www.pixiv.net",
          "port": "",
          "path": "/member_illust.php",
          "query": "mode=medium&illust_id=99260204",
          "fragment": "",
          "params":
          {
            "mode": "medium",
            "illust_id": "99260204"
          }
        } | url join

  Outputs a url representing the contents of this record
  > {
        "scheme": "http",
        "username": "user",
        "password": "pwd",
        "host": "www.pixiv.net",
        "port": "1234",
        "query": "test=a",
        "fragment": ""
      } | url join

  Outputs a url representing the contents of this record
  > {
        "scheme": "http",
        "host": "www.pixiv.net",
        "port": "1234",
        "path": "user",
        "fragment": "frag"
      } | url join
```                  

# Questions
- Which parameters should be required? Currenlty are: `scheme` and
`host`.
2023-01-22 19:49:40 +01:00
d8027656b5 benchmark now pipes input into the closure (#7776)
# Description

Closes #7762. See issue for motivation.

# User-Facing Changes

Something like this is now possible without having to split this into 2
commands:
```
fetch "https://www.gutenberg.org/files/11/11-0.txt" | benchmark { str downcase | split words | uniq -c | sort-by count --reverse | first 10 }
```

# Tests + Formatting

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

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

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

# After Submitting

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

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-01-22 19:18:28 +01:00
4f57c5d56e Fix multi-line redirection inside a block (#7808)
# Description

Fixes: #7786

The issue is because the lite block is wrong while converting from lex
tokens

# What happened internally?
Take the following as example:
```
❯ def foobar [] { 
    'hello' out> /tmp/output.1
    'world' out> /tmp/output.2
}
```

## Before:
```
LiteBlock { block: [
    LitePipeline { commands: [
        Command(None, LiteCommand { comments: [], parts: [Span { start: 40900, end:40907 }] }),
        Redirection(Span { start: 40908, end: 40912 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 40913, end: 40926 }] })]
    },
    LitePipeline { commands: [
        Redirection(Span { start: 40908, end: 40912 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 40929, end: 40936 }] }),   // this is wrong, should be command.
        Redirection(Span { start: 40937, end: 40941 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 40942, end: 40955 }] })]
    }] }
```

## After:
```
LiteBlock { block: [
    LitePipeline { commands: [
        Command(None, LiteCommand { comments: [], parts: [Span { start: 40824, end: 40831 }] }),
        Redirection(Span { start: 40832, end: 40836 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 40837, end: 40850 }] })] 
    },
    LitePipeline { commands: [
        Command(None, LiteCommand { comments: [], parts: [Span { start: 40854, end: 40861 }] }), 
        Redirection(Span { start: 40862, end: 40866 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 40867, end: 40880 }] })] 
    }
] }
```

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-23 06:32:56 +13:00
2c5c81815a Fix typos (#7811)
Some minor text fixes
2023-01-22 15:22:10 +01:00
b97bfe9297 [nu-test-support] Gate system locale tests (#7824)
# Description

`src/locale_override.rs` is gated with `#![cfg(debug_assertions)]` but
`tests/get_system_locale.rs` is not; this makes `cargo test --release`
fails:

```
error[E0432]: unresolved import `nu_test_support::locale_override`
 --> crates/nu-test-support/tests/get_system_locale.rs:1:22
  |
1 | use nu_test_support::locale_override::with_locale_override;
  |                      ^^^^^^^^^^^^^^^ could not find `locale_override` in `nu_test_support`

For more information about this error, try `rustc --explain E0432`.
error: could not compile `nu-test-support` due to previous error
warning: build failed, waiting for other jobs to finish...
```

With the change it now passes:

```
❯ cargo test --release
   Compiling nu-test-support v0.74.1 (/home/michel/src/github/nushell/nushell/crates/nu-test-support)
    Finished release [optimized] target(s) in 7.57s
     Running unittests src/lib.rs (/home/michel/src/github/nushell/nushell/target/release/deps/nu_test_support-750505b13c102d2c)

running 3 tests
test tests::constructs_a_pipeline ... ok
test playground::tests::current_working_directory_back_to_root_from_anywhere ... ok
test playground::tests::current_working_directory_in_sandbox_directory_created ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/get_system_locale.rs (/home/michel/src/github/nushell/nushell/target/release/deps/get_system_locale-e0ecabe312044fa8)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```

Signed-off-by: Michel Alexandre Salim <salimma@fedoraproject.org>

# User-Facing Changes

N/A

Signed-off-by: Michel Alexandre Salim <salimma@fedoraproject.org>
2023-01-21 20:05:29 -05:00
db07657e40 Reduce number of match calls (#7813) 2023-01-21 15:47:00 +02:00
2d98d0fcc2 Extract manual PWD extraction with method current_work_dir (#7812) 2023-01-21 15:44:17 +02:00
e6f6f17c6d Bump bumpalo from 3.11.0 to 3.12.0 (#7805) 2023-01-21 13:31:56 +00:00
166a927c20 Bump git2 from 0.16.0 to 0.16.1 (#7807) 2023-01-21 13:25:52 +00:00
625fe8866c Bump libgit2-sys from 0.14.1+1.5.0 to 0.14.2+1.5.1 (#7806) 2023-01-21 00:50:33 +00:00
69e7aa9fc9 nu-table: Wrap last column in table -e (#7778)
Hi  @rgwood thank you for report.

So this PR must fix it but I'd make a few runs.

close #7763

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2023-01-20 14:18:22 -08:00
a775cfe177 Clean up nu-cli/src/eval_file.rs (#7804)
- Replace `match` with `unwrap_or_else` or `if let`
- Remove unnecessary `mut`
- Check if file path has a parent
- Reduce visibility to `pub(crate)`
2023-01-20 13:45:34 -08:00
9e4a2ab824 Move all functions of main.rs into modules (#7803)
The affected modules are:
- `command.rs`
- `config_files.rs`
- `terminal.rs`
2023-01-20 13:20:38 -08:00
24aa1f312a Cleanup of src/main.rs (#7801)
While reading nushell's code, I've started to clean up `src/main.rs` a
bit.
The description of the changes can be found in the commit messages.
2023-01-20 10:44:49 -08:00
cde56741fb fetch -> http get and post -> http post (#7796)
# Updated description by @rgwood

This PR changes `fetch` to `http get` and `post` to `http post`. `fetch`
and `post` are now deprecated. [I surveyed people on
Discord](https://discord.com/channels/601130461678272522/601130461678272524/1065706282566307910)
and users strongly approved of this change.

# Original Description
This PR is related to #2741 and my first pull request in rust :)

Implemented a new http mod to better http support and alias `fetch` and
`post` commands to `http get` and `http post` respectively.

# User-Facing Changes

Users will be able to use HTTP method via http command, for example
``` shell
> http get "https://www.example.com"
<!doctype html>
<html>
...
```
2023-01-20 10:38:30 -08:00
bbe694a622 fix signature display in help commands (#7802)
# Description

This PR fixes the signature display when running `help commands`. Before
this PR, there were leading spaces in the signatures column. Now,
they're left aligned for a cleaner look.

Before:

![image](https://user-images.githubusercontent.com/343840/213711847-368fba0d-c902-47e6-b777-54de978b1ce3.png)

After:

![image](https://user-images.githubusercontent.com/343840/213711551-c5eb29c9-1d47-444b-86a1-8e14711e9771.png)


# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-20 07:58:54 -06:00
7e575a718b Do not list deprecated subcommands in help <cmd> (#7798)
# Description

BEFORE:
```
Subcommands:
  str camel-case - Convert a string to camelCase
  str capitalize - Capitalize first letter of text
  str collect - 'str collect' is deprecated. Please use 'str join' instead.
  str contains - Checks if string input contains a substring
  str distance - Compare two strings and return the edit distance/Levenshtein distance
  str downcase - Make text lowercase
  str ends-with - Check if an input ends with a string
  str find-replace - Deprecated command
  str index-of - Returns start index of first occurrence of string in input, or -1 if no match
  str join - Concatenate multiple strings into a single string, with an optional separator between each
  str kebab-case - Convert a string to kebab-case
  str length - Output the length of any strings in the pipeline
  str lpad - Left-pad a string to a specific length
  str pascal-case - Convert a string to PascalCase
  str replace - Find and replace text
  str reverse - Reverse every string in the pipeline
  str rpad - Right-pad a string to a specific length
  str screaming-snake-case - Convert a string to SCREAMING_SNAKE_CASE
  str snake-case - Convert a string to snake_case
  str starts-with - Check if an input starts with a string
  str substring - Get part of a string. Note that the start is included but the end is excluded, and that the first character of a string is index 0.
  str title-case - Convert a string to Title Case
  str to-datetime - Deprecated command
  str to-decimal - Deprecated command
  str to-int - Deprecated command
  str trim - Trim whitespace or specific character
  str upcase - Make text uppercase
```

AFTER:

```
Subcommands:
  str camel-case - Convert a string to camelCase
  str capitalize - Capitalize first letter of text
  str contains - Checks if string input contains a substring
  str distance - Compare two strings and return the edit distance/Levenshtein distance
  str downcase - Make text lowercase
  str ends-with - Check if an input ends with a string
  str index-of - Returns start index of first occurrence of string in input, or -1 if no match
  str join - Concatenate multiple strings into a single string, with an optional separator between each     
  str kebab-case - Convert a string to kebab-case
  str length - Output the length of any strings in the pipeline
  str lpad - Left-pad a string to a specific length
  str pascal-case - Convert a string to PascalCase
  str replace - Find and replace text
  str reverse - Reverse every string in the pipeline
  str rpad - Right-pad a string to a specific length
  str screaming-snake-case - Convert a string to SCREAMING_SNAKE_CASE
  str snake-case - Convert a string to snake_case
  str starts-with - Check if an input starts with a string
  str substring - Get part of a string. Note that the start is included but the end is excluded, and that the first character of a string is index 0.
  str title-case - Convert a string to Title Case
  str trim - Trim whitespace or specific character
  str upcase - Make text uppercase
```

The deprecated subcommands still exist, but are no longer listed in
`help` for the containing command.

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-20 06:28:27 -06:00
ea9ca8b4ed str length, str substring, str index-of, split words and split chars now use graphemes instead of UTF-8 bytes (#7752)
Closes https://github.com/nushell/nushell/issues/7742
2023-01-20 09:16:18 +02:00
0fe2884397 add dedicated const in pipeline, const builtin var errors (#7784)
# Description

Currently `let` and `const` share error handling, and this might lead to
confusing error messages


![sJan17-51](https://user-images.githubusercontent.com/98623181/212981108-c80b3e55-cece-4ee5-ba8f-cb5dcfdf63e0.png)

This PR adds dedicated errors to `const`
2023-01-20 00:11:48 +01:00
2982a2c963 let find take linebreaks into account in Value::String (#7789)
# Description

Fixes #7774. The functionality should be the same as feeding all
`PipelineDate::Value(Value::String(_,_),_)` into `lines` before putting
it into `find`.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-20 00:07:34 +01:00
6a43e1a64d Add test for fix of issue #7754 (#7756)
Fix already landed with #7779
2023-01-19 11:19:27 +01:00
3b5172a8fa LazyRecord (#7619)
This is an attempt to implement a new `Value::LazyRecord` variant for
performance reasons.

`LazyRecord` is like a regular `Record`, but it's possible to access
individual columns without evaluating other columns. I've implemented
`LazyRecord` for the special `$nu` variable; accessing `$nu` is
relatively slow because of all the information in `scope`, and [`$nu`
accounts for about 2/3 of Nu's startup time on
Linux](https://github.com/nushell/nushell/issues/6677#issuecomment-1364618122).

### Benchmarks

I ran some benchmarks on my desktop (Linux, 12900K) and the results are
very pleasing.

Nu's time to start up and run a command (`cargo build --release;
hyperfine 'target/release/nu -c "echo \"Hello, world!\""' --shell=none
--warmup 10`) goes from **8.8ms to 3.2ms, about 2.8x faster**.

Tests are also much faster! Running `cargo nextest` (with our very slow
`proptest` tests disabled) goes from **7.2s to 4.4s (1.6x faster)**,
because most tests involve launching a new instance of Nu.

### Design (updated)

I've added a new `LazyRecord` trait and added a `Value` variant wrapping
those trait objects, much like `CustomValue`. `LazyRecord`
implementations must implement these 2 functions:

```rust
// All column names
fn column_names(&self) -> Vec<&'static str>;

// Get 1 specific column value
fn get_column_value(&self, column: &str) -> Result<Value, ShellError>;
 ```

### Serializability

`Value` variants must implement `Serializable` and `Deserializable`, which poses some problems because I want to use unserializable things like `EngineState` in `LazyRecord`s. To work around this, I basically lie to the type system:

1. Add `#[typetag::serde(tag = "type")]` to `LazyRecord` to make it serializable
2. Any unserializable fields in `LazyRecord` implementations get marked with `#[serde(skip)]`
3. At the point where a `LazyRecord` normally would get serialized and sent to a plugin, I instead collect it into a regular `Value::Record` (which can be serialized)
2023-01-18 19:27:26 -08:00
be32aeee70 add magenta to ansi command as synonym for purple (#7785)
# Description

Added `magenta` as a synonym for Purple in the ansi command. Previously,
ansi errored out on `ansi magenta` as the `AnsiCode` vec didn't have
magenta as a name as seen in #7747.


<img width="909" alt="image"
src="https://user-images.githubusercontent.com/101823296/213012495-10bb29e3-b6d3-4fdc-bc3c-cb8a891c2bc2.png">




# User-Facing Changes

Users can now use the ANSI standard name `magenta`.
2023-01-17 19:50:55 -06:00
adcc74ab8d Check all user groups. (#7775)
Previously the group check was only for the current users gid, now we
check against all the users groups.


# Description

Currently when using the `cd` command to enter a directory there was
only a check against the current user id, and exact current group id,
meaning if a directory had a group permission and that group wasn't the
users group it was effectively ignored.

The fix simply implements a check through all the users current groups.

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-01-17 19:49:54 -06:00
8acced56b2 Fixes Issue 7648 which crashes nushell and happens when an alias name is shorter than the alias command and the alias command is an external command. (#7779) 2023-01-17 08:30:00 +02:00
f823c7cb5d fix some typos (#7773)
# Description

Nothing changed, just fix some typos

# Tests + Formatting

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

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

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

# After Submitting

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

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-01-16 12:43:46 +01:00
26e6516626 update sqlparser dependency (#7772)
# Description

This PR updates the `sqlparser` dependency and updates code to the
latest api changes.

# User-Facing Changes


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-15 21:30:39 -06:00
5979e0cd0c update semver dep (#7771)
# Description

This PR updates the semver dependency and updates the `inc` plugin to
use the latest api.

# User-Facing Changes



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-15 20:39:27 -06:00
3ba1bfc369 Bump quick-xml from 0.25.0 to 0.27.1 (#7768) 2023-01-16 02:06:31 +00:00
efa0e6eb62 Bump serial_test from 0.8.0 to 0.10.0 (#7769) 2023-01-16 02:06:03 +00:00
159b4bd7dc Bump actions/stale from 3 to 6 (#7770) 2023-01-16 02:05:35 +00:00
2611c9525e Bump dialoguer from 0.9.0 to 0.10.3 (#7765) 2023-01-16 02:04:47 +00:00
0353eb4a12 make save stream on list stream data (#7675)
# Description

Closes: #7590

# User-Facing Changes

So the following command
```
1..100 | each { |i| sleep 400ms; $i} | save --raw -f output.txt
```

Will stream data to `output.txt`

But I'm note sure how to make a proper test for it, so I leave with no
new test cases..

Also rename from `string_binary_list_value_to_bytes ` to
`value_to_bytes` to accepts more Value type.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-15 10:54:30 -08:00
92c4097f8d Rename to url command to url build-query (#7702)
# Description

Refactor command: "to url" in: "to url query". Changed usage sentence.
Closes: #7495

# User-Facing Changes

Now we get a query string from a record or table by using command: "to
url query".

```
> help to url query
Convert record or table into query string applying percent-encoding.

Usage:
  > to url query

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

Signatures:
  <record> | to url query -> <string>
  <table> | to url query -> <string>

Examples:
  Outputs a query string representing the contents of this record
  > { mode:normal userid:31415 } | to url query

  Outputs a query string representing the contents of this 1-row table
  > [[foo bar]; ["1" "2"]] | to url query

  Outputs a query string representing the contents of this record
  > {a:"AT&T", b: "AT T"} | to url query
```

# Tests + Formatting

Added this test:
```
Example {
    description: "Outputs a query string representing the contents of this record",
    example: r#"{a:"AT&T", b: "AT T"} | to url query"#,
    result: Some(Value::test_string("a=AT%26T&b=AT+T")),
},
```
to ensure percent-encoding. 

# After Submitting

If PR is accepted I'll open another PR on documentation to notify
changes on
[this.](https://github.com/nushell/nushell.github.io/blob/main/book/commands/to_url.md)
2023-01-15 10:16:29 -08:00
a909c60f05 Ansi link (#7751) 2023-01-15 17:23:37 +02:00
56a9eab7eb Allow underscores in integers and floats (#7759)
# Description

This PR makes changes that allow underscores in numbers.

Example:
```nu
# allows underscores to be placed arbitrarily to enhance readability.
let pi = 3.1415_9265_3589_793

# works with integers
let num = 1_000_000_000_000
let fav_color = 0x68_9d_6a
```
2023-01-15 09:03:57 -06:00
7221eb7f39 Fix typos and use more idiomatic assertions (#7755)
I have changed `assert!(a == b)` calls to `assert_eq!(a, b)`, which give
better error messages. Similarly for `assert!(a != b)` and
`assert_ne!(a, b)`. Basically all instances were comparing primitives
(string slices or integers), so there is no loss of generality from
special-case macros,

I have also fixed a number of typos in comments, variable names, and a
few user-facing messages.
2023-01-15 15:03:32 +13:00
b0b0482d71 Add cursor shape configuration for each edit mode (#7745)
# Description

This PR allows the configuration of cursor shapes in nushell for each
edit mode. This is the change that is in the default_config.nu file.
```
  cursor_shape: {
    emacs: line # block, underscore, line (line is the default)
    vi_insert: block # block, underscore, line (block is the default)
    vi_normal: underscore # block, underscore, line  (underscore is the default)
  }
```

# User-Facing Changes

See above. If you'd prefer a different default, please speak up and let
us know.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-13 14:37:39 -06:00
49ab559992 Trim quotes when shelling out to cmd.exe (#7740)
Closes #6337 and #5366. Prior to this PR, when "shelling out" to cmd.exe
on Windows we were not trimming quotes correctly:

```bash
〉^echo "foo"
\"foo\"
```
After this change, we do:
```bash
〉^echo "foo"
foo
```

### Breaking Change

I ended up removing `dir` from the list of supported cmd.exe internal
commands as part of this PR.

For this PR, I extracted the argument-cleaning-and-expanding code from
`spawn_simple_command()` for reuse in `spawn_cmd_command()`. This means
that we now expand globs, which broke some tests for the `dir` cmd.exe
internal command.

I probably could have kept the tests working, but... tbh, I don't think
it's worth it. I don't want to make the `cmd.exe` functionality any more
complicated than it already is, and calling `dir` from Nu is always
going to be weird+hacky compared to `ls`.
2023-01-13 11:00:30 -08:00
3dd21c635a dependency update: update polar to 0.26.1 (#7743)
# Description

As title

# User-Facing Changes

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-13 09:27:37 -06:00
835bbb2e44 update base64 implementation to newer crate (#7739)
# Description

This PR updates the base64 crate, which has changed significantly, so
all the base64 implementations had to be changed too. Tests pass. I hope
that's enough.

# User-Facing Changes

None, except added a new character encoding imap-mutf7 as mutf7.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-13 07:16:14 -06:00
2ee2370a71 Detailed message during core dumped (#7737)
# Description

In bash when a program crashes, it prints the reason for what happened:
```
$ ./division_by_zero
Floating point exception (core dumped)
$ ./segfault
Segmentation fault (core dumped)
```

Nushell always prints the same thing in this case:
```
> ./division_by_zero
nushell: oops, process './division_by_zero' core dumped
Error: nu:🐚:external_command (link)
# etc..
```

This PR adds more detailed error printing, like in bash:
```
> ./division_by_zero
Floating point exception: oops, process './division_by_zero' core dumped
Error: nu:🐚:external_command (link)
# etc..
```

I made this message format as an example:
```
Floating point exception: oops, process './division_by_zero' core dumped
```
Instead of `nushell:` it writes a meaningful message, but I can change
this format as per the suggestions.

I tested the change only on linux, but it should work on other unix
systems.

# User-Facing Changes

The error message only.

# Tests + Formatting

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

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

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

# After Submitting

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

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-01-13 10:22:11 +01:00
54dd65cfe1 Disallow encode's silent conversion to HTML entities (and add -i/--ignore-errors flag to re-allow it) (#7738)
# Description

Closes #7514.

* For both `encode` and `decode`: add a special case allowing `utf16` as
a valid alias for `utf-16` (just as `utf-8` has `utf8`).
* For `encode` , make it an error when encodings_rs replaces characters
outside the given encoding with HTML entities
* For `encode` , add `-i`/`--ignore-errors` flag to bring back this
behaviour.

Note: `--ignore-errors` does NOT ignore the error for using a wrong
encoding label like `uft8`

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

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

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-01-12 15:00:17 -06:00
b004aacd69 Add search terms in random and expression categories (#7736)
# Description

Refers to: [5093](https://github.com/nushell/nushell/issues/5093)

# Tests

- [x] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- [x]  `cargo test --workspace` to check that all tests pass
2023-01-12 14:01:40 +01:00
48b7b415e2 spanned error on path exists command (#7717)
# Description

Closes: #7696 

# User-Facing Changes

Before:
```
❯ 'temp/aa' | path exists
Error: nu:🐚:io_error (link)

  × I/O error
  help: Os { code: 13, kind: PermissionDenied, message: "Permission denied" }
```

After:
```
❯ 'temp/aa' | path exists
Error: nu:🐚:io_error (link)

  × I/O error
   ╭─[entry #42:1:1]
 1 │ 'temp/aa' | path exists
   · ────┬────
   ·     ╰── Permission denied (os error 13)
   ╰────
```
# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 06:56:39 -06:00
544cea95e1 Bump uuid from 1.1.2 to 1.2.2 (#7734) 2023-01-12 12:48:49 +00:00
8aa2632661 Support redirect err and out to different streams (#7685)
# Description

Closes: #7364 

# User-Facing Changes

Given the following shell script:
```bash
x=$(printf '=%.0s' {1..100})
echo $x
echo $x 1>&2
```

It supports the following command:
```
bash test.sh out> out.txt err> err.txt
```

Then both `out.txt` and `err.txt` will contain `=`(100 times)

## About the change

The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SeparateRedirection` if we meet two Redirection
token(which is generated by `lex` function),
During converting from lite block to block,
`LiteElement::SeparateRedirection` will be converted to
`PipelineElement::SeparateRedirection`.

Then in the block eval process, if we get
`PipelineElement::SeparateRedirection`, we invoke `save` command with
`--stderr` arguments to acthive our behavior.



## What happened internally?
Take the following command as example:
```
^ls out> out.txt err> err.txt
```

lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.

### LiteBlock before
```rust
LiteBlock {
    block: [
        LitePipeline { commands: [
            Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
            // actually the span of first Redirection is wrong too..
            Redirection(Span { start: 39058, end: 39062 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
            Redirection(Span { start: 39058, end: 39062 }, Stderr, LiteCommand { comments: [], parts: [Span { start: 39063, end: 39070 }] })
        ]
    }]
}
```
### LiteBlock after
```rust
LiteBlock {
    block: [
        LitePipeline { commands: [
            Command(
                None, 
                LiteCommand { comments: [], parts: [Span { start: 38525, end: 38528 }] }),
                // new one! two Redirection merged into one SeparateRedirection.
                SeparateRedirection { 
                    out: (Span { start: 38529, end: 38533 }, LiteCommand { comments: [], parts: [Span { start: 38534, end: 38541 }] }),
                    err: (Span { start: 38542, end: 38546 }, LiteCommand { comments: [], parts: [Span { start: 38547, end: 38554 }] })
                }
        ]
    }]
}
```

### Block before
```rust
Pipeline {
    elements: [
        Expression(None, Expression {
            expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
            span: Span { start: 39041, end: 39044 },
            ty: Any, custom_completion: None 
        }),
        Redirection(Span { start: 39058, end: 39062 }, Stdout, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None }),
        Redirection(Span { start: 39058, end: 39062 }, Stderr, Expression { expr: String("err.txt"), span: Span { start: 39063, end: 39070 }, ty: String, custom_completion: None })] }
```

### Block after
```rust
Pipeline {
    elements: [
        Expression(None, Expression {
            expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 38526, end: 38528 }, ty: String, custom_completion: None }, [], false),
            span: Span { start: 38525, end: 38528 },
            ty: Any,
            custom_completion: None 
        }),
        // new one! SeparateRedirection
        SeparateRedirection {
            out: (Span { start: 38529, end: 38533 }, Expression { expr: String("out.txt"), span: Span { start: 38534, end: 38541 }, ty: String, custom_completion: None }),
            err: (Span { start: 38542, end: 38546 }, Expression { expr: String("err.txt"), span: Span { start: 38547, end: 38554 }, ty: String, custom_completion: None }) 
        }
    ]
}
```
# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 10:22:30 +01:00
5419e8ae9d don't expand tilde if we quote external arguments (#7711)
# Description

As title
Fixes: #7673
Fixes: #4205
Also possiblely fixes: https://github.com/nushell/nushell/issues/6993

# User-Facing Changes

Before:
```
> ^echo "~"
/Users/ttt
```

After:
```
> ^echo "~"
~
```
# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-11 19:14:19 -05:00
d4d28ab796 Bump once_cell from 1.16.0 to 1.17.0 (#7732) 2023-01-11 23:00:44 +00:00
b8db928c58 Bump git2 from 0.15.0 to 0.16.0 (#7731) 2023-01-11 22:56:02 +00:00
bf45a5860e experiment with dependabot and rust dependencies (#7716)
# Description

The PR is an experiment to see if we want to use dependabot to notify us
and automatically update rust dependencies.

# User-Facing Changes



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-11 16:30:34 -06:00
ca543fc8af update release-pkg comments for manual runs (#7726)
# Description

After running this script manually again, I found more comments that
needed to be added.

# User-Facing Changes

N/A

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-11 16:28:30 -06:00
57cf805e12 Add const support for all overlay commands (#7720) 2023-01-12 00:18:06 +02:00
1ae9157985 Bump to 0.74.1 development version (#7721)
# Description

May be used for hotfix if needed
2023-01-11 22:30:41 +01:00
206a6ae6c9 Fix generated doc for explore commands (#7723)
# Description

Fix generated doc for `explore` commands, and resolve the static site
build error:
https://github.com/nushell/nushell.github.io/actions/runs/3889029668/jobs/6636921318

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-11 11:17:12 +08:00
82ac590412 Progress bar Implementation (#7661)
# Description

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

I implemented the status bar we talk about yesterday. The idea was
inspired by the progress bar of `wget`.
I decided to go for the second suggestion by `@Reilly`
> 2. add an Option<usize> or whatever to RawStream (and ListStream?) for
situations where you do know the length ahead of time

For now only works with the command `save` but after the approve of this
PR we can see how we can implement it on commands like `cp` and `mv`

When using `fetch` nushell will check if there is any `content-length`
attribute in the request header. If so, then `fetch` will send it
through the new `Option` variable in the `RawStream` to the `save`.
If we know the total size we show the progress bar 

![nu_pb01](https://user-images.githubusercontent.com/38369407/210298647-07ee55ea-e751-41b1-a84d-f72ec1f6e9e5.jpg)
but if we don't then we just show the stats like: data already saved,
bytes per second, and time lapse.

![nu_pb02](https://user-images.githubusercontent.com/38369407/210298698-1ef65f51-40cc-4481-83de-309cbd1049cb.jpg)

![nu_pb03](https://user-images.githubusercontent.com/38369407/210298701-eef2ef13-9206-4a98-8202-e4fe5531d79d.jpg)

Please let me know If I need to make any changes and I will be happy to
do it.

# User-Facing Changes

A new flag (`--progress` `-p`) was added to the `save` command 
Examples:
```nu
fetch https://github.com/torvalds/linux/archive/refs/heads/master.zip | save --progress -f main.zip
fetch https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso | save --progress -f main.zip
open main.zip --raw | save --progress main.copy
```

# 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
-
I am getting some errors and its weird because the errors are showing up
in files i haven't touch. Is this normal?

# After Submitting

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

Co-authored-by: Reilly Wood <reilly.wood@icloud.com>
2023-01-10 20:57:48 -05:00
9a274128ce Combine benchmarks to speed up cargo bench build times (#7722)
I've been using the new Criterion benchmarks and I noticed that they
take a _long_ time to build before the benchmark can run. Turns out
`cargo build` was building 3 separate benchmarking binaries with most of
Nu's functionality in each one.

As a simple temporary fix, I've moved all the benchmarks into a single
file so that we only build 1 binary.

### Future work

Would be nice to split the unrelated benchmarks out into modules, but
when I did that a separate binary still got built for each one. I
suspect Criterion's macros are doing something funny with module or file
names. I've left a FIXME in the code to investigate this further.
2023-01-10 17:51:25 -08:00
5664ee7bda Remove engine_state clones in REPL eval (#7713)
A small but easy optimization for `evaluate_repl()`: clone
`engine_state` 1x instead of 3x.

This reduces time spent in a simple REPL eval (`enter` key pressed with
no command text) by about 10%, as measured in
[Superluminal](https://superluminal.eu/).
2023-01-10 17:22:32 -08:00
9a56665c6b Commit the lockfile for 0.74 (#7719)
Was missed in #7718
2023-01-10 21:12:41 +01:00
8044fb2db0 Bump version to 0.74.0 (#7718)
Preparing the release
2023-01-10 20:58:13 +01:00
3a59ab9f14 Improve wording of str replace help messages (#7708)
# Description

See title.

# User-Facing Changes

See title.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-10 20:46:50 +01:00
f609a4f26a Auto-Completion: put ` tildes around filenames with parentheses (#7712)
# Description

Fixes: #7706

# User-Facing Changes


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


# Tests + Formatting

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

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

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

# After Submitting

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

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

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

Currently the implementation is different for Windows and Unix.

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

example failing under windows

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

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

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

cc @WindSoilder 


# User-Facing Changes

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

# Tests + Formatting

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

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

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

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

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

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

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

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

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


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

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

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

---

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

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

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

</details>

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

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

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

After this PR

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


# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-06 16:36:59 -06:00
a9bdc655c1 Add benchmarks for evaluating default env+config (#7688)
A quick follow-up to https://github.com/nushell/nushell/pull/7686. This
adds benchmarks for evaluating `default_env.nu` and `default_config.nu`,
because evaluating config takes up the lion's share of Nushell's startup
time. The benchmarks will help us speed up Nu's startup and test
execution.

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

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

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

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

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

## Context

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

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-04 19:57:56 -08:00
00469de93e Limit recursion to avoid stack overflow (#7657)
Add recursion limit to `def` and `block`.
Summary of this PR , it will detect if `def` call itself or not .
Then execute by using `stack` which I think best choice to use with this
design and core as it is available in all crates and mutable and
calculate the recursion limit on calling `def`.
Set 50 as recursion limit on `Config`.
Add some tests too .

Fixes #5899

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

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

Closes #7073

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

User-Facing Changes:

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

Tests:

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

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

This improves performance on large data sets.

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


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

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

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

# User-Facing Changes



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-04 11:22:54 -06:00
c5639cd9fa Primitives now use color closures when printed on the command line (#7650)
# Description

Closes #7554


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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

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

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

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

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

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

Fixed a minor scripting error 😀

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


# User-Facing Changes

No

# Tests + Formatting

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

### Before

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

### After

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

The new ordering:

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

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

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

Search terms: network, parse

Usage:
  > url 

Subcommands:
  url parse - Parses a url

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

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

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

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

Search terms: network, parse

Usage:
  > url 

Subcommands:
  url parse - Parses a url

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-03 10:47:37 -08:00
9e1f645428 Fix save error handling (#7608)
Closes #5178

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-02 17:09:55 -08:00
65d0b5b9d9 Make get hole errors and cell path hole errors identical (improvement on #7002) (#7647)
# Description

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

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

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

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

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

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-02 14:45:43 -08:00
614bc2a943 early return for parsing closure and block with interchanged shape (#7618) 2023-01-01 12:26:51 +02:00
27b06358ea Tweak new input type error message (#7646)
A tiny follow-up from https://github.com/nushell/nushell/pull/7623,
changes "Only supports for specific input types" to "Input type not
supported"

Before:

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

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

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

Closes #7273.

Also slightly edits/tidies up parser.rs.

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-27 15:00:44 -08:00
db06edc5d3 add --mime-type(-m) to ls in the type column (#7616)
# Description

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

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

# User-Facing Changes

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

Part of issue #7612 and and #7524

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

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-27 12:46:23 -06:00
568927349d Fix and Allow Number and Boolean type to be key in from yaml (#7607)
Fix and Allow Number and Boolean type to be key in Yaml .

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

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

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

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

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

Could you verify if it got fixed?

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

Fix const examples

# Tests + Formatting

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
2022-12-27 15:44:03 +08:00
b4c5693ac6 Fix an example of env command (#7603)
# Description

Fix an example of `env` command

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-26 16:40:34 +08:00
79000aa5e0 Fix typos by codespell (#7600)
# Description

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

# User-Facing Changes

None.

# Tests + Formatting

None and done.

# After Submitting

None.
2022-12-26 02:31:26 -05:00
2415381682 fix python plugin example (#7599)
# Description

_(Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.)_

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-24 18:31:44 -06:00
b499e7c682 Fix some issues with table wrapping of lists (#7598)
close #7591 

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

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

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

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


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

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


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

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


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

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


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

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

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


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


# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

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

Inspired by #7592

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

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

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

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


# User-Facing Changes

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

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-24 07:16:29 -06:00
e76b38882c columns now errors when given a non-record non-table (#7593)
# Description

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

# User-Facing Changes

See title.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-24 07:08:25 -06:00
11bdab7e61 Change instances of Value::string("foo", Span::test_data()) to Value::test_string("foo") (#7592) 2022-12-24 10:25:38 +01:00
3d682fe957 Fix error message when interrupting table with ctrl+c (#7588)
`table` was displaying an incorrect "Couldn't fit table into X columns!"
error when streaming was interrupted by `ctrl+c`:

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

This PR fixes that:

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

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

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

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

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

This PR allows `wrap` to pass through metadata.

# User-Facing Changes

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

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

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-23 15:56:28 -06:00
5041a4ffa3 Re-enable test_bits (#7585)
The tests in `src/tests/test_bits.rs` weren't being run because we were
missing a `mod test_bits;`. Fixed.
2022-12-23 11:19:10 -08:00
b16b3c0b7f Add values command (see #7166) (#7583) 2022-12-23 12:49:19 -06:00
852ec3f9a0 Fix signatures of commands which accept records also (#7582)
# Description

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

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

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-23 07:38:37 -06:00
dd7b7311b3 Standardise the use of ShellError::UnsupportedInput and ShellError::TypeMismatch and add spans to every instance of the former (#7217)
# Description

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

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

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

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

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

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

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

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

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

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-23 01:48:53 -05:00
9364bad625 Make to text stream ListStreams (#7577)
This PR changes `to text` so that when given a `ListStream`, it streams
the incoming values instead of collecting them all first.

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-22 12:31:34 -08:00
8e1112c1dd Change other instances of $nothing to null (#7569)
# Description

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-22 12:30:10 -08:00
9d1cb1bfaf Make $in work in catch closures (#7458)
# Description

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-22 09:35:41 -06:00
216d7d035f Add cross-rs config (#7559)
Cross-compiling Nu can be a little tricky due to dependencies. This PR
makes it easy to use [`cross-rs`](https://github.com/cross-rs/cross), a
popular tool for cross-compiling Rust code using Docker:
```bash
cross build --target aarch64-unknown-linux-musl --release
```

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

Fixes #7529.

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-22 08:50:59 -06:00
5b616770df Make config.filesize_format/config.filesize_metric conflict resolution consistent (#7410)
# Description

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

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

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

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

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

Fixes #5996 

Just found a relative easy way to fix the issue

# User-Facing Changes

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

Error:
  × Error when loading


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

Error:
  × error when loading
```

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-21 16:42:23 -08:00
ec08e4bc6d Fix && quotation in to nuon after proptest fail (#7564)
`proptest` caught a failing test condition for `&&` as a literal string. It requires a quotation to be parsed correctly by current `from nuon`
    
https://github.com/nushell/nushell/actions/runs/3753242377/jobs/6376308675

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

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

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

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

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

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

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

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

Closes https://github.com/nushell/nushell/issues/7553
2022-12-21 21:51:30 +01:00
4e1f94026c Add more input/output type annotations (#7532) 2022-12-21 20:20:46 +01:00
d27263af97 Bump to new development version 0.73.1 (#7544) 2022-12-21 12:35:50 -06:00
215f1af1da fix the wix file to overwrite with save -f (#7545) 2022-12-21 12:34:49 -06:00
JT
1291b647ae bump to 0.73 (#7542)
# Description

_(Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.)_

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-20 21:58:48 +13:00
91df6c236f Remove unused deps or move to devdeps (#7537)
# Description

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

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

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

This turns off `cd` abbreviations by default

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-19 13:32:24 -08:00
b01f50bd70 nu-explore/ Fix configuration issues (#7520)
close #7518

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

Signature for the following commands:

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

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

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

Prevents numeric underflow in span.

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

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

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

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

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

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

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

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

cc @fdncred 
ref #7446

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

1. Rewrote the help text a bit.
    1. I think it's important to mention `:try` up front.
2. Removed the info about `:help foo` because it's currently supported
by very few subcommands
2. Make `exit_esc` default to true. I want to avoid people getting stuck
in `explore` like they get stuck in Vim
3. ~~Always show the help message ("For help type :help") on startup~~
1. The message is small+unobtrusive and I don't this is worth a
configuration item
4. Exit the information view when Escape is pressed
5. General typo+grammar cleanup
    
cc: @zhiburt @fdncred
2022-12-17 12:05:41 -08:00
c3c41a61b0 replace lazy_static with once_cell (#7502)
replacing the dependence on `lazy_static` with `once_cell`, this will
ensure that variables are initialized when needed instead of startup
time.
2022-12-17 10:30:04 -08:00
90849a067f Fix encode base64 type signature and examples (#7515)
# Description

See title.

# User-Facing Changes

See title.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-17 11:57:16 -06:00
705f12c1d9 Fix cell path when getting columns of non-records (#7508)
A follow-up to #7497. That change made it so that `get foo` would
eliminate non-record rows; I think that was an unintentional and
undesirable side-effect.

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

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

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

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

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

# User-Facing Changes

More colors.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-17 11:10:14 -06:00
774769a7ad color_config now accepts closures as color values (#7141)
# Description

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

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

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

Slightly important notes:

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-17 07:07:56 -06:00
e72cecf457 Remove unnecessary echo uses from examples (#7500)
`echo` tends to confuse new Nu users; they expect it to work like
`print` when it just passes a value to the next stage of the pipeline.

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

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

Fixes #7494.

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

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

```

# User-Facing Changes

cell paths no longer drill into nested tables.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-15 22:03:38 -08:00
JT
fdd92b2dda Update README.md 2022-12-16 18:50:37 +13:00
8c70189422 (nu_plugin_python): Send back the signature required fields. (#7489)
# Description

The `Signature` data structure has changed. We need to add the required
fields for the nu plugin in python to work well when registering it.

# Tests + Formatting

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
2022-12-15 14:37:12 -06:00
075c83b3a1 add new plugins to script (#7493)
# Description

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

# User-Facing Changes



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-15 14:36:14 -06:00
d3a19c5ac7 (register-plugins.nu): Filter out files ending with .d on systems other than windows. (#7492) 2022-12-15 14:00:48 -06:00
080874df10 Fix #7486 (#7487)
close #7486

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

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

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

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

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

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

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

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

# Tests + Formatting

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

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

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

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

# User-Facing Changes

NaN

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-14 10:25:32 -08:00
db3177a5aa break for, loop, while execution when external command runs to failed (#7475)
Fixes: #7467

# User-Facing Changes

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

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

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

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

# User-Facing Changes


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-14 07:11:17 -06:00
52278f8562 Help messages: edit various instances of "block" to "closure" (#7470) 2022-12-14 14:02:41 +01:00
c19d9597fd Add riscv64 binary release target (#7469)
# Description

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-14 23:09:39 +13:00
d9d9916ccc Fix streaming page missing newline (#7466)
Fixes #7342. `0..1000 | table` before this change:


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

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


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

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

## The Fix

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

## Tests

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

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


# Description

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

# User-Facing Changes

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


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

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

# User-Facing Changes


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-13 10:46:22 -06:00
0c4d4632ef let UnknownFlag error list out available flags (#7443)
# Description
Fixes #6773.

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

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

# User-Facing Changes

Different error for unknown flag.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-13 06:45:33 -06:00
e2c1216c1b Fix du error message (#7460)
BEFORE:
```
〉du *.***
Error:
  × wildcards are either regular `*` or recursive `**`
   ╭─[entry #6:1:1]
 1 │ du *.***
   · ─┬
   ·  ╰── glob error
   ╰────
```
AFTER:
```
〉du *.***
Error:
  × glob error
   ╭─[entry #8:1:1]
 1 │ du *.***
   ·    ──┬──
   ·      ╰── wildcards are either regular `*` or recursive `**`
   ╰────

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

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


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-13 06:10:55 -06:00
JT
0242b30027 Revert "Add pipeline operators to help" (#7454)
Reverts nushell/nushell#7449
2022-12-13 16:49:00 +13:00
JT
0c656fd276 Revert "Pipeline operators: && and ||" (#7452)
Reverts nushell/nushell#7448

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

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

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

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

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

## Future work

This does _not_ fix https://github.com/nushell/nushell/issues/7342.
2022-12-12 15:23:04 -08:00
JT
5b5f1d1b92 Add pipeline operators to help (#7449)
# Description

_(Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.)_

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-13 11:31:03 +13:00
JT
35bea5e044 Pipeline operators: && and || (#7448)
# Description

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-13 09:53:46 +13:00
7917cf9f00 Add config mutation tests (#7437)
# Description

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-12 12:46:25 -06:00
5036672a58 add interact-once switch to rm (#7432)
# Description

Fixes: #7216 

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-11 13:22:27 -08:00
585ab56ea4 in for, loop, while, auto print final value in each iteration (#7433)
# Description

Fixes: #7404 
Fixes: #7402 

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

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

# User-Facing Changes

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

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

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-12 05:46:03 +13:00
9009f68e09 Tweak "Cannot convert {x} to a string argument" error in run_external (#7434)
# Description

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

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

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

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

```
# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-11 08:39:51 -08:00
2bacc29d30 Replace row conditions with closures in commands (#7428)
# Description

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

# User-Facing Changes

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

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 19:24:06 +02:00
4d7d97e0e6 Simplify FILE_PWD setting in 'overlay use' (#7425)
# Description

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

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

# User-Facing Changes

None

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 19:23:55 +02:00
f1000a17b4 Add FILE_PWD environment variable when running 'nu script.nu' (#7424)
# Description

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

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

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

```

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 19:23:44 +02:00
f43edbccdc Make env-related tests more resilient (#7423)
# Description

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

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

# User-Facing Changes

None

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 19:23:34 +02:00
6b4282eadf Move 'where' to parser keywords; Add 'filter' command (#7365)
# Description

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

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 19:23:24 +02:00
7e2781a2af fix docker build (#7422)
# Description

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

Fixes: #7389 

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

python:
```python
In [6]: "\nasdf\nghi\n".split("\n")
Out[6]: ['', 'asdf', 'ghi', '']
```
rust:
```rust
fn main() {
    let x = "\nabc\ndef\n";
    let y = x.split("\n").collect::<Vec<&str>>();
    println!("{:?}", y);   // outputs: ["", "abc", "def", ""]
}
```
# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 09:21:53 -06:00
ce78817f41 $env.config now always holds a record with only valid values (#7309)
# Description
Closes #7059. Rather than generate a new Record each time $env.config is
accessed (as described in that issue), instead `$env.config = ` now A)
parses the input record, then B) un-parses it into a clean Record with
only the valid values, and stores that as an env-var. The reasoning for
this is that I believe `config_to_nu_record()` (the method that performs
step B) will be useful in later PRs. (See below)

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

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

# Example of new behaviour

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

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

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

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

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

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

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

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

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

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


```

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 15:34:46 +02:00
f0e93c2fa9 into cellpath command (#7417)
# Description

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

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

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

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-10 15:26:42 +02:00
fa6bb147ea remove example missed from an earlier refactor (#7419)
# Description

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

# User-Facing Changes


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 20:04:14 -06:00
220b105efb Reduced LOC by replacing several instances of Value::Int {}, Value::Float{}, Value::Bool {}, and Value::String {} with Value::int(), Value::float(), Value::boolean() and Value::string() (#7412)
# Description

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

# User-Facing Changes

None.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 11:37:51 -05:00
b56ad92e25 ++= appendAssign operator (#7346) (#7354)
# Description

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



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

Fixes #7407. 

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 15:48:12 +02:00
c01d44e37d Add arbitrary base math log (#7409)
Adds new command `math log` that takes as a required positional argument
a base.

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


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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 22:27:50 +13:00
b4529a20e8 Add math tau (#7408)
# Description

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

# User-Facing Changes

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

# Tests + Formatting

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

# Tests + Formatting

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

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

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

# User-Facing Changes

Updated default config

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-08 11:07:40 +02:00
JT
b7572f107f Remove and/or from 'help operators' (#7388)
# Description

_(Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.)_

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-07 18:05:51 -06:00
JT
379e3d70ca Better errors when bash-like operators are used (#7241)
# Description

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

fixes #7237 

Examples:

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

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

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

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

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

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

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

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


# User-Facing Changes

**BREAKING CHANGES**

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-08 12:02:11 +13:00
JT
6fc87fad72 Fix input redirect for externals (#7387)
# Description

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-08 11:33:42 +13:00
JT
fa15a2856a Add OneOf shape to fix else (#7385)
# Description

fixes #7384 

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-08 08:58:54 +13:00
JT
eaec480f42 Improve empty pipelines (#7383)
# Description

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

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

# User-Facing Changes

None

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-08 07:31:57 +13:00
d18587330a fix semicolon doesn't work for some commands (#7373)
# Description

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

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

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

# User-Facing Changes

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-06 19:48:39 -08:00
5114dfca7d Remove use of deprecated actions-rs/cargo GH action (#7375)
Our CI actions have a lot of warnings like this:

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

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

Title.

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-06 18:48:03 -08:00
df66d9fcdf Fix watch for block+closure split (#7374)
The block+closure split broke `watch` for some use cases. Fixed.

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

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

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

Add tests too

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-06 15:26:27 -05:00
9b41f9ecb8 Allow $env and mutable records to be mutated by = (closes #7110) (#7318)
# Description

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-06 19:51:55 +02:00
48ade4993d remove redundant code mentioning ToCsv (#7370)
# Description

```rust
ToCsv
```

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

_(Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.)_

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-06 09:42:12 -08:00
4ecc807dbb fix split list when separater is the first element of list (#7355)
# Description

Fixes: #7278

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-06 08:51:38 -06:00
86b69cc5d1 add input_output_types() to ansi gradient (#7357)
# Description

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

# User-Facing Changes


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-06 08:51:18 -06:00
017a13fa3f bump to dev build v0.72.2 (#7360)
# Description

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

# User-Facing Changes



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-05 14:18:06 -06:00
ca12b2e30e Pin CI jobs to Ubuntu 20.04 (#7359)
The release job was pinned to Ubuntu 20.04 to avoid glibc breakage in
https://github.com/nushell/nushell/pull/7290. This PR updates the CI
jobs to keep things consistent (it would sure be unpleasant if things
worked in CI but not at release time).
2022-12-05 10:12:36 -08:00
db6c804b17 fix menus in default config (#7352)
Simply fixes menus commands_with_description, commands_menu
2022-12-04 19:23:58 -08:00
57ff668d2e Make SQLite queries cancellable (#7351)
This change makes SQLite queries (`open foo.db`, `open foo.db | query db
"select ..."`) cancellable using `ctrl+c`. Previously they were not
cancellable, which made it unpleasant to accidentally open a very large
database or run an unexpectedly slow query!

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


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

## Notes

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

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

# User-Facing Changes

N/A

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-04 16:39:54 -08:00
41178dff90 Try to fix #7338 (#7343)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-04 17:47:46 -06:00
12deff5d1b Add comments for nu syntax shape (#7349)
# Description

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


# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-04 11:20:47 -08:00
21a645b1a9 Improve error message for illegal filenames on Windows (#7348)
`ls` can fail when a directory contains a file that violates [the
Windows file naming
conventions](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions).
This PR tweaks the error message so we tell the user _which_ file caused
the problem.

Closes #7345.

### Before:

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

### After:


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


## Future Work

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

### Before

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

### After

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

## Rationale

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

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

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

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

## Future Work

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

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

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-03 13:08:51 -06:00
850ecf648a Protocol: debug_assert!() Span to reflect a valid slice (#6806)
Also enforce this by #[non_exhaustive] span such that going forward we
cannot, in debug builds (1), construct invalid spans.

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

My hope is this will mitigate such senarios

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

# Description

(description of your pull request here)

# Tests

Make sure you've done the following:

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

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

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

# Documentation

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

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

Closes:  #7208

After this pr, histogram will output by count descending

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-02 09:29:19 -08:00
380c216d77 Fix documentation for merge (#7329)
# Description

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

### Before:

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

### After:


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

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

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

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

# User-Facing Changes

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-02 08:13:19 -06:00
5762489070 Edited help text and examples in explore for readability (#7324)
# Description

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-02 08:01:02 -06:00
fcdc474731 uniq-by command (#7295)
New command `uniq-by` to get uniq results by column.

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-02 01:42:18 -05:00
94c89eb623 Use setup-rust-toolchain for release workflow (#7315)
# Description

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

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

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

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

# User-Facing Changes

`where -b` is not broken

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-01 15:11:03 -08:00
JT
0621ab6652 couple minor updates to xml deps (#7311)
# Description

Just some minor updates to xml deps

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-02 08:25:13 +13:00
64a028cc76 Deliver a few fixes for explore command (#7310)
ref #6984

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

Fixes: #7298

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

# User-Facing Changes

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

After this pr, it will output "fail"

# Tests + Formatting

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

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

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

# After Submitting

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

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

#### example

```nu
ls | explore
```

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Work towards #7073

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

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

# User-Facing Changes

See above.

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-01 07:11:30 -06:00
11977759ce fix cal input_output_types signature (#7306)
# Description

This is one of many commands that needs the `input_output_types()` part
of the signature filled in so that `$nu.scope.commands` shows the
signature properly which leads to the documentation being updated
properly.

TIL that when commands like `cal` don't have Example tests that can
easily be expressed and require the use of `None` results that we need
to use this as part of the signature.

```rust
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
```

Related to https://github.com/nushell/nushell/issues/7287

# User-Facing Changes



# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-01 07:10:49 -06:00
bc3dc98b34 add -f, --force for save command (#7262)
# Description

Closes: #6920 

# User-Facing Changes

```
❯ "asdf" | save dump.rdb
Error:
  × Destination file already exists
   ╭─[entry #21:1:1]
 1 │ "asdf" | save dump.rdb
   ·               ────┬───
   ·                   ╰── Destination file '/tmp/dump.rdb' already exists
   ╰────
  help: you can use -f, --force to force overwriting the destination
```
# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-01 06:26:17 -06:00
6fadc72553 Remove unused dev-dependencies (#7285)
Warning: the `nu-json` crate seems to be undertested as the main test
code is commented out. Thus the unused dependencies there were just
commented out
2022-12-01 11:53:24 +01:00
a9e6b1ec6b Add did-you-mean suggestions for bitwise ops (#7252)
Continues work from #7251 to include the bitwise operations.
Covers the C style conventions as well as the typo `bits-*` instead of
`bit-*`
2022-12-01 11:34:41 +01:00
cbc7b94b02 Remove inactive actions-rs/toolchain@v1.0.6 for release workflow (#7302)
# Description

Remove inactive actions-rs/toolchain@v1.0.6 for release workflow,
https://github.com/actions-rs/toolchain is inactive for more than two
years, and have lots of unfixed warnings:
https://github.com/actions-rs/toolchain/issues?q=is%3Aissue+is%3Aopen+warning

After this PR:

Workflow running result:
https://github.com/hustcer/nu-release/actions/runs/3590194180
Release Test: https://github.com/hustcer/nu-release/releases/tag/v0.72.7
2022-12-01 16:30:25 +08:00
45c66e2090 Update release script to nu v0.71 and use ubuntu-20.04 to build nu binary (#7290)
# Description

1. Update nu to v0.71 for release script
2. Remove the usage of `set-output` see:
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
3. Use `ubuntu-20.04` instead of `ubuntu-latest` to fix #7282 

To check the workflow running result see:
https://github.com/hustcer/nu-release/actions/runs/3588720720/jobs/6040412953

# User-Facing Changes

_(List of all changes that impact the user experience here. This helps
us keep track of breaking changes.)_

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-01 10:44:21 +08:00
11b2423544 add comments to release-pkg for manual running (#7277)
# Description

This PR is just some comments in the release-pkg.nu script. We had to
figure out how to run it manually so I thought it would be good to
document those requirements just in case we need to do it again
sometime.

# User-Facing Changes

N/A

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-30 20:18:28 -06:00
1f9907d2ff Fix failing test after #7051 (#7299)
Numerics as record names have to be quoted
Changed to string
2022-12-01 00:48:02 +01:00
fd503fceaf allow tables in ++ operator (#7051)
This PR closes #6916, which now allows table/table operations on the
`++` operator.
```
[[a b]; [1 2]] ++ [[a b]; [1 3]]
╭───┬───┬───╮
│ # │ a │ b │
├───┼───┼───┤
│ 0 │ 1 │ 2 │
│ 1 │ 1 │ 3 │
╰───┴───┴───╯
```
2022-12-01 00:21:59 +01:00
b7e5790cd1 fix dfr datetime conversion (#7264)
# Description

Closes #7257

This fixes an issue where dataframes would always try to convert
datetimes with milliseconds. Since the timeunit is passed in, I make use
of it and try to choose the appropriate divisor.


# User-Facing Changes


# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-30 17:10:28 -06:00
794 changed files with 35598 additions and 17242 deletions

20
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,20 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# docs
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@ -11,9 +11,26 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [windows-latest, macos-latest, ubuntu-latest] # Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
# revisiting this when 20.04 is closer to EOL (April 2025)
platform: [windows-latest, macos-latest, ubuntu-20.04]
style: [default, dataframe]
rust: rust:
- stable - stable
include:
- style: default
flags: ""
- style: dataframe
flags: "--features=dataframe "
exclude:
# only test dataframes on Ubuntu (the fastest platform)
- platform: windows-latest
style: dataframe
- platform: macos-latest
style: dataframe
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
env: env:
@ -23,19 +40,13 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4 uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
- name: Rustfmt - name: cargo fmt
uses: actions-rs/cargo@v1.0.1 run: cargo fmt --all -- --check
with:
command: fmt
args: --all -- --check
- name: Clippy - name: Clippy
uses: actions-rs/cargo@v1.0.1 run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
with:
command: clippy
args: --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
nu-tests: nu-tests:
env: env:
@ -44,7 +55,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [windows-latest, macos-latest, ubuntu-latest] platform: [windows-latest, macos-latest, ubuntu-20.04]
style: [default, dataframe] style: [default, dataframe]
rust: rust:
- stable - stable
@ -66,13 +77,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4 uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
- name: Tests - name: Tests
uses: actions-rs/cargo@v1.0.1 run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
with:
command: test
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
python-virtualenv: python-virtualenv:
env: env:
@ -81,7 +89,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [ubuntu-latest, macos-latest, windows-latest] platform: [ubuntu-20.04, macos-latest, windows-latest]
rust: rust:
- stable - stable
py: py:
@ -93,13 +101,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4 uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
- name: Install Nushell - name: Install Nushell
uses: actions-rs/cargo@v1.0.1 run: cargo install --locked --path=. --profile ci --no-default-features
with:
command: install
args: --locked --path=. --profile ci --no-default-features
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
@ -108,8 +113,9 @@ jobs:
- run: python -m pip install tox - run: python -m pip install tox
# Get only the latest tagged version for stability reasons
- name: Install virtualenv - name: Install virtualenv
run: git clone https://github.com/pypa/virtualenv.git run: git clone https://github.com/pypa/virtualenv.git && cd virtualenv && git checkout $(git describe --tags | cut -d - -f 1)
shell: bash shell: bash
- name: Test Nushell in virtualenv - name: Test Nushell in virtualenv
@ -125,7 +131,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [windows-latest, macos-latest, ubuntu-latest] platform: [windows-latest, macos-latest, ubuntu-20.04]
rust: rust:
- stable - stable
@ -135,16 +141,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4 uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
- name: Clippy - name: Clippy
uses: actions-rs/cargo@v1.0.1 run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
with:
command: clippy
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
- name: Tests - name: Tests
uses: actions-rs/cargo@v1.0.1 run: cargo test --profile ci --package nu_plugin_*
with:
command: test
args: --profile ci --package nu_plugin_*

View File

@ -6,6 +6,32 @@
# REF: # REF:
# 1. https://github.com/volks73/cargo-wix # 1. https://github.com/volks73/cargo-wix
# Added 2022-11-29 when Windows packaging wouldn't work
# To run this manual for windows
# unset CARGO_TARGET_DIR if set
# hide-env CARGO_TARGET_DIR
# let-env TARGET = 'x86_64-pc-windows-msvc'
# let-env TARGET_RUSTFLAGS = ''
# let-env GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
# let-env GITHUB_OUTPUT = 'C:\Users\dschroeder\source\repos\forks\nushell\output\out.txt'
# let-env OS = 'windows-latest'
# You need to run this twice. The first pass makes the output folder and builds everything
# The second pass generates the msi file
# Pass 1 let-env _EXTRA_ = 'bin'
# Pass 2 let-env _EXTRA_ = 'msi'
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
# let-env Path = ($env.Path | append 'c:\apps\7-zip')
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
# let-env Path = ($env.Path | append 'c:\path\to\aria2c')
# make sure you have the wixtools installed https://wixtoolset.org/
# let-env Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
# After msi is generated, if you have to update winget-pkgs repo, you'll need to patch the release
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
# on the winget-pkgs PR. To generate the hash, run this command
# open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
# The main binary file to be released # The main binary file to be released
let bin = 'nu' let bin = 'nu'
let os = $env.OS let os = $env.OS
@ -16,8 +42,13 @@ let flags = $env.TARGET_RUSTFLAGS
let dist = $'($env.GITHUB_WORKSPACE)/output' let dist = $'($env.GITHUB_WORKSPACE)/output'
let version = (open Cargo.toml | get package.version) let version = (open Cargo.toml | get package.version)
$'Debugging info:'
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
# $env # $env
let USE_UBUNTU = 'ubuntu-20.04'
$'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b $'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
if not ('Cargo.lock' | path exists) { cargo generate-lockfile } if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
@ -26,8 +57,9 @@ $'Start building ($bin)...'; hr-line
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# Build for Ubuntu and macOS # Build for Ubuntu and macOS
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
if $os in ['ubuntu-latest', 'macos-latest'] { if $os in [$USE_UBUNTU, 'macos-latest'] {
if $os == 'ubuntu-latest' { if $os == $USE_UBUNTU {
sudo apt update
sudo apt-get install libxcb-composite0-dev -y sudo apt-get install libxcb-composite0-dev -y
} }
if $target == 'aarch64-unknown-linux-gnu' { if $target == 'aarch64-unknown-linux-gnu' {
@ -38,10 +70,14 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc' let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
cargo-build-nu $flags cargo-build-nu $flags
} else if $target == 'riscv64gc-unknown-linux-gnu' {
sudo apt-get install gcc-riscv64-linux-gnu -y
let-env CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER = 'riscv64-linux-gnu-gcc'
cargo-build-nu $flags
} else { } else {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?' # musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
# Actually just for x86_64-unknown-linux-musl target # Actually just for x86_64-unknown-linux-musl target
if $os == 'ubuntu-latest' { sudo apt install musl-tools -y } if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
cargo-build-nu $flags cargo-build-nu $flags
} }
} }
@ -88,7 +124,7 @@ if ($ver | str trim | is-empty) {
# Create a release archive and send it to output for the following steps # Create a release archive and send it to output for the following steps
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
cd $dist; $'(char nl)Creating release archive...'; hr-line cd $dist; $'(char nl)Creating release archive...'; hr-line
if $os in ['ubuntu-latest', 'macos-latest'] { if $os in [$USE_UBUNTU, 'macos-latest'] {
let files = (ls | get name) let files = (ls | get name)
let dest = $'($bin)-($version)-($target)' let dest = $'($bin)-($version)-($target)'
@ -101,7 +137,8 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
tar -czf $archive $dest tar -czf $archive $dest
print $'archive: ---> ($archive)'; ls $archive print $'archive: ---> ($archive)'; ls $archive
echo $'::set-output name=archive::($archive)' # REF: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
} else if $os == 'windows-latest' { } else if $os == 'windows-latest' {
@ -121,7 +158,8 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
cp -r $'($dist)/*' target/release/ cp -r $'($dist)/*' target/release/
cargo install cargo-wix --version 0.3.3 cargo install cargo-wix --version 0.3.3
cargo wix --no-build --nocapture --package nu --output $wixRelease cargo wix --no-build --nocapture --package nu --output $wixRelease
echo $'::set-output name=archive::($wixRelease)' print $'archive: ---> ($wixRelease)';
echo $"archive=($wixRelease)" | save --append $env.GITHUB_OUTPUT
} else { } else {
@ -131,7 +169,7 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
print $'archive: ---> ($archive)'; print $'archive: ---> ($archive)';
let pkg = (ls -f $archive | get name) let pkg = (ls -f $archive | get name)
if not ($pkg | is-empty) { if not ($pkg | is-empty) {
echo $'::set-output name=archive::($pkg | get 0)' echo $"archive=($pkg | get 0)" | save --append $env.GITHUB_OUTPUT
} }
} }
} }

View File

@ -27,6 +27,7 @@ jobs:
- x86_64-unknown-linux-musl - x86_64-unknown-linux-musl
- aarch64-unknown-linux-gnu - aarch64-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf - armv7-unknown-linux-gnueabihf
- riscv64gc-unknown-linux-gnu
extra: ['bin'] extra: ['bin']
include: include:
- target: aarch64-apple-darwin - target: aarch64-apple-darwin
@ -44,35 +45,37 @@ jobs:
os: windows-latest os: windows-latest
target_rustflags: '' target_rustflags: ''
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
- target: aarch64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
- target: armv7-unknown-linux-gnueabihf - target: armv7-unknown-linux-gnueabihf
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: ''
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v3.0.2 - uses: actions/checkout@v3.1.0
- name: Install Rust Toolchain Components - name: Update Rust Toolchain Target
uses: actions-rs/toolchain@v1.0.6 run: |
with: echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
override: true
profile: minimal - name: Setup Rust toolchain and cache
toolchain: stable uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
target: ${{ matrix.target }}
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v2.1 uses: hustcer/setup-nu@v3
with: with:
version: 0.69.1 version: 0.72.1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -12,7 +12,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v6
with: with:
#debug-only: true #debug-only: true
ascending: true ascending: true

13
.github/workflows/typos.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Typos
on: [pull_request]
jobs:
run:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Check spelling of book
uses: crate-ci/typos@master

12
.typos.toml Normal file
View File

@ -0,0 +1,12 @@
[files]
extend-exclude = ["crates/nu-command/tests/commands/table.rs", "*.tsv", "*.json", "*.txt"]
[default.extend-words]
# Ignore false-positives
nd = "nd"
fo = "fo"
ons = "ons"
ba = "ba"
Plasticos = "Plasticos"
IIF = "IIF"
numer = "numer"

835
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,14 @@ authors = ["The Nushell Project Developers"]
default-run = "nu" default-run = "nu"
description = "A new type of shell" description = "A new type of shell"
documentation = "https://www.nushell.sh/book/" documentation = "https://www.nushell.sh/book/"
edition = "2018" edition = "2021"
exclude = ["images"] exclude = ["images"]
homepage = "https://www.nushell.sh" homepage = "https://www.nushell.sh"
license = "MIT" license = "MIT"
name = "nu" name = "nu"
repository = "https://github.com/nushell/nushell" repository = "https://github.com/nushell/nushell"
rust-version = "1.60" rust-version = "1.60"
version = "0.72.1" version = "0.75.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -43,25 +43,25 @@ chrono = { version = "0.4.23", features = ["serde"] }
crossterm = "0.24.0" crossterm = "0.24.0"
ctrlc = "3.2.1" ctrlc = "3.2.1"
log = "0.4" log = "0.4"
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] } miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
nu-ansi-term = "0.46.0" nu-ansi-term = "0.46.0"
nu-cli = { path="./crates/nu-cli", version = "0.72.1" } nu-cli = { path = "./crates/nu-cli", version = "0.75.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.72.1" } nu-color-config = { path = "./crates/nu-color-config", version = "0.75.0" }
nu-command = { path="./crates/nu-command", version = "0.72.1" } nu-command = { path = "./crates/nu-command", version = "0.75.0" }
nu-engine = { path="./crates/nu-engine", version = "0.72.1" } nu-engine = { path = "./crates/nu-engine", version = "0.75.0" }
nu-json = { path="./crates/nu-json", version = "0.72.1" } nu-json = { path = "./crates/nu-json", version = "0.75.0" }
nu-parser = { path="./crates/nu-parser", version = "0.72.1" } nu-parser = { path = "./crates/nu-parser", version = "0.75.0" }
nu-path = { path="./crates/nu-path", version = "0.72.1" } nu-path = { path = "./crates/nu-path", version = "0.75.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.72.1" } nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.75.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.72.1" } nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.75.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.72.1" } nu-protocol = { path = "./crates/nu-protocol", version = "0.75.0" }
nu-system = { path = "./crates/nu-system", version = "0.72.1" } nu-system = { path = "./crates/nu-system", version = "0.75.0" }
nu-table = { path = "./crates/nu-table", version = "0.72.1" } nu-table = { path = "./crates/nu-table", version = "0.75.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.72.1" } nu-term-grid = { path = "./crates/nu-term-grid", version = "0.75.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.72.1" } nu-utils = { path = "./crates/nu-utils", version = "0.75.0" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]} reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
rayon = "1.5.1" rayon = "1.6.1"
is_executable = "1.0.1" is_executable = "1.0.1"
simplelog = "0.12.0" simplelog = "0.12.0"
time = "0.3.12" time = "0.3.12"
@ -76,21 +76,29 @@ signal-hook = { version = "0.3.14", default-features = false }
winres = "0.1" winres = "0.1"
[target.'cfg(target_family = "unix")'.dependencies] [target.'cfg(target_family = "unix")'.dependencies]
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]} nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"] }
atty = "0.2" atty = "0.2"
[dev-dependencies] [dev-dependencies]
nu-test-support = { path="./crates/nu-test-support", version = "0.72.1" } nu-test-support = { path = "./crates/nu-test-support", version = "0.75.0" }
tempfile = "3.2.0" tempfile = "3.2.0"
assert_cmd = "2.0.2" assert_cmd = "2.0.2"
criterion = "0.4"
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"
serial_test = "0.8.0" serial_test = "1.0.0"
hamcrest2 = "0.3.0" hamcrest2 = "0.3.0"
rstest = {version = "0.15.0", default-features = false} rstest = { version = "0.15.0", default-features = false }
itertools = "0.10.3" itertools = "0.10.3"
[features] [features]
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"] plugin = [
"nu-plugin",
"nu-cli/plugin",
"nu-parser/plugin",
"nu-command/plugin",
"nu-protocol/plugin",
"nu-engine/plugin",
]
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts # 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"] extra = ["default"]
default = ["plugin", "which-support", "trash-support", "sqlite"] default = ["plugin", "which-support", "trash-support", "sqlite"]
@ -113,7 +121,7 @@ dataframe = ["nu-command/dataframe"]
sqlite = ["nu-command/sqlite"] sqlite = ["nu-command/sqlite"]
[profile.release] [profile.release]
opt-level = "s" # Optimize for size opt-level = "s" # Optimize for size
strip = "debuginfo" strip = "debuginfo"
lto = "thin" lto = "thin"
@ -140,3 +148,10 @@ path = "src/main.rs"
# changing versions in each sub-crate of the workspace is tedious # changing versions in each sub-crate of the workspace is tedious
[patch.crates-io] [patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" } # 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

9
Cross.toml Normal file
View File

@ -0,0 +1,9 @@
# Configuration for cross-rs: https://github.com/cross-rs/cross
# Run cross-rs like this:
# cross build --target aarch64-unknown-linux-musl --release
[target.aarch64-unknown-linux-gnu]
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-gnu.dockerfile"
[target.aarch64-unknown-linux-musl]
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-musl.dockerfile"

View File

@ -1,6 +1,6 @@
# Nushell <!-- omit in toc --> # Nushell <!-- omit in toc -->
[![Crates.io](https://img.shields.io/crates/v/nu.svg)](https://crates.io/crates/nu) [![Crates.io](https://img.shields.io/crates/v/nu.svg)](https://crates.io/crates/nu)
![Build Status](https://img.shields.io/github/workflow/status/nushell/nushell/continuous-integration) ![Build Status](https://img.shields.io/github/actions/workflow/status/nushell/nushell/ci.yml?branch=main)
[![Discord](https://img.shields.io/discord/601130461678272522.svg?logo=discord)](https://discord.gg/NtAbbGn) [![Discord](https://img.shields.io/discord/601130461678272522.svg?logo=discord)](https://discord.gg/NtAbbGn)
[![The Changelog #363](https://img.shields.io/badge/The%20Changelog-%23363-61c192.svg)](https://changelog.com/podcast/363) [![The Changelog #363](https://img.shields.io/badge/The%20Changelog-%23363-61c192.svg)](https://changelog.com/podcast/363)
[![@nu_shell](https://img.shields.io/badge/twitter-@nu_shell-1DA1F3?style=flat-square)](https://twitter.com/nu_shell) [![@nu_shell](https://img.shields.io/badge/twitter-@nu_shell-1DA1F3?style=flat-square)](https://twitter.com/nu_shell)
@ -47,7 +47,7 @@ brew install nushell
winget install nushell winget install nushell
``` ```
To use `Nu` in Github Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail. To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers: Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
@ -174,6 +174,8 @@ These binaries interact with nu via a simple JSON-RPC protocol where the command
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout. If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases. If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
The [awesome-nu repo](https://github.com/nushell/awesome-nu#plugins) lists a variety of nu-plugins.
## Goals ## Goals
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals. Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.

7
benches/README.md Normal file
View File

@ -0,0 +1,7 @@
# Criterion benchmarks
These are benchmarks using [Criterion](https://github.com/bheisler/criterion.rs), a microbenchmarking tool for Rust.
Run all benchmarks with `cargo bench`
Or run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`

187
benches/benchmarks.rs Normal file
View File

@ -0,0 +1,187 @@
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use nu_cli::eval_source;
use nu_parser::parse;
use nu_plugin::{EncodingType, PluginResponse};
use nu_protocol::{PipelineData, Span, Value};
use nu_utils::{get_default_config, get_default_env};
// FIXME: All benchmarks live in this 1 file to speed up build times when benchmarking.
// When the *_benchmarks functions were in different files, `cargo bench` would build
// an executable for every single one - incredibly slowly. Would be nice to figure out
// a way to split things up again.
fn parser_benchmarks(c: &mut Criterion) {
let mut engine_state = nu_command::create_default_context();
// parsing config.nu breaks without PWD set
engine_state.add_env_var(
"PWD".into(),
Value::string("/some/dir".to_string(), Span::test_data()),
);
let default_env = get_default_env().as_bytes();
c.bench_function("parse_default_env_file", |b| {
b.iter_batched(
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|mut working_set| parse(&mut working_set, None, default_env, false, &[]),
BatchSize::SmallInput,
)
});
let default_config = get_default_config().as_bytes();
c.bench_function("parse_default_config_file", |b| {
b.iter_batched(
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|mut working_set| parse(&mut working_set, None, default_config, false, &[]),
BatchSize::SmallInput,
)
});
c.bench_function("eval default_env.nu", |b| {
b.iter(|| {
let mut engine_state = nu_command::create_default_context();
let mut stack = nu_protocol::engine::Stack::new();
eval_source(
&mut engine_state,
&mut stack,
get_default_env().as_bytes(),
"default_env.nu",
PipelineData::empty(),
)
})
});
c.bench_function("eval default_config.nu", |b| {
b.iter(|| {
let mut engine_state = nu_command::create_default_context();
// parsing config.nu breaks without PWD set
engine_state.add_env_var(
"PWD".into(),
Value::string("/some/dir".to_string(), Span::test_data()),
);
let mut stack = nu_protocol::engine::Stack::new();
eval_source(
&mut engine_state,
&mut stack,
get_default_config().as_bytes(),
"default_config.nu",
PipelineData::empty(),
)
})
});
}
fn eval_benchmarks(c: &mut Criterion) {
c.bench_function("eval default_env.nu", |b| {
b.iter(|| {
let mut engine_state = nu_command::create_default_context();
let mut stack = nu_protocol::engine::Stack::new();
eval_source(
&mut engine_state,
&mut stack,
get_default_env().as_bytes(),
"default_env.nu",
PipelineData::empty(),
)
})
});
c.bench_function("eval default_config.nu", |b| {
b.iter(|| {
let mut engine_state = nu_command::create_default_context();
// parsing config.nu breaks without PWD set
engine_state.add_env_var(
"PWD".into(),
Value::string("/some/dir".to_string(), Span::test_data()),
);
let mut stack = nu_protocol::engine::Stack::new();
eval_source(
&mut engine_state,
&mut stack,
get_default_config().as_bytes(),
"default_config.nu",
PipelineData::empty(),
)
})
});
}
// generate a new table data with `row_cnt` rows, `col_cnt` columns.
fn encoding_test_data(row_cnt: usize, col_cnt: usize) -> Value {
let columns: Vec<String> = (0..col_cnt).map(|x| format!("col_{x}")).collect();
let vals: Vec<Value> = (0..col_cnt as i64).map(Value::test_int).collect();
Value::List {
vals: (0..row_cnt)
.map(|_| Value::test_record(columns.clone(), vals.clone()))
.collect(),
span: Span::test_data(),
}
}
fn encoding_benchmarks(c: &mut Criterion) {
let mut group = c.benchmark_group("Encoding");
let test_cnt_pairs = [
(100, 5),
(100, 10),
(100, 15),
(1000, 5),
(1000, 10),
(1000, 15),
(10000, 5),
(10000, 10),
(10000, 15),
];
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
for fmt in ["json", "msgpack"] {
group.bench_function(&format!("{fmt} encode {row_cnt} * {col_cnt}"), |b| {
let mut res = vec![];
let test_data =
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
b.iter(|| encoder.encode_response(&test_data, &mut res))
});
}
}
group.finish();
}
fn decoding_benchmarks(c: &mut Criterion) {
let mut group = c.benchmark_group("Decoding");
let test_cnt_pairs = [
(100, 5),
(100, 10),
(100, 15),
(1000, 5),
(1000, 10),
(1000, 15),
(10000, 5),
(10000, 10),
(10000, 15),
];
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
for fmt in ["json", "msgpack"] {
group.bench_function(&format!("{fmt} decode for {row_cnt} * {col_cnt}"), |b| {
let mut res = vec![];
let test_data =
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
encoder.encode_response(&test_data, &mut res).unwrap();
let mut binary_data = std::io::Cursor::new(res);
b.iter(|| {
binary_data.set_position(0);
encoder.decode_response(&mut binary_data)
})
});
}
}
group.finish();
}
criterion_group!(
benches,
parser_benchmarks,
eval_benchmarks,
encoding_benchmarks,
decoding_benchmarks
);
criterion_main!(benches);

View File

@ -5,7 +5,7 @@
@echo. @echo.
echo Building nushell.exe echo Building nushell.exe
cargo build cargo build --features=dataframe cargo build --features=dataframe
@echo. @echo.
@cd crates\nu_plugin_example @cd crates\nu_plugin_example

View File

@ -5,34 +5,34 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cli" name = "nu-cli"
version = "0.72.1" version = "0.75.0"
[dev-dependencies] [dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.72.1" } nu-test-support = { path = "../nu-test-support", version = "0.75.0" }
nu-command = { path = "../nu-command", version = "0.72.1" } nu-command = { path = "../nu-command", version = "0.75.0" }
rstest = {version = "0.15.0", default-features = false} rstest = { version = "0.15.0", default-features = false }
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.72.1" } nu-engine = { path = "../nu-engine", version = "0.75.0" }
nu-path = { path = "../nu-path", version = "0.72.1" } nu-path = { path = "../nu-path", version = "0.75.0" }
nu-parser = { path = "../nu-parser", version = "0.72.1" } nu-parser = { path = "../nu-parser", version = "0.75.0" }
nu-protocol = { path = "../nu-protocol", version = "0.72.1" } nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
nu-utils = { path = "../nu-utils", version = "0.72.1" } nu-utils = { path = "../nu-utils", version = "0.75.0" }
nu-ansi-term = "0.46.0" nu-ansi-term = "0.46.0"
nu-color-config = { path = "../nu-color-config", version = "0.72.1" } nu-color-config = { path = "../nu-color-config", version = "0.75.0" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]} reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
atty = "0.2.14" atty = "0.2.14"
chrono = { default-features = false, features = ["std"], version = "0.4.23" } chrono = { default-features = false, features = ["std"], version = "0.4.23" }
crossterm = "0.24.0" crossterm = "0.24.0"
fancy-regex = "0.10.0" fancy-regex = "0.11.0"
fuzzy-matcher = "0.3.7" fuzzy-matcher = "0.3.7"
is_executable = "1.0.1" is_executable = "1.0.1"
lazy_static = "1.4.0" once_cell = "1.17.0"
log = "0.4" log = "0.4"
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] } miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
percent-encoding = "2" percent-encoding = "2"
sysinfo = "0.26.2" sysinfo = "0.27.7"
thiserror = "1.0.31" thiserror = "1.0.31"
[features] [features]

View File

@ -94,10 +94,7 @@ impl CommandCompletion {
value: String::from_utf8_lossy(&x.0).to_string(), value: String::from_utf8_lossy(&x.0).to_string(),
description: x.1, description: x.1,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
@ -108,10 +105,7 @@ impl CommandCompletion {
value: String::from_utf8_lossy(&x).to_string(), value: String::from_utf8_lossy(&x).to_string(),
description: None, description: None,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
@ -128,15 +122,15 @@ impl CommandCompletion {
value: x, value: x,
description: None, description: None,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
let results_strings: Vec<String> =
results.clone().into_iter().map(|x| x.value).collect();
for external in results_external { for external in results_external {
if results.contains(&external) { if results_strings.contains(&external.value) {
results.push(Suggestion { results.push(Suggestion {
value: format!("^{}", external.value), value: format!("^{}", external.value),
description: None, description: None,
@ -170,7 +164,7 @@ impl Completer for CommandCompletion {
.flattened .flattened
.iter() .iter()
.rev() .rev()
.skip_while(|x| x.0.end > pos) .skip_while(|x| x.0.end + offset > pos)
.take_while(|x| { .take_while(|x| {
matches!( matches!(
x.1, x.1,
@ -187,10 +181,7 @@ impl Completer for CommandCompletion {
let subcommands = if let Some(last) = last { let subcommands = if let Some(last) = last {
self.complete_commands( self.complete_commands(
working_set, working_set,
Span { Span::new(last.0.start, pos),
start: last.0.start,
end: pos,
},
offset, offset,
false, false,
options.match_algorithm, options.match_algorithm,

View File

@ -77,10 +77,7 @@ impl NuCompleter {
Value::List { Value::List {
vals: spans vals: spans
.iter() .iter()
.map(|it| Value::String { .map(|it| Value::string(it, Span::unknown()))
val: it.to_string(),
span: Span::unknown(),
})
.collect(), .collect(),
span: Span::unknown(), span: Span::unknown(),
}, },
@ -92,7 +89,7 @@ impl NuCompleter {
&self.engine_state, &self.engine_state,
&mut callee_stack, &mut callee_stack,
block, block,
PipelineData::new(span), PipelineData::empty(),
true, true,
true, true,
); );
@ -101,30 +98,31 @@ impl NuCompleter {
Ok(pd) => { Ok(pd) => {
let value = pd.into_value(span); let value = pd.into_value(span);
if let Value::List { vals, span: _ } = value { if let Value::List { vals, span: _ } = value {
let result = map_value_completions( let result =
vals.iter(), map_value_completions(vals.iter(), Span::new(span.start, span.end), offset);
Span {
start: span.start,
end: span.end,
},
offset,
);
return Some(result); return Some(result);
} }
} }
Err(err) => println!("failed to eval completer block: {}", err), Err(err) => println!("failed to eval completer block: {err}"),
} }
None None
} }
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> { fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
// pos: is the position of the cursor in the shell input.
// e.g. lets say you have an alias -> `alias ll = ls -l` and you type in the shell:
// > ll -a | c
// and your cursor is right after `c` then `pos` = 9
let mut working_set = StateWorkingSet::new(&self.engine_state); let mut working_set = StateWorkingSet::new(&self.engine_state);
let offset = working_set.next_span_start(); let mut offset = working_set.next_span_start();
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set); let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
let initial_line = line.to_string(); // new_line: vector containing all alias "translations" so if it was `ll` now is `ls -l`.
let alias_total_offset: usize = alias_offset.iter().sum(); // alias_offset:vector the offset between the name and the alias)
let initial_line = line.to_string(); // Entire line in the shell input.
let alias_total_offset: usize = alias_offset.iter().sum(); // the sum of all alias offsets.
new_line.insert(alias_total_offset + pos, b'a'); new_line.insert(alias_total_offset + pos, b'a');
let pos = offset + pos; let pos = offset + pos;
let config = self.engine_state.get_config(); let config = self.engine_state.get_config();
@ -137,7 +135,8 @@ impl NuCompleter {
PipelineElement::Expression(_, expr) PipelineElement::Expression(_, expr)
| PipelineElement::Redirection(_, _, expr) | PipelineElement::Redirection(_, _, expr)
| PipelineElement::And(_, expr) | PipelineElement::And(_, expr)
| PipelineElement::Or(_, expr) => { | PipelineElement::Or(_, expr)
| PipelineElement::SeparateRedirection { out: (_, expr), .. } => {
let flattened: Vec<_> = flatten_expression(&working_set, &expr); let flattened: Vec<_> = flatten_expression(&working_set, &expr);
let span_offset: usize = alias_offset.iter().sum(); let span_offset: usize = alias_offset.iter().sum();
let mut spans: Vec<String> = vec![]; let mut spans: Vec<String> = vec![];
@ -164,17 +163,22 @@ impl NuCompleter {
most_left_variable(flat_idx, &working_set, flattened.clone()); most_left_variable(flat_idx, &working_set, flattened.clone());
// Create a new span // Create a new span
let new_span = if flat_idx == 0 { // if flat_idx == 0
Span { let mut span_start = flat.0.start;
start: flat.0.start, let mut span_end = flat.0.end - 1 - span_offset;
end: flat.0.end - 1 - span_offset,
} if flat_idx != 0 {
} else { span_start = flat.0.start - span_offset;
Span { span_end = flat.0.end - 1 - span_offset;
start: flat.0.start - span_offset, }
end: flat.0.end - 1 - span_offset,
} if span_end < span_start {
}; span_start = flat.0.start;
span_end = flat.0.end - 1;
offset += span_offset
}
let new_span = Span::new(span_start, span_end);
// Parses the prefix. Completion should look up to the cursor position, not after. // Parses the prefix. Completion should look up to the cursor position, not after.
let mut prefix = working_set.get_span_contents(flat.0).to_vec(); let mut prefix = working_set.get_span_contents(flat.0).to_vec();
@ -189,6 +193,10 @@ impl NuCompleter {
most_left_var.unwrap_or((vec![], vec![])), most_left_var.unwrap_or((vec![], vec![])),
); );
if offset > new_span.start {
offset -= span_offset;
}
return self.process_completion( return self.process_completion(
&mut completer, &mut completer,
&working_set, &working_set,

View File

@ -52,13 +52,13 @@ impl Completer for CustomCompletion {
head: span, head: span,
arguments: vec![ arguments: vec![
Argument::Positional(Expression { Argument::Positional(Expression {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
ty: Type::String, ty: Type::String,
expr: Expr::String(self.line.clone()), expr: Expr::String(self.line.clone()),
custom_completion: None, custom_completion: None,
}), }),
Argument::Positional(Expression { Argument::Positional(Expression {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
ty: Type::Int, ty: Type::Int,
expr: Expr::Int(line_pos as i64), expr: Expr::Int(line_pos as i64),
custom_completion: None, custom_completion: None,
@ -66,15 +66,16 @@ impl Completer for CustomCompletion {
], ],
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: true, redirect_stderr: true,
parser_info: vec![],
}, },
PipelineData::new(span), PipelineData::empty(),
); );
let mut custom_completion_options = None; let mut custom_completion_options = None;
// Parse result // Parse result
let suggestions = match result { let suggestions = result
Ok(pd) => { .map(|pd| {
let value = pd.into_value(span); let value = pd.into_value(span);
match &value { match &value {
Value::Record { .. } => { Value::Record { .. } => {
@ -131,9 +132,8 @@ impl Completer for CustomCompletion {
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset), Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
_ => vec![], _ => vec![],
} }
} })
_ => vec![], .unwrap_or_default();
};
if let Some(custom_completion_options) = custom_completion_options { if let Some(custom_completion_options) = custom_completion_options {
filter(&prefix, suggestions, &custom_completion_options) filter(&prefix, suggestions, &custom_completion_options)

View File

@ -33,14 +33,7 @@ impl Completer for DirectoryCompletion {
_: usize, _: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<Suggestion> { ) -> Vec<Suggestion> {
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") { let cwd = self.engine_state.current_work_dir();
match d.as_string() {
Ok(s) => s,
Err(_) => "".to_string(),
}
} else {
"".to_string()
};
let partial = String::from_utf8_lossy(&prefix).to_string(); let partial = String::from_utf8_lossy(&prefix).to_string();
// Filter only the folders // Filter only the folders
@ -126,7 +119,7 @@ pub fn directory_completion(
let mut file_name = entry.file_name().to_string_lossy().into_owned(); let mut file_name = entry.file_name().to_string_lossy().into_owned();
if matches(&partial, &file_name, options) { if matches(&partial, &file_name, options) {
let mut path = if prepend_base_dir(original_input, &base_dir_name) { let mut path = if prepend_base_dir(original_input, &base_dir_name) {
format!("{}{}", base_dir_name, file_name) format!("{base_dir_name}{file_name}")
} else { } else {
file_name.to_string() file_name.to_string()
}; };
@ -136,9 +129,13 @@ pub fn directory_completion(
file_name.push(SEP); file_name.push(SEP);
} }
// Fix files or folders with quotes // Fix files or folders with quotes or hash
if path.contains('\'') || path.contains('"') || path.contains(' ') { if path.contains('\'')
path = format!("`{}`", path); || path.contains('"')
|| path.contains(' ')
|| path.contains('#')
{
path = format!("`{path}`");
} }
Some((span, path)) Some((span, path))

View File

@ -58,7 +58,7 @@ impl Completer for DotNuCompletion {
}; };
// Check if the base_dir is a folder // Check if the base_dir is a folder
if base_dir != format!(".{}", SEP) { if base_dir != format!(".{SEP}") {
// Add the base dir into the directories to be searched // Add the base dir into the directories to be searched
search_dirs.push(base_dir.clone()); search_dirs.push(base_dir.clone());
@ -70,14 +70,7 @@ impl Completer for DotNuCompletion {
partial = base_dir_partial; partial = base_dir_partial;
} else { } else {
// Fetch the current folder // Fetch the current folder
let current_folder = if let Some(d) = self.engine_state.get_env_var("PWD") { let current_folder = self.engine_state.current_work_dir();
match d.as_string() {
Ok(s) => s,
Err(_) => "".to_string(),
}
} else {
"".to_string()
};
is_current_folder = true; is_current_folder = true;
// Add the current folder and the lib dirs into the // Add the current folder and the lib dirs into the

View File

@ -30,14 +30,7 @@ impl Completer for FileCompletion {
_: usize, _: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<Suggestion> { ) -> Vec<Suggestion> {
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") { let cwd = self.engine_state.current_work_dir();
match d.as_string() {
Ok(s) => s,
Err(_) => "".to_string(),
}
} else {
"".to_string()
};
let prefix = String::from_utf8_lossy(&prefix).to_string(); let prefix = String::from_utf8_lossy(&prefix).to_string();
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options) let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
.into_iter() .into_iter()
@ -131,7 +124,7 @@ pub fn file_path_completion(
let mut file_name = entry.file_name().to_string_lossy().into_owned(); let mut file_name = entry.file_name().to_string_lossy().into_owned();
if matches(&partial, &file_name, options) { if matches(&partial, &file_name, options) {
let mut path = if prepend_base_dir(original_input, &base_dir_name) { let mut path = if prepend_base_dir(original_input, &base_dir_name) {
format!("{}{}", base_dir_name, file_name) format!("{base_dir_name}{file_name}")
} else { } else {
file_name.to_string() file_name.to_string()
}; };
@ -141,9 +134,15 @@ pub fn file_path_completion(
file_name.push(SEP); file_name.push(SEP);
} }
// Fix files or folders with quotes // Fix files or folders with quotes or hashes
if path.contains('\'') || path.contains('"') || path.contains(' ') { if path.contains('\'')
path = format!("`{}`", path); || path.contains('"')
|| path.contains(' ')
|| path.contains('#')
|| path.contains('(')
|| path.contains(')')
{
path = format!("`{path}`");
} }
Some((span, path)) Some((span, path))
@ -171,7 +170,7 @@ pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
/// Returns whether the base_dir should be prepended to the file path /// Returns whether the base_dir should be prepended to the file path
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool { pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
if base_dir == format!(".{}", SEP) { if base_dir == format!(".{SEP}") {
// if the current base_dir path is the local folder we only add a "./" prefix if the user // if the current base_dir path is the local folder we only add a "./" prefix if the user
// input already includes a local folder prefix. // input already includes a local folder prefix.
let manually_entered = { let manually_entered = {

View File

@ -9,6 +9,8 @@ use reedline::Suggestion;
use std::str; use std::str;
use std::sync::Arc; use std::sync::Arc;
use super::MatchAlgorithm;
#[derive(Clone)] #[derive(Clone)]
pub struct VariableCompletion { pub struct VariableCompletion {
engine_state: Arc<EngineState>, // TODO: Is engine state necessary? It's already a part of working set in fetch() engine_state: Arc<EngineState>, // TODO: Is engine state necessary? It's already a part of working set in fetch()
@ -73,10 +75,11 @@ impl Completer for VariableCompletion {
for suggestion in for suggestion in
nested_suggestions(val.clone(), nested_levels, current_span) nested_suggestions(val.clone(), nested_levels, current_span)
{ {
if options if options.match_algorithm.matches_u8_insensitive(
.match_algorithm options.case_sensitive,
.matches_u8(suggestion.value.as_bytes(), &prefix) suggestion.value.as_bytes(),
{ &prefix,
) {
output.push(suggestion); output.push(suggestion);
} }
} }
@ -86,10 +89,11 @@ impl Completer for VariableCompletion {
} else { } else {
// No nesting provided, return all env vars // No nesting provided, return all env vars
for env_var in env_vars { for env_var in env_vars {
if options if options.match_algorithm.matches_u8_insensitive(
.match_algorithm options.case_sensitive,
.matches_u8(env_var.0.as_bytes(), &prefix) env_var.0.as_bytes(),
{ &prefix,
) {
output.push(Suggestion { output.push(Suggestion {
value: env_var.0, value: env_var.0,
description: None, description: None,
@ -111,18 +115,16 @@ impl Completer for VariableCompletion {
&self.engine_state, &self.engine_state,
&self.stack, &self.stack,
nu_protocol::NU_VARIABLE_ID, nu_protocol::NU_VARIABLE_ID,
nu_protocol::Span { nu_protocol::Span::new(current_span.start, current_span.end),
start: current_span.start,
end: current_span.end,
},
) { ) {
for suggestion in for suggestion in
nested_suggestions(nuval, self.var_context.1.clone(), current_span) nested_suggestions(nuval, self.var_context.1.clone(), current_span)
{ {
if options if options.match_algorithm.matches_u8_insensitive(
.match_algorithm options.case_sensitive,
.matches_u8(suggestion.value.as_bytes(), &prefix) suggestion.value.as_bytes(),
{ &prefix,
) {
output.push(suggestion); output.push(suggestion);
} }
} }
@ -134,23 +136,18 @@ impl Completer for VariableCompletion {
// Completion other variable types // Completion other variable types
if let Some(var_id) = var_id { if let Some(var_id) = var_id {
// Extract the variable value from the stack // Extract the variable value from the stack
let var = self.stack.get_var( let var = self.stack.get_var(var_id, Span::new(span.start, span.end));
var_id,
Span {
start: span.start,
end: span.end,
},
);
// If the value exists and it's of type Record // If the value exists and it's of type Record
if let Ok(value) = var { if let Ok(value) = var {
for suggestion in for suggestion in
nested_suggestions(value, self.var_context.1.clone(), current_span) nested_suggestions(value, self.var_context.1.clone(), current_span)
{ {
if options if options.match_algorithm.matches_u8_insensitive(
.match_algorithm options.case_sensitive,
.matches_u8(suggestion.value.as_bytes(), &prefix) suggestion.value.as_bytes(),
{ &prefix,
) {
output.push(suggestion); output.push(suggestion);
} }
} }
@ -162,10 +159,11 @@ impl Completer for VariableCompletion {
// Variable completion (e.g: $en<tab> to complete $env) // Variable completion (e.g: $en<tab> to complete $env)
for builtin in builtins { for builtin in builtins {
if options if options.match_algorithm.matches_u8_insensitive(
.match_algorithm options.case_sensitive,
.matches_u8(builtin.as_bytes(), &prefix) builtin.as_bytes(),
{ &prefix,
) {
output.push(Suggestion { output.push(Suggestion {
value: builtin.to_string(), value: builtin.to_string(),
description: None, description: None,
@ -187,7 +185,11 @@ impl Completer for VariableCompletion {
.rev() .rev()
{ {
for v in &overlay_frame.vars { for v in &overlay_frame.vars {
if options.match_algorithm.matches_u8(v.0, &prefix) { if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
v.0,
&prefix,
) {
output.push(Suggestion { output.push(Suggestion {
value: String::from_utf8_lossy(v.0).to_string(), value: String::from_utf8_lossy(v.0).to_string(),
description: None, description: None,
@ -209,7 +211,11 @@ impl Completer for VariableCompletion {
.rev() .rev()
{ {
for v in &overlay_frame.vars { for v in &overlay_frame.vars {
if options.match_algorithm.matches_u8(v.0, &prefix) { if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
v.0,
&prefix,
) {
output.push(Suggestion { output.push(Suggestion {
value: String::from_utf8_lossy(v.0).to_string(), value: String::from_utf8_lossy(v.0).to_string(),
description: None, description: None,
@ -256,6 +262,20 @@ fn nested_suggestions(
output output
} }
Value::LazyRecord { val, .. } => {
// Add all the columns as completion
for column_name in val.column_names() {
output.push(Suggestion {
value: column_name.to_string(),
description: None,
extra: None,
span: current_span,
append_whitespace: false,
});
}
output
}
_ => output, _ => output,
} }
@ -281,7 +301,7 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
// Current sublevel value not found // Current sublevel value not found
return Value::Nothing { return Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}; };
} }
_ => return val, _ => return val,
@ -290,3 +310,13 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
val val
} }
impl MatchAlgorithm {
pub fn matches_u8_insensitive(&self, sensitive: bool, haystack: &[u8], needle: &[u8]) -> bool {
if sensitive {
self.matches_u8(haystack, needle)
} else {
self.matches_u8(&haystack.to_ascii_lowercase(), &needle.to_ascii_lowercase())
}
}
}

View File

@ -1,14 +1,14 @@
use crate::util::{eval_source, report_error}; use crate::util::{eval_source, report_error};
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use log::info;
#[cfg(feature = "plugin")]
use nu_parser::ParseError; use nu_parser::ParseError;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use nu_path::canonicalize_with; use nu_path::canonicalize_with;
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use nu_protocol::Spanned; use nu_protocol::Spanned;
use nu_protocol::{HistoryFileFormat, PipelineData, Span}; use nu_protocol::{HistoryFileFormat, PipelineData};
#[cfg(feature = "plugin")]
use nu_utils::utils::perf;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
@ -24,6 +24,8 @@ pub fn read_plugin_file(
plugin_file: Option<Spanned<String>>, plugin_file: Option<Spanned<String>>,
storage_path: &str, storage_path: &str,
) { ) {
let start_time = std::time::Instant::now();
let mut plug_path = String::new();
// Reading signatures from signature file // Reading signatures from signature file
// The plugin.nu file stores the parsed signature collected from each registered plugin // The plugin.nu file stores the parsed signature collected from each registered plugin
add_plugin_file(engine_state, plugin_file, storage_path); add_plugin_file(engine_state, plugin_file, storage_path);
@ -31,19 +33,25 @@ pub fn read_plugin_file(
let plugin_path = engine_state.plugin_signatures.clone(); let plugin_path = engine_state.plugin_signatures.clone();
if let Some(plugin_path) = plugin_path { if let Some(plugin_path) = plugin_path {
let plugin_filename = plugin_path.to_string_lossy(); let plugin_filename = plugin_path.to_string_lossy();
plug_path = plugin_filename.to_string();
if let Ok(contents) = std::fs::read(&plugin_path) { if let Ok(contents) = std::fs::read(&plugin_path) {
eval_source( eval_source(
engine_state, engine_state,
stack, stack,
&contents, &contents,
&plugin_filename, &plugin_filename,
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
} }
} }
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!()); perf(
&format!("read_plugin_file {}", &plug_path),
start_time,
file!(),
line!(),
column!(),
);
} }
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
@ -56,12 +64,11 @@ pub fn add_plugin_file(
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
let cwd = working_set.get_cwd(); let cwd = working_set.get_cwd();
match canonicalize_with(&plugin_file.item, cwd) { if let Ok(path) = canonicalize_with(&plugin_file.item, cwd) {
Ok(path) => engine_state.plugin_signatures = Some(path), engine_state.plugin_signatures = Some(path)
Err(_) => { } else {
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span); let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
report_error(&working_set, &e); report_error(&working_set, &e);
}
} }
} else if let Some(mut plugin_path) = nu_path::config_dir() { } else if let Some(mut plugin_path) = nu_path::config_dir() {
// Path to store plugins signatures // Path to store plugins signatures
@ -85,7 +92,7 @@ pub fn eval_config_contents(
stack, stack,
&contents, &contents,
&config_filename, &config_filename,
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
// Merge the environment in case env vars changed in the config // Merge the environment in case env vars changed in the config

View File

@ -2,13 +2,13 @@ use crate::util::{eval_source, report_error};
use log::info; use log::info;
use log::trace; use log::trace;
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use nu_engine::convert_env_values; use nu_engine::{convert_env_values, current_dir};
use nu_parser::parse; use nu_parser::parse;
use nu_protocol::Type; use nu_path::canonicalize_with;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
Config, PipelineData, Span, Value, Config, PipelineData, ShellError, Span, Type, Value,
}; };
use nu_utils::stdout_write_all_and_flush; use nu_utils::stdout_write_all_and_flush;
@ -27,14 +27,75 @@ pub fn evaluate_file(
std::process::exit(1); std::process::exit(1);
} }
let file = std::fs::read(&path).into_diagnostic()?; let cwd = current_dir(engine_state, stack)?;
engine_state.start_in_file(Some(&path)); let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::FileNotFoundCustom(
format!("Could not access file '{}': {:?}", path, e.to_string()),
Span::unknown(),
),
);
std::process::exit(1);
});
let file_path_str = file_path.to_str().unwrap_or_else(|| {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::NonUtf8Custom(
format!(
"Input file name '{}' is not valid UTF8",
file_path.to_string_lossy()
),
Span::unknown(),
),
);
std::process::exit(1);
});
let file = std::fs::read(&file_path)
.into_diagnostic()
.unwrap_or_else(|e| {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::FileNotFoundCustom(
format!(
"Could not read file '{}': {:?}",
file_path_str,
e.to_string()
),
Span::unknown(),
),
);
std::process::exit(1);
});
engine_state.start_in_file(Some(file_path_str));
let parent = file_path.parent().unwrap_or_else(|| {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::FileNotFoundCustom(
format!("The file path '{file_path_str}' does not have a parent"),
Span::unknown(),
),
);
std::process::exit(1);
});
stack.add_env_var(
"FILE_PWD".to_string(),
Value::string(parent.to_string_lossy(), Span::unknown()),
);
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
trace!("parsing file: {}", path); trace!("parsing file: {}", file_path_str);
let _ = parse(&mut working_set, Some(file_path_str), &file, false, &[]);
let _ = parse(&mut working_set, Some(&path), &file, false, &[]);
if working_set.find_decl(b"main", &Type::Any).is_some() { if working_set.find_decl(b"main", &Type::Any).is_some() {
let args = format!("main {}", args.join(" ")); let args = format!("main {}", args.join(" "));
@ -43,15 +104,15 @@ pub fn evaluate_file(
engine_state, engine_state,
stack, stack,
&file, &file,
&path, file_path_str,
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
) { ) {
std::process::exit(1); std::process::exit(1);
} }
if !eval_source(engine_state, stack, args.as_bytes(), "<commandline>", input) { if !eval_source(engine_state, stack, args.as_bytes(), "<commandline>", input) {
std::process::exit(1); std::process::exit(1);
} }
} else if !eval_source(engine_state, stack, &file, &path, input) { } else if !eval_source(engine_state, stack, &file, file_path_str, input) {
std::process::exit(1); std::process::exit(1);
} }
@ -60,7 +121,7 @@ pub fn evaluate_file(
Ok(()) Ok(())
} }
pub fn print_table_or_error( pub(crate) fn print_table_or_error(
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
mut pipeline_data: PipelineData, mut pipeline_data: PipelineData,
@ -76,43 +137,36 @@ pub fn print_table_or_error(
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data { if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, error); report_error(&working_set, error);
std::process::exit(1); std::process::exit(1);
} }
match engine_state.find_decl("table".as_bytes(), &[]) { if let Some(decl_id) = engine_state.find_decl("table".as_bytes(), &[]) {
Some(decl_id) => { let command = engine_state.get_decl(decl_id);
let command = engine_state.get_decl(decl_id); if command.get_block_id().is_some() {
if command.get_block_id().is_some() { print_or_exit(pipeline_data, engine_state, config);
print_or_exit(pipeline_data, engine_state, config); } else {
} else { let table = command.run(
let table = command.run( engine_state,
engine_state, stack,
stack, &Call::new(Span::new(0, 0)),
&Call::new(Span::new(0, 0)), pipeline_data,
pipeline_data, );
);
match table { match table {
Ok(table) => { Ok(table) => {
print_or_exit(table, engine_state, config); print_or_exit(table, engine_state, config);
} }
Err(error) => { Err(error) => {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &error);
report_error(&working_set, &error); std::process::exit(1);
std::process::exit(1);
}
} }
} }
} }
None => { } else {
print_or_exit(pipeline_data, engine_state, config); print_or_exit(pipeline_data, engine_state, config);
} }
};
// Make sure everything has finished // Make sure everything has finished
if let Some(exit_code) = exit_code { if let Some(exit_code) = exit_code {
@ -138,9 +192,7 @@ fn print_or_exit(pipeline_data: PipelineData, engine_state: &mut EngineState, co
std::process::exit(1); std::process::exit(1);
} }
let mut out = item.into_string("\n", config); let out = item.into_string("\n", config) + "\n";
out.push('\n'); let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{err}"));
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{}", err));
} }
} }

View File

@ -411,10 +411,10 @@ impl DescriptionMenu {
RESET RESET
) )
} else { } else {
format!(" {}\r\n", example) format!(" {example}\r\n")
} }
} else { } else {
format!(" {}\r\n", example) format!(" {example}\r\n")
} }
}) })
.collect(); .collect();
@ -429,7 +429,7 @@ impl DescriptionMenu {
examples, examples,
) )
} else { } else {
format!("\r\n\r\nExamples:\r\n{}", examples,) format!("\r\n\r\nExamples:\r\n{examples}",)
} }
} }
} }

View File

@ -42,20 +42,14 @@ impl Completer for NuMenuCompleter {
if let Some(buffer) = block.signature.get_positional(0) { if let Some(buffer) = block.signature.get_positional(0) {
if let Some(buffer_id) = &buffer.var_id { if let Some(buffer_id) = &buffer.var_id {
let line_buffer = Value::String { let line_buffer = Value::string(parsed.remainder, self.span);
val: parsed.remainder.to_string(),
span: self.span,
};
self.stack.add_var(*buffer_id, line_buffer); self.stack.add_var(*buffer_id, line_buffer);
} }
} }
if let Some(position) = block.signature.get_positional(1) { if let Some(position) = block.signature.get_positional(1) {
if let Some(position_id) = &position.var_id { if let Some(position_id) = &position.var_id {
let line_buffer = Value::Int { let line_buffer = Value::int(pos as i64, self.span);
val: pos as i64,
span: self.span,
};
self.stack.add_var(*position_id, line_buffer); self.stack.add_var(*position_id, line_buffer);
} }
} }
@ -87,13 +81,10 @@ fn convert_to_suggestions(
) -> Vec<Suggestion> { ) -> Vec<Suggestion> {
match value { match value {
Value::Record { .. } => { Value::Record { .. } => {
let text = match value let text = value
.get_data_by_key("value") .get_data_by_key("value")
.and_then(|val| val.as_string().ok()) .and_then(|val| val.as_string().ok())
{ .unwrap_or_else(|| "No value key".to_string());
Some(val) => val,
None => "No value key".to_string(),
};
let description = value let description = value
.get_data_by_key("description") .get_data_by_key("description")
@ -163,7 +154,7 @@ fn convert_to_suggestions(
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference)) .flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
.collect(), .collect(),
_ => vec![Suggestion { _ => vec![Suggestion {
value: format!("Not a record: {:?}", value), value: format!("Not a record: {value:?}"),
description: None, description: None,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span {

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Value}; use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type, Value};
use reedline::Highlighter; use reedline::Highlighter;
#[derive(Clone)] #[derive(Clone)]
@ -12,7 +12,9 @@ impl Command for NuHighlight {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("nu-highlight").category(Category::Strings) Signature::build("nu-highlight")
.category(Category::Strings)
.input_output_types(vec![(Type::String, Type::String)])
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -33,7 +35,7 @@ impl Command for NuHighlight {
let head = call.head; let head = call.head;
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone(); let engine_state = std::sync::Arc::new(engine_state.clone());
let config = engine_state.get_config().clone(); let config = engine_state.get_config().clone();
let highlighter = crate::NuHighlighter { let highlighter = crate::NuHighlighter {

View File

@ -2,7 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -15,6 +16,7 @@ impl Command for Print {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("print") Signature::build("print")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.rest("rest", SyntaxShape::Any, "the values to print") .rest("rest", SyntaxShape::Any, "the values to print")
.switch( .switch(
"no-newline", "no-newline",
@ -50,14 +52,13 @@ Since this command has no output, there is no point in piping it with other comm
let args: Vec<Value> = call.rest(engine_state, stack, 0)?; let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
let no_newline = call.has_flag("no-newline"); let no_newline = call.has_flag("no-newline");
let to_stderr = call.has_flag("stderr"); let to_stderr = call.has_flag("stderr");
let head = call.head;
for arg in args { for arg in args {
arg.into_pipeline_data() arg.into_pipeline_data()
.print(engine_state, stack, no_newline, to_stderr)?; .print(engine_state, stack, no_newline, to_stderr)?;
} }
Ok(PipelineData::new(head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -91,7 +91,7 @@ impl NushellPrompt {
} }
fn default_wrapped_custom_string(&self, str: String) -> String { fn default_wrapped_custom_string(&self, str: String) -> String {
format!("({})", str) format!("({str})")
} }
} }
@ -105,7 +105,7 @@ impl Prompt for NushellPrompt {
if let Some(prompt_string) = &self.left_prompt_string { if let Some(prompt_string) = &self.left_prompt_string {
prompt_string.replace('\n', "\r\n").into() prompt_string.replace('\n', "\r\n").into()
} else { } else {
let default = DefaultPrompt::new(); let default = DefaultPrompt::default();
default default
.render_prompt_left() .render_prompt_left()
.to_string() .to_string()
@ -118,7 +118,7 @@ impl Prompt for NushellPrompt {
if let Some(prompt_string) = &self.right_prompt_string { if let Some(prompt_string) = &self.right_prompt_string {
prompt_string.replace('\n', "\r\n").into() prompt_string.replace('\n', "\r\n").into()
} else { } else {
let default = DefaultPrompt::new(); let default = DefaultPrompt::default();
default default
.render_prompt_right() .render_prompt_right()
.to_string() .to_string()
@ -130,32 +130,36 @@ impl Prompt for NushellPrompt {
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> { fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
match edit_mode { match edit_mode {
PromptEditMode::Default => match &self.default_prompt_indicator { PromptEditMode::Default => match &self.default_prompt_indicator {
Some(indicator) => indicator.as_str().into(), Some(indicator) => indicator,
None => "".into(), None => "",
}, }
.into(),
PromptEditMode::Emacs => match &self.default_prompt_indicator { PromptEditMode::Emacs => match &self.default_prompt_indicator {
Some(indicator) => indicator.as_str().into(), Some(indicator) => indicator,
None => "".into(), None => "",
}, }
.into(),
PromptEditMode::Vi(vi_mode) => match vi_mode { PromptEditMode::Vi(vi_mode) => match vi_mode {
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator { PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
Some(indicator) => indicator.as_str().into(), Some(indicator) => indicator,
None => ": ".into(), None => ": ",
}, },
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator { PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
Some(indicator) => indicator.as_str().into(), Some(indicator) => indicator,
None => "".into(), None => "",
}, },
}, }
.into(),
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(), PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
} }
} }
fn render_prompt_multiline_indicator(&self) -> Cow<str> { fn render_prompt_multiline_indicator(&self) -> Cow<str> {
match &self.default_multiline_indicator { match &self.default_multiline_indicator {
Some(indicator) => indicator.as_str().into(), Some(indicator) => indicator,
None => "::: ".into(), None => "::: ",
} }
.into()
} }
fn render_prompt_history_search_indicator( fn render_prompt_history_search_indicator(

View File

@ -1,10 +1,10 @@
use crate::util::report_error; use crate::util::report_error;
use crate::NushellPrompt; use crate::NushellPrompt;
use log::info; use log::trace;
use nu_engine::eval_subexpression; use nu_engine::eval_subexpression;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
Config, PipelineData, Span, Value, Config, PipelineData, Value,
}; };
use reedline::Prompt; use reedline::Prompt;
@ -37,52 +37,39 @@ fn get_prompt_string(
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
let mut stack = stack.captures_to_stack(&captures); let mut stack = stack.captures_to_stack(&captures);
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt // Use eval_subexpression to force a redirection of output, so we can use everything in prompt
let ret_val = eval_subexpression( let ret_val =
engine_state, eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());
&mut stack, trace!(
block,
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
);
info!(
"get_prompt_string (block) {}:{}:{}", "get_prompt_string (block) {}:{}:{}",
file!(), file!(),
line!(), line!(),
column!() column!()
); );
match ret_val { ret_val
Ok(ret_val) => Some(ret_val), .map_err(|err| {
Err(err) => {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err); report_error(&working_set, &err);
None })
} .ok()
}
} }
Value::Block { val: block_id, .. } => { Value::Block { val: block_id, .. } => {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt // Use eval_subexpression to force a redirection of output, so we can use everything in prompt
let ret_val = eval_subexpression( let ret_val = eval_subexpression(engine_state, stack, block, PipelineData::empty());
engine_state, trace!(
stack,
block,
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
);
info!(
"get_prompt_string (block) {}:{}:{}", "get_prompt_string (block) {}:{}:{}",
file!(), file!(),
line!(), line!(),
column!() column!()
); );
match ret_val { ret_val
Ok(ret_val) => Some(ret_val), .map_err(|err| {
Err(err) => {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err); report_error(&working_set, &err);
None })
} .ok()
}
} }
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)), Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
_ => None, _ => None,
@ -90,20 +77,17 @@ fn get_prompt_string(
.and_then(|pipeline_data| { .and_then(|pipeline_data| {
let output = pipeline_data.collect_string("", config).ok(); let output = pipeline_data.collect_string("", config).ok();
match output { output.map(|mut x| {
Some(mut x) => { // Just remove the very last newline.
// Just remove the very last newline. if x.ends_with('\n') {
if x.ends_with('\n') { x.pop();
x.pop();
}
if x.ends_with('\r') {
x.pop();
}
Some(x)
} }
None => None,
} if x.ends_with('\r') {
x.pop();
}
x
})
}) })
} }
@ -120,12 +104,12 @@ pub(crate) fn update_prompt<'prompt>(
// Now that we have the prompt string lets ansify it. // Now that we have the prompt string lets ansify it.
// <133 A><prompt><133 B><command><133 C><command output> // <133 A><prompt><133 B><command><133 C><command output>
let left_prompt_string = if config.shell_integration { let left_prompt_string = if config.shell_integration {
match left_prompt_string { if let Some(prompt_string) = left_prompt_string {
Some(prompt_string) => Some(format!( Some(format!(
"{}{}{}", "{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
PRE_PROMPT_MARKER, prompt_string, POST_PROMPT_MARKER ))
)), } else {
None => left_prompt_string, left_prompt_string
} }
} else { } else {
left_prompt_string left_prompt_string
@ -157,7 +141,7 @@ pub(crate) fn update_prompt<'prompt>(
); );
let ret_val = nu_prompt as &dyn Prompt; let ret_val = nu_prompt as &dyn Prompt;
info!("update_prompt {}:{}:{}", file!(), line!(), column!()); trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
ret_val ret_val
} }

View File

@ -1,14 +1,13 @@
use super::DescriptionMenu; use super::DescriptionMenu;
use crate::{menus::NuMenuCompleter, NuHelpCompleter}; use crate::{menus::NuMenuCompleter, NuHelpCompleter};
use crossterm::event::{KeyCode, KeyModifiers}; use crossterm::event::{KeyCode, KeyModifiers};
use nu_color_config::lookup_ansi_color_style; use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::parse; use nu_parser::parse;
use nu_protocol::{ use nu_protocol::{
color_value_string, create_menus, create_menus,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData, extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, ShellError, Span, Value,
ShellError, Span, Value,
}; };
use reedline::{ use reedline::{
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
@ -110,7 +109,7 @@ pub(crate) fn add_menus(
}; };
let mut temp_stack = Stack::new(); let mut temp_stack = Stack::new();
let input = Value::nothing(Span::test_data()).into_pipeline_data(); let input = PipelineData::Empty;
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?; let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
if let PipelineData::Value(value, None) = res { if let PipelineData::Value(value, None) = res {
@ -159,14 +158,11 @@ macro_rules! add_style {
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => { ($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
$menu = match extract_value($name, $cols, $vals, $span) { $menu = match extract_value($name, $cols, $vals, $span) {
Ok(text) => { Ok(text) => {
let text = match text { let style = match text {
Value::String { val, .. } => val.clone(), Value::String { val, .. } => lookup_ansi_color_style(&val),
Value::Record { cols, vals, span } => { Value::Record { .. } => color_record_to_nustyle(&text),
color_value_string(span, cols, vals, $config).into_string("", $config) _ => lookup_ansi_color_style("green"),
}
_ => "green".to_string(),
}; };
let style = lookup_ansi_color_style(&text);
$f($menu, style) $f($menu, style)
} }
Err(_) => $menu, Err(_) => $menu,
@ -655,14 +651,15 @@ fn add_parsed_keybinding(
let pos1 = char_iter.next(); let pos1 = char_iter.next();
let pos2 = char_iter.next(); let pos2 = char_iter.next();
let char = match (pos1, pos2) { let char = if let (Some(char), None) = (pos1, pos2) {
(Some(char), None) => Ok(char), char
_ => Err(ShellError::UnsupportedConfigValue( } else {
return Err(ShellError::UnsupportedConfigValue(
"char_<CHAR: unicode codepoint>".to_string(), "char_<CHAR: unicode codepoint>".to_string(),
c.to_string(), c.to_string(),
keybinding.keycode.span()?, keybinding.keycode.span()?,
)), ));
}?; };
KeyCode::Char(char) KeyCode::Char(char)
} }
@ -683,10 +680,10 @@ fn add_parsed_keybinding(
let fn_num: u8 = c[1..] let fn_num: u8 = c[1..]
.parse() .parse()
.ok() .ok()
.filter(|num| matches!(num, 1..=12)) .filter(|num| matches!(num, 1..=20))
.ok_or(ShellError::UnsupportedConfigValue( .ok_or(ShellError::UnsupportedConfigValue(
"(f1|f2|...|f12)".to_string(), "(f1|f2|...|f20)".to_string(),
format!("unknown function key: {}", c), format!("unknown function key: {c}"),
keybinding.keycode.span()?, keybinding.keycode.span()?,
))?; ))?;
KeyCode::F(fn_num) KeyCode::F(fn_num)
@ -993,10 +990,7 @@ mod test {
#[test] #[test]
fn test_send_event() { fn test_send_event() {
let cols = vec!["send".to_string()]; let cols = vec!["send".to_string()];
let vals = vec![Value::String { let vals = vec![Value::test_string("Enter")];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let span = Span::test_data(); let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap(); let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
@ -1016,10 +1010,7 @@ mod test {
#[test] #[test]
fn test_edit_event() { fn test_edit_event() {
let cols = vec!["edit".to_string()]; let cols = vec!["edit".to_string()];
let vals = vec![Value::String { let vals = vec![Value::test_string("Clear")];
val: "Clear".to_string(),
span: Span::test_data(),
}];
let span = Span::test_data(); let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap(); let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
@ -1043,14 +1034,8 @@ mod test {
fn test_send_menu() { fn test_send_menu() {
let cols = vec!["send".to_string(), "name".to_string()]; let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![ let vals = vec![
Value::String { Value::test_string("Menu"),
val: "Menu".to_string(), Value::test_string("history_menu"),
span: Span::test_data(),
},
Value::String {
val: "history_menu".to_string(),
span: Span::test_data(),
},
]; ];
let span = Span::test_data(); let span = Span::test_data();
@ -1076,14 +1061,8 @@ mod test {
// Menu event // Menu event
let cols = vec!["send".to_string(), "name".to_string()]; let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![ let vals = vec![
Value::String { Value::test_string("Menu"),
val: "Menu".to_string(), Value::test_string("history_menu"),
span: Span::test_data(),
},
Value::String {
val: "history_menu".to_string(),
span: Span::test_data(),
},
]; ];
let menu_event = Value::Record { let menu_event = Value::Record {
@ -1094,10 +1073,7 @@ mod test {
// Enter event // Enter event
let cols = vec!["send".to_string()]; let cols = vec!["send".to_string()];
let vals = vec![Value::String { let vals = vec![Value::test_string("Enter")];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let enter_event = Value::Record { let enter_event = Value::Record {
cols, cols,
@ -1138,14 +1114,8 @@ mod test {
// Menu event // Menu event
let cols = vec!["send".to_string(), "name".to_string()]; let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![ let vals = vec![
Value::String { Value::test_string("Menu"),
val: "Menu".to_string(), Value::test_string("history_menu"),
span: Span::test_data(),
},
Value::String {
val: "history_menu".to_string(),
span: Span::test_data(),
},
]; ];
let menu_event = Value::Record { let menu_event = Value::Record {
@ -1156,10 +1126,7 @@ mod test {
// Enter event // Enter event
let cols = vec!["send".to_string()]; let cols = vec!["send".to_string()];
let vals = vec![Value::String { let vals = vec![Value::test_string("Enter")];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let enter_event = Value::Record { let enter_event = Value::Record {
cols, cols,
@ -1187,10 +1154,7 @@ mod test {
#[test] #[test]
fn test_error() { fn test_error() {
let cols = vec!["not_exist".to_string()]; let cols = vec!["not_exist".to_string()];
let vals = vec![Value::String { let vals = vec![Value::test_string("Enter")];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let span = Span::test_data(); let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span); let b = EventType::try_from_columns(&cols, &vals, &span);

View File

@ -5,20 +5,21 @@ use crate::{
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new}, util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
NuHighlighter, NuValidator, NushellPrompt, NuHighlighter, NuValidator, NushellPrompt,
}; };
use fancy_regex::Regex; use crossterm::cursor::CursorShape;
use lazy_static::lazy_static; use log::{trace, warn};
use log::{info, trace, warn};
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use nu_color_config::get_color_config; use nu_color_config::StyleComputer;
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return}; use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
use nu_parser::{lex, parse, trim_quotes_str}; use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::{ use nu_protocol::{
ast::PathMember, ast::PathMember,
config::NuCursorShape,
engine::{EngineState, ReplOperation, Stack, StateWorkingSet}, engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span, format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
Spanned, Type, Value, VarId, Spanned, Type, Value, VarId,
}; };
use reedline::{DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi}; use nu_utils::utils::perf;
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
use std::{ use std::{
io::{self, Write}, io::{self, Write},
sync::atomic::Ordering, sync::atomic::Ordering,
@ -41,6 +42,7 @@ pub fn evaluate_repl(
stack: &mut Stack, stack: &mut Stack,
nushell_path: &str, nushell_path: &str,
prerun_command: Option<Spanned<String>>, prerun_command: Option<Spanned<String>>,
entire_start_time: Instant,
) -> Result<()> { ) -> Result<()> {
use reedline::{FileBackedHistory, Reedline, Signal}; use reedline::{FileBackedHistory, Reedline, Signal};
@ -58,63 +60,47 @@ pub fn evaluate_repl(
let mut nu_prompt = NushellPrompt::new(); let mut nu_prompt = NushellPrompt::new();
info!( let start_time = std::time::Instant::now();
"translate environment vars {}:{}:{}",
file!(),
line!(),
column!()
);
// Translate environment variables from Strings to Values // Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) { if let Some(e) = convert_env_values(engine_state, stack) {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e); report_error(&working_set, &e);
} }
perf(
"translate env vars",
start_time,
file!(),
line!(),
column!(),
);
// seed env vars // seed env vars
stack.add_env_var( stack.add_env_var(
"CMD_DURATION_MS".into(), "CMD_DURATION_MS".into(),
Value::String { Value::string("0823", Span::unknown()),
val: "0823".to_string(),
span: Span { start: 0, end: 0 },
},
); );
stack.add_env_var( stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(0, Span::unknown()));
"LAST_EXIT_CODE".into(),
Value::Int {
val: 0,
span: Span { start: 0, end: 0 },
},
);
info!(
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
let mut start_time = std::time::Instant::now();
let mut line_editor = Reedline::create(); let mut line_editor = Reedline::create();
// Now that reedline is created, get the history session id and store it in engine_state // Now that reedline is created, get the history session id and store it in engine_state
let hist_sesh = match line_editor.get_history_session_id() { let hist_sesh = line_editor
Some(id) => i64::from(id), .get_history_session_id()
None => 0, .map(i64::from)
}; .unwrap_or(0);
engine_state.history_session_id = hist_sesh; engine_state.history_session_id = hist_sesh;
perf("setup reedline", start_time, file!(), line!(), column!());
let config = engine_state.get_config(); let config = engine_state.get_config();
start_time = std::time::Instant::now();
let history_path = crate::config_files::get_history_path( let history_path = crate::config_files::get_history_path(
nushell_path, nushell_path,
engine_state.config.history_file_format, engine_state.config.history_file_format,
); );
if let Some(history_path) = history_path.as_deref() { if let Some(history_path) = history_path.as_deref() {
info!("setup history {}:{}:{}", file!(), line!(), column!());
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format { let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
HistoryFileFormat::PlainText => Box::new( HistoryFileFormat::PlainText => Box::new(
FileBackedHistory::with_file( FileBackedHistory::with_file(
@ -129,7 +115,9 @@ pub fn evaluate_repl(
}; };
line_editor = line_editor.with_history(history); line_editor = line_editor.with_history(history);
}; };
perf("setup history", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let sys = sysinfo::System::new(); let sys = sysinfo::System::new();
let show_banner = config.show_banner; let show_banner = config.show_banner;
@ -137,63 +125,89 @@ pub fn evaluate_repl(
if show_banner { if show_banner {
let banner = get_banner(engine_state, stack); let banner = get_banner(engine_state, stack);
if use_ansi { if use_ansi {
println!("{}", banner); println!("{banner}");
} else { } else {
println!("{}", nu_utils::strip_ansi_string_likely(banner)); println!("{}", nu_utils::strip_ansi_string_likely(banner));
} }
} }
perf(
"get sysinfo/show banner",
start_time,
file!(),
line!(),
column!(),
);
if let Some(s) = prerun_command { if let Some(s) = prerun_command {
eval_source( eval_source(
engine_state, engine_state,
stack, stack,
s.item.as_bytes(), s.item.as_bytes(),
&format!("entry #{}", entry_num), &format!("entry #{entry_num}"),
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?; engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
} }
loop { loop {
info!( let loop_start_time = std::time::Instant::now();
"load config each loop {}:{}:{}",
file!(),
line!(),
column!()
);
let cwd = get_guaranteed_cwd(engine_state, stack); let cwd = get_guaranteed_cwd(engine_state, stack);
start_time = std::time::Instant::now();
// Before doing anything, merge the environment from the previous REPL iteration into the // Before doing anything, merge the environment from the previous REPL iteration into the
// permanent state. // permanent state.
if let Err(err) = engine_state.merge_env(stack, cwd) { if let Err(err) = engine_state.merge_env(stack, cwd) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
perf("merge env", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
//Reset the ctrl-c handler //Reset the ctrl-c handler
if let Some(ctrlc) = &mut engine_state.ctrlc { if let Some(ctrlc) = &mut engine_state.ctrlc {
ctrlc.store(false, Ordering::SeqCst); ctrlc.store(false, Ordering::SeqCst);
} }
perf("reset ctrlc", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
// Reset the SIGQUIT handler // Reset the SIGQUIT handler
if let Some(sig_quit) = engine_state.get_sig_quit() { if let Some(sig_quit) = engine_state.get_sig_quit() {
sig_quit.store(false, Ordering::SeqCst); sig_quit.store(false, Ordering::SeqCst);
} }
perf("reset sig_quit", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let config = engine_state.get_config(); let config = engine_state.get_config();
info!("setup colors {}:{}:{}", file!(), line!(), column!());
let color_hm = get_color_config(config);
info!("update reedline {}:{}:{}", file!(), line!(), column!());
let engine_reference = std::sync::Arc::new(engine_state.clone()); let engine_reference = std::sync::Arc::new(engine_state.clone());
// Find the configured cursor shapes for each mode
let cursor_config = CursorConfig {
vi_insert: Some(map_nucursorshape_to_cursorshape(
config.cursor_shape_vi_insert,
)),
vi_normal: Some(map_nucursorshape_to_cursorshape(
config.cursor_shape_vi_normal,
)),
emacs: Some(map_nucursorshape_to_cursorshape(config.cursor_shape_emacs)),
};
perf(
"get config/cursor config",
start_time,
file!(),
line!(),
column!(),
);
start_time = std::time::Instant::now();
line_editor = line_editor line_editor = line_editor
.with_highlighter(Box::new(NuHighlighter { .with_highlighter(Box::new(NuHighlighter {
engine_state: engine_state.clone(), engine_state: engine_reference.clone(),
config: config.clone(), config: config.clone(),
})) }))
.with_validator(Box::new(NuValidator { .with_validator(Box::new(NuValidator {
engine_state: engine_state.clone(), engine_state: engine_reference.clone(),
})) }))
.with_completer(Box::new(NuCompleter::new( .with_completer(Box::new(NuCompleter::new(
engine_reference.clone(), engine_reference.clone(),
@ -201,25 +215,39 @@ pub fn evaluate_repl(
))) )))
.with_quick_completions(config.quick_completions) .with_quick_completions(config.quick_completions)
.with_partial_completions(config.partial_completions) .with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring); .with_ansi_colors(config.use_ansi_coloring)
.with_cursor_config(cursor_config);
perf("reedline builder", start_time, file!(), line!(), column!());
let style_computer = StyleComputer::from_config(engine_state, stack);
start_time = std::time::Instant::now();
line_editor = if config.use_ansi_coloring { line_editor = if config.use_ansi_coloring {
line_editor.with_hinter(Box::new( line_editor.with_hinter(Box::new({
DefaultHinter::default().with_style(color_hm["hints"]), // As of Nov 2022, "hints" color_config closures only get `null` passed in.
)) let style = style_computer.compute("hints", &Value::nothing(Span::unknown()));
DefaultHinter::default().with_style(style)
}))
} else { } else {
line_editor.disable_hints() line_editor.disable_hints()
}; };
perf(
"reedline coloring/style_computer",
start_time,
file!(),
line!(),
column!(),
);
line_editor = match add_menus(line_editor, engine_reference, stack, config) { start_time = std::time::Instant::now();
Ok(line_editor) => line_editor, line_editor = add_menus(line_editor, engine_reference, stack, config).unwrap_or_else(|e| {
Err(e) => { let working_set = StateWorkingSet::new(engine_state);
let working_set = StateWorkingSet::new(engine_state); report_error(&working_set, &e);
report_error(&working_set, &e); Reedline::create()
Reedline::create() });
} perf("reedline menus", start_time, file!(), line!(), column!());
};
start_time = std::time::Instant::now();
let buffer_editor = if !config.buffer_editor.is_empty() { let buffer_editor = if !config.buffer_editor.is_empty() {
Some(config.buffer_editor.clone()) Some(config.buffer_editor.clone())
} else { } else {
@ -240,17 +268,23 @@ pub fn evaluate_repl(
} else { } else {
line_editor line_editor
}; };
perf(
"reedline buffer_editor",
start_time,
file!(),
line!(),
column!(),
);
start_time = std::time::Instant::now();
if config.sync_history_on_enter { if config.sync_history_on_enter {
info!("sync history {}:{}:{}", file!(), line!(), column!());
if let Err(e) = line_editor.sync_history() { if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {}", e); warn!("Failed to sync history: {}", e);
} }
} }
perf("sync_history", start_time, file!(), line!(), column!());
info!("setup keybindings {}:{}:{}", file!(), line!(), column!()); start_time = std::time::Instant::now();
// Changing the line editor based on the found keybindings // Changing the line editor based on the found keybindings
line_editor = match create_keybindings(config) { line_editor = match create_keybindings(config) {
Ok(keybindings) => match keybindings { Ok(keybindings) => match keybindings {
@ -272,9 +306,9 @@ pub fn evaluate_repl(
line_editor line_editor
} }
}; };
perf("keybindings", start_time, file!(), line!(), column!());
info!("prompt_update {}:{}:{}", file!(), line!(), column!()); start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user, // Right before we start our prompt and take input from the user,
// fire the "pre_prompt" hook // fire the "pre_prompt" hook
if let Some(hook) = config.hooks.pre_prompt.clone() { if let Some(hook) = config.hooks.pre_prompt.clone() {
@ -282,7 +316,9 @@ pub fn evaluate_repl(
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
} }
perf("pre-prompt hook", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for // Next, check all the environment variables they ask for
// fire the "env_change" hook // fire the "env_change" hook
let config = engine_state.get_config(); let config = engine_state.get_config();
@ -291,19 +327,23 @@ pub fn evaluate_repl(
{ {
report_error_new(engine_state, &error) report_error_new(engine_state, &error)
} }
perf("env-change hook", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let config = engine_state.get_config(); let config = engine_state.get_config();
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt); let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
perf("update_prompt", start_time, file!(), line!(), column!());
entry_num += 1; entry_num += 1;
info!( if entry_num == 1 && show_banner {
"finished setup, starting repl {}:{}:{}", println!(
file!(), "Startup Time: {}",
line!(), format_duration(entire_start_time.elapsed().as_nanos() as i64)
column!() );
); }
start_time = std::time::Instant::now();
let input = line_editor.read_line(prompt); let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration; let shell_integration = config.shell_integration;
@ -375,7 +415,7 @@ pub fn evaluate_repl(
"OLDPWD".into(), "OLDPWD".into(),
Value::String { Value::String {
val: cwd.clone(), val: cwd.clone(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -385,7 +425,7 @@ pub fn evaluate_repl(
"PWD".into(), "PWD".into(),
Value::String { Value::String {
val: path.clone(), val: path.clone(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
let cwd = Value::String { val: cwd, span }; let cwd = Value::String { val: cwd, span };
@ -430,8 +470,8 @@ pub fn evaluate_repl(
engine_state, engine_state,
stack, stack,
s.as_bytes(), s.as_bytes(),
&format!("entry #{}", entry_num), &format!("entry #{entry_num}"),
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
} }
let cmd_duration = start_time.elapsed(); let cmd_duration = start_time.elapsed();
@ -440,7 +480,7 @@ pub fn evaluate_repl(
"CMD_DURATION_MS".into(), "CMD_DURATION_MS".into(),
Value::String { Value::String {
val: format!("{}", cmd_duration.as_millis()), val: format!("{}", cmd_duration.as_millis()),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -488,7 +528,7 @@ pub fn evaluate_repl(
// ESC]0;stringBEL -- Set icon name and window title to string // ESC]0;stringBEL -- Set icon name and window title to string
// ESC]1;stringBEL -- Set icon name to string // ESC]1;stringBEL -- Set icon name to string
// ESC]2;stringBEL -- Set window title to string // ESC]2;stringBEL -- Set window title to string
run_ansi_sequence(&format!("\x1b]2;{}\x07", maybe_abbrev_path))?; run_ansi_sequence(&format!("\x1b]2;{maybe_abbrev_path}\x07"))?;
} }
run_ansi_sequence(RESET_APPLICATION_MODE)?; run_ansi_sequence(RESET_APPLICATION_MODE)?;
} }
@ -528,7 +568,7 @@ pub fn evaluate_repl(
Err(err) => { Err(err) => {
let message = err.to_string(); let message = err.to_string();
if !message.contains("duration") { if !message.contains("duration") {
eprintln!("Error: {:?}", err); eprintln!("Error: {err:?}");
// TODO: Identify possible error cases where a hard failure is preferable // TODO: Identify possible error cases where a hard failure is preferable
// Ignoring and reporting could hide bigger problems // Ignoring and reporting could hide bigger problems
// e.g. https://github.com/nushell/nushell/issues/6452 // e.g. https://github.com/nushell/nushell/issues/6452
@ -539,11 +579,34 @@ pub fn evaluate_repl(
} }
} }
} }
perf(
"processing line editor input",
start_time,
file!(),
line!(),
column!(),
);
perf(
"finished repl loop",
loop_start_time,
file!(),
line!(),
column!(),
);
} }
Ok(()) Ok(())
} }
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> CursorShape {
match shape {
NuCursorShape::Block => CursorShape::Block,
NuCursorShape::UnderScore => CursorShape::UnderScore,
NuCursorShape::Line => CursorShape::Line,
}
}
fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String { fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
let age = match eval_string_with_input( let age = match eval_string_with_input(
engine_state, engine_state,
@ -567,15 +630,7 @@ Our {}Documentation{} is located at {}http://nushell.sh{}
{}Tweet{} us at {}@nu_shell{} {}Tweet{} us at {}@nu_shell{}
It's been this long since {}Nushell{}'s first commit: It's been this long since {}Nushell{}'s first commit:
{} {}{}
{}You can disable this banner using the {}config nu{}{} command
to modify the config.nu file and setting show_banner to false.
let-env config = {{
show_banner: false
...
}}{}
"#, "#,
"\x1b[32m", //start line 1 green "\x1b[32m", //start line 1 green
"\x1b[32m", //start line 2 "\x1b[32m", //start line 2
@ -607,11 +662,7 @@ let-env config = {{
"\x1b[32m", //before Nushell "\x1b[32m", //before Nushell
"\x1b[0m", //after Nushell "\x1b[0m", //after Nushell
age, age,
"\x1b[2;37m", //before banner disable dim white "\x1b[0m", //after banner disable
"\x1b[2;36m", //before config nu dim cyan
"\x1b[0m", //after config nu
"\x1b[2;37m", //after config nu dim white
"\x1b[0m", //after banner disable
); );
banner banner
@ -637,7 +688,7 @@ pub fn eval_string_with_input(
let input_as_pipeline_data = match input { let input_as_pipeline_data = match input {
Some(input) => PipelineData::Value(input, None), Some(input) => PipelineData::Value(input, None),
None => PipelineData::new(Span::test_data()), None => PipelineData::empty(),
}; };
eval_block( eval_block(
@ -648,7 +699,7 @@ pub fn eval_string_with_input(
false, false,
true, true,
) )
.map(|x| x.into_value(Span::test_data())) .map(|x| x.into_value(Span::unknown()))
} }
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String { pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
@ -718,11 +769,18 @@ pub fn eval_hook(
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let value_span = value.span()?; let value_span = value.span()?;
// Hooks can optionally be a record in this form:
// {
// condition: {|before, after| ... } # block that evaluates to true/false
// code: # block or a string
// }
// The condition block will be run to check whether the main hook (in `code`) should be run.
// If it returns true (the default if a condition block is not specified), the hook should be run.
let condition_path = PathMember::String { let condition_path = PathMember::String {
val: "condition".to_string(), val: "condition".to_string(),
span: value_span, span: value_span,
}; };
let mut output = PipelineData::new(Span::new(0, 0)); let mut output = PipelineData::empty();
let code_path = PathMember::String { let code_path = PathMember::String {
val: "code".to_string(), val: "code".to_string(),
@ -736,57 +794,63 @@ pub fn eval_hook(
} }
} }
Value::Record { .. } => { Value::Record { .. } => {
let do_run_hook = let do_run_hook = if let Ok(condition) =
if let Ok(condition) = value.clone().follow_cell_path(&[condition_path], false) { value
match condition { .clone()
Value::Block { .follow_cell_path(&[condition_path], false, false)
val: block_id, {
span: block_span, match condition {
.. Value::Block {
} val: block_id,
| Value::Closure { span: block_span,
val: block_id, ..
span: block_span, }
.. | Value::Closure {
} => { val: block_id,
match run_hook_block( span: block_span,
engine_state, ..
stack, } => {
block_id, match run_hook_block(
None, engine_state,
arguments.clone(), stack,
block_span, block_id,
) { None,
Ok(value) => match value { arguments.clone(),
Value::Bool { val, .. } => val, block_span,
other => { ) {
return Err(ShellError::UnsupportedConfigValue( Ok(pipeline_data) => {
"boolean output".to_string(), if let PipelineData::Value(Value::Bool { val, .. }, ..) =
format!("{}", other.get_type()), pipeline_data
other.span()?, {
)); val
} } else {
}, return Err(ShellError::UnsupportedConfigValue(
Err(err) => { "boolean output".to_string(),
return Err(err); "other PipelineData variant".to_string(),
block_span,
));
} }
} }
} Err(err) => {
other => { return Err(err);
return Err(ShellError::UnsupportedConfigValue( }
"block".to_string(),
format!("{}", other.get_type()),
other.span()?,
));
} }
} }
} else { other => {
// always run the hook return Err(ShellError::UnsupportedConfigValue(
true "block".to_string(),
}; format!("{}", other.get_type()),
other.span()?,
));
}
}
} else {
// always run the hook
true
};
if do_run_hook { if do_run_hook {
match value.clone().follow_cell_path(&[code_path], false)? { match value.clone().follow_cell_path(&[code_path], false, false)? {
Value::String { Value::String {
val, val,
span: source_span, span: source_span,
@ -823,7 +887,7 @@ pub fn eval_hook(
}; };
engine_state.merge_delta(delta)?; engine_state.merge_delta(delta)?;
let input = PipelineData::new(value_span); let input = PipelineData::empty();
let var_ids: Vec<VarId> = vars let var_ids: Vec<VarId> = vars
.into_iter() .into_iter()
@ -889,34 +953,28 @@ pub fn eval_hook(
span: block_span, span: block_span,
.. ..
} => { } => {
output = PipelineData::Value( output = run_hook_block(
run_hook_block( engine_state,
engine_state, stack,
stack, *block_id,
*block_id, input,
input, arguments,
arguments, *block_span,
*block_span, )?;
)?,
None,
);
} }
Value::Closure { Value::Closure {
val: block_id, val: block_id,
span: block_span, span: block_span,
.. ..
} => { } => {
output = PipelineData::Value( output = run_hook_block(
run_hook_block( engine_state,
engine_state, stack,
stack, *block_id,
*block_id, input,
input, arguments,
arguments, *block_span,
*block_span, )?;
)?,
None,
);
} }
other => { other => {
return Err(ShellError::UnsupportedConfigValue( return Err(ShellError::UnsupportedConfigValue(
@ -933,17 +991,17 @@ pub fn eval_hook(
Ok(output) Ok(output)
} }
pub fn run_hook_block( fn run_hook_block(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
block_id: BlockId, block_id: BlockId,
optional_input: Option<PipelineData>, optional_input: Option<PipelineData>,
arguments: Vec<(String, Value)>, arguments: Vec<(String, Value)>,
span: Span, span: Span,
) -> Result<Value, ShellError> { ) -> Result<PipelineData, ShellError> {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
let input = optional_input.unwrap_or_else(|| PipelineData::new(span)); let input = optional_input.unwrap_or_else(PipelineData::empty);
let mut callee_stack = stack.gather_captures(&block.captures); let mut callee_stack = stack.gather_captures(&block.captures);
@ -962,63 +1020,58 @@ pub fn run_hook_block(
} }
} }
match eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false) let pipeline_data =
{ eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)?;
Ok(pipeline_data) => match pipeline_data.into_value(span) {
Value::Error { error } => Err(error),
val => {
// If all went fine, preserve the environment of the called block
let caller_env_vars = stack.get_env_var_names(engine_state);
// remove env vars that are present in the caller but not in the callee if let PipelineData::Value(Value::Error { error }, _) = pipeline_data {
// (the callee hid them) return Err(error);
for var in caller_env_vars.iter() {
if !callee_stack.has_env_var(engine_state, var) {
stack.remove_env_var(engine_state, var);
}
}
// add new env vars from callee to caller
for (var, value) in callee_stack.get_stack_env_vars() {
stack.add_env_var(var, value);
}
Ok(val)
}
},
Err(err) => Err(err),
} }
// If all went fine, preserve the environment of the called block
let caller_env_vars = stack.get_env_var_names(engine_state);
// remove env vars that are present in the caller but not in the callee
// (the callee hid them)
for var in caller_env_vars.iter() {
if !callee_stack.has_env_var(engine_state, var) {
stack.remove_env_var(engine_state, var);
}
}
// add new env vars from callee to caller
for (var, value) in callee_stack.get_stack_env_vars() {
stack.add_env_var(var, value);
}
Ok(pipeline_data)
} }
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> { fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
match io::stdout().write_all(seq.as_bytes()) { io::stdout().write_all(seq.as_bytes()).map_err(|e| {
Ok(it) => it, ShellError::GenericError(
Err(err) => { "Error writing ansi sequence".into(),
return Err(ShellError::GenericError( e.to_string(),
"Error writing ansi sequence".into(), Some(Span::unknown()),
err.to_string(), None,
Some(Span { start: 0, end: 0 }), Vec::new(),
None, )
Vec::new(), })?;
));
}
};
io::stdout().flush().map_err(|e| { io::stdout().flush().map_err(|e| {
ShellError::GenericError( ShellError::GenericError(
"Error flushing stdio".into(), "Error flushing stdio".into(),
e.to_string(), e.to_string(),
Some(Span { start: 0, end: 0 }), Some(Span::unknown()),
None, None,
Vec::new(), Vec::new(),
) )
}) })
} }
lazy_static! { // Absolute paths with a drive letter, like 'C:', 'D:\', 'E:\foo'
// Absolute paths with a drive letter, like 'C:', 'D:\', 'E:\foo' #[cfg(windows)]
static ref DRIVE_PATH_REGEX: Regex = static DRIVE_PATH_REGEX: once_cell::sync::Lazy<fancy_regex::Regex> =
Regex::new(r"^[a-zA-Z]:[/\\]?").expect("Internal error: regex creation"); once_cell::sync::Lazy::new(|| {
} fancy_regex::Regex::new(r"^[a-zA-Z]:[/\\]?").expect("Internal error: regex creation")
});
// A best-effort "does this string look kinda like a path?" function to determine whether to auto-cd // A best-effort "does this string look kinda like a path?" function to determine whether to auto-cd
fn looks_like_path(orig: &str) -> bool { fn looks_like_path(orig: &str) -> bool {

View File

@ -6,9 +6,10 @@ use nu_protocol::ast::{Argument, Block, Expr, Expression, PipelineElement};
use nu_protocol::engine::{EngineState, StateWorkingSet}; use nu_protocol::engine::{EngineState, StateWorkingSet};
use nu_protocol::{Config, Span}; use nu_protocol::{Config, Span};
use reedline::{Highlighter, StyledText}; use reedline::{Highlighter, StyledText};
use std::sync::Arc;
pub struct NuHighlighter { pub struct NuHighlighter {
pub engine_state: EngineState, pub engine_state: Arc<EngineState>,
pub config: Config, pub config: Config,
} }
@ -149,7 +150,7 @@ fn split_span_by_highlight_positions(
for pos in highlight_positions { for pos in highlight_positions {
if start <= *pos && pos < &span.end { if start <= *pos && pos < &span.end {
if start < *pos { if start < *pos {
result.push((Span { start, end: *pos }, false)); result.push((Span::new(start, *pos), false));
} }
let span_str = &line[pos - global_span_offset..span.end - global_span_offset]; let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
let end = span_str let end = span_str
@ -157,18 +158,12 @@ fn split_span_by_highlight_positions(
.next() .next()
.map(|c| pos + get_char_length(c)) .map(|c| pos + get_char_length(c))
.unwrap_or(pos + 1); .unwrap_or(pos + 1);
result.push((Span { start: *pos, end }, true)); result.push((Span::new(*pos, end), true));
start = end; start = end;
} }
} }
if start < span.end { if start < span.end {
result.push(( result.push((Span::new(start, span.end), false));
Span {
start,
end: span.end,
},
false,
));
} }
result result
} }
@ -239,7 +234,8 @@ fn find_matching_block_end_in_block(
PipelineElement::Expression(_, e) PipelineElement::Expression(_, e)
| PipelineElement::Redirection(_, _, e) | PipelineElement::Redirection(_, _, e)
| PipelineElement::And(_, e) | PipelineElement::And(_, e)
| PipelineElement::Or(_, e) => { | PipelineElement::Or(_, e)
| PipelineElement::SeparateRedirection { out: (_, e), .. } => {
if e.span.contains(global_cursor_offset) { if e.span.contains(global_cursor_offset) {
if let Some(pos) = find_matching_block_end_in_expr( if let Some(pos) = find_matching_block_end_in_expr(
line, line,
@ -358,6 +354,7 @@ fn find_matching_block_end_in_expr(
let opt_expr = match arg { let opt_expr = match arg {
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(), Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
Argument::Positional(inner_expr) => Some(inner_expr), Argument::Positional(inner_expr) => Some(inner_expr),
Argument::Unknown(inner_expr) => Some(inner_expr),
}; };
if let Some(inner_expr) = opt_expr { if let Some(inner_expr) = opt_expr {

View File

@ -9,6 +9,7 @@ use nu_protocol::{
}; };
#[cfg(windows)] #[cfg(windows)]
use nu_utils::enable_vt_processing; use nu_utils::enable_vt_processing;
use nu_utils::utils::perf;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
// This will collect environment variables from std::env and adds them to a stack. // This will collect environment variables from std::env and adds them to a stack.
@ -43,7 +44,7 @@ fn gather_env_vars(
report_error( report_error(
&working_set, &working_set,
&ShellError::GenericError( &ShellError::GenericError(
format!("Environment variable was not captured: {}", env_str), format!("Environment variable was not captured: {env_str}"),
"".to_string(), "".to_string(),
None, None,
Some(msg.into()), Some(msg.into()),
@ -79,8 +80,7 @@ fn gather_env_vars(
"".to_string(), "".to_string(),
None, None,
Some(format!( Some(format!(
"Retrieving current directory failed: {:?} not a valid utf-8 path", "Retrieving current directory failed: {init_cwd:?} not a valid utf-8 path"
init_cwd
)), )),
Vec::new(), Vec::new(),
), ),
@ -204,6 +204,8 @@ pub fn eval_source(
fname: &str, fname: &str,
input: PipelineData, input: PipelineData,
) -> bool { ) -> bool {
let start_time = std::time::Instant::now();
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse( let (output, err) = parse(
@ -250,7 +252,7 @@ pub fn eval_source(
} }
} }
} else { } else {
result = pipeline_data.print(engine_state, stack, false, false); result = pipeline_data.print(engine_state, stack, true, false);
} }
match result { match result {
@ -282,6 +284,13 @@ pub fn eval_source(
return false; return false;
} }
} }
perf(
&format!("eval_source {}", &fname),
start_time,
file!(),
line!(),
column!(),
);
true true
} }
@ -289,10 +298,7 @@ pub fn eval_source(
fn set_last_exit_code(stack: &mut Stack, exit_code: i64) { fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
stack.add_env_var( stack.add_env_var(
"LAST_EXIT_CODE".to_string(), "LAST_EXIT_CODE".to_string(),
Value::Int { Value::int(exit_code, Span::unknown()),
val: exit_code,
span: Span { start: 0, end: 0 },
},
); );
} }
@ -318,27 +324,19 @@ pub fn report_error_new(
} }
pub fn get_init_cwd() -> PathBuf { pub fn get_init_cwd() -> PathBuf {
match std::env::current_dir() { std::env::current_dir().unwrap_or_else(|_| {
Ok(cwd) => cwd, std::env::var("PWD")
Err(_) => match std::env::var("PWD") { .map(Into::into)
Ok(cwd) => PathBuf::from(cwd), .unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
Err(_) => match nu_path::home_dir() { })
Some(cwd) => cwd,
None => PathBuf::new(),
},
},
}
} }
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf { pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
match nu_engine::env::current_dir(engine_state, stack) { nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
Ok(p) => p, let working_set = StateWorkingSet::new(engine_state);
Err(e) => { report_error(&working_set, &e);
let working_set = StateWorkingSet::new(engine_state); get_init_cwd()
report_error(&working_set, &e); })
get_init_cwd()
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,9 +1,10 @@
use nu_parser::{parse, ParseError}; use nu_parser::{parse, ParseError};
use nu_protocol::engine::{EngineState, StateWorkingSet}; use nu_protocol::engine::{EngineState, StateWorkingSet};
use reedline::{ValidationResult, Validator}; use reedline::{ValidationResult, Validator};
use std::sync::Arc;
pub struct NuValidator { pub struct NuValidator {
pub engine_state: EngineState, pub engine_state: Arc<EngineState>,
} }
impl Validator for NuValidator { impl Validator for NuValidator {

View File

@ -5,7 +5,7 @@ use nu_parser::parse;
use nu_protocol::engine::StateWorkingSet; use nu_protocol::engine::StateWorkingSet;
use reedline::{Completer, Suggestion}; use reedline::{Completer, Suggestion};
use rstest::{fixture, rstest}; use rstest::{fixture, rstest};
use support::{file, folder, match_suggestions, new_engine}; use support::{completions_helpers::new_quote_engine, file, folder, match_suggestions, new_engine};
#[fixture] #[fixture]
fn completer() -> NuCompleter { fn completer() -> NuCompleter {
@ -34,6 +34,26 @@ fn completer_strings() -> NuCompleter {
NuCompleter::new(std::sync::Arc::new(engine), stack) NuCompleter::new(std::sync::Arc::new(engine), stack)
} }
#[fixture]
fn extern_completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = r#"
def animals [] { [ "cat", "dog", "eel" ] }
extern spam [
animal: string@animals
--foo (-f): string@animals
-b: string@animals
]
"#;
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(std::sync::Arc::new(engine), stack)
}
#[test] #[test]
fn variables_dollar_sign_with_varialblecompletion() { fn variables_dollar_sign_with_varialblecompletion() {
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_engine();
@ -89,7 +109,7 @@ fn dotnu_completions() {
// Create a new engine // Create a new engine
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_engine();
// Instatiate a new completer // Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack); let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test source completion // Test source completion
@ -149,11 +169,11 @@ fn file_completions() {
// Create a new engine // Create a new engine
let (dir, dir_str, engine, stack) = new_engine(); let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer // Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack); let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder // Test completions for the current folder
let target_dir = format!("cp {}", dir_str); let target_dir = format!("cp {dir_str}");
let suggestions = completer.complete(&target_dir, target_dir.len()); let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values // Create the expected values
@ -411,17 +431,36 @@ fn command_watch_with_filecompletion() {
match_suggestions(expected_paths, suggestions) match_suggestions(expected_paths, suggestions)
} }
#[test]
fn file_completion_quoted() {
let (_, _, engine, stack) = new_quote_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "open ";
let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec![
"`te st.txt`".to_string(),
"`te#st.txt`".to_string(),
"`te'st.txt`".to_string(),
"`te(st).txt`".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test] #[test]
fn flag_completions() { fn flag_completions() {
// Create a new engine // Create a new engine
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_engine();
// Instatiate a new completer // Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack); let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the 'ls' flags // Test completions for the 'ls' flags
let suggestions = completer.complete("ls -", 4); let suggestions = completer.complete("ls -", 4);
assert_eq!(14, suggestions.len()); assert_eq!(16, suggestions.len());
let expected: Vec<String> = vec![ let expected: Vec<String> = vec![
"--all".into(), "--all".into(),
@ -430,6 +469,7 @@ fn flag_completions() {
"--full-paths".into(), "--full-paths".into(),
"--help".into(), "--help".into(),
"--long".into(), "--long".into(),
"--mime-type".into(),
"--short-names".into(), "--short-names".into(),
"-D".into(), "-D".into(),
"-a".into(), "-a".into(),
@ -437,6 +477,7 @@ fn flag_completions() {
"-f".into(), "-f".into(),
"-h".into(), "-h".into(),
"-l".into(), "-l".into(),
"-m".into(),
"-s".into(), "-s".into(),
]; ];
@ -449,11 +490,11 @@ fn folder_with_directorycompletions() {
// Create a new engine // Create a new engine
let (dir, dir_str, engine, stack) = new_engine(); let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer // Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack); let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder // Test completions for the current folder
let target_dir = format!("cd {}", dir_str); let target_dir = format!("cd {dir_str}");
let suggestions = completer.complete(&target_dir, target_dir.len()); let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values // Create the expected values
@ -477,7 +518,7 @@ fn variables_completions() {
let record = "let actor = { name: 'Tom Hardy', age: 44 }"; let record = "let actor = { name: 'Tom Hardy', age: 44 }";
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok()); assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instatiate a new completer // Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack); let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for $nu // Test completions for $nu
@ -634,7 +675,7 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
config.external_completer = Some(latest_block_id); config.external_completer = Some(latest_block_id);
engine_state.set_config(&config); engine_state.set_config(&config);
// Instatiate a new completer // Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack); let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
completer.complete(input, input.len()) completer.complete(input, input.len())
@ -732,3 +773,83 @@ fn filecompletions_triggers_after_cursor() {
match_suggestions(expected_paths, suggestions); match_suggestions(expected_paths, suggestions);
} }
#[rstest]
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam ", 5);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo=", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -f ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -b ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_complete_flags(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -", 6);
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn alias_offset_bug_7748() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ea = ^$env.EDITOR /tmp/test.s"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Issue #7748
// Nushell crashes when an alias name is shorter than the alias command
// and the alias command is a external command
// This happens because of offset is not correct.
// This crashes before PR #7779
let _suggestions = completer.complete("e", 1);
//println!(" --------- suggestions: {:?}", suggestions);
}
#[rstest]
fn alias_offset_bug_7754() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls -l"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Issue #7754
// Nushell crashes when an alias name is shorter than the alias command
// and the alias command contains pipes.
// This crashes before PR #7756
let _suggestions = completer.complete("ll -a | c", 9);
//println!(" --------- suggestions: {:?}", suggestions);
}

View File

@ -33,20 +33,53 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
"PWD".to_string(), "PWD".to_string(),
Value::String { Value::String {
val: dir_str.clone(), val: dir_str.clone(),
span: nu_protocol::Span { span: nu_protocol::Span::new(0, dir_str.len()),
start: 0,
end: dir_str.len(),
},
}, },
); );
stack.add_env_var( stack.add_env_var(
"TEST".to_string(), "TEST".to_string(),
Value::String { Value::String {
val: "NUSHELL".to_string(), val: "NUSHELL".to_string(),
span: nu_protocol::Span { span: nu_protocol::Span::new(0, dir_str.len()),
start: 0, },
end: dir_str.len(), );
},
// Merge environment into the permanent state
let merge_result = engine_state.merge_env(&mut stack, &dir);
assert!(merge_result.is_ok());
(dir, dir_str, engine_state, stack)
}
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("quoted_completions");
let mut dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
dir_str.push(SEP);
// Create a new engine with default context
let mut engine_state = create_default_context();
// New stack
let mut stack = Stack::new();
// Add pwd as env var
stack.add_env_var(
"PWD".to_string(),
Value::String {
val: dir_str.clone(),
span: nu_protocol::Span::new(0, dir_str.len()),
},
);
stack.add_env_var(
"TEST".to_string(),
Value::String {
val: "NUSHELL".to_string(),
span: nu_protocol::Span::new(0, dir_str.len()),
}, },
); );
@ -112,7 +145,7 @@ pub fn merge_input(
&block, &block,
PipelineData::Value( PipelineData::Value(
Value::Nothing { Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
None None
), ),

View File

@ -5,11 +5,18 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-color-config" name = "nu-color-config"
version = "0.72.1" version = "0.75.0"
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.72.1" }
nu-ansi-term = "0.46.0"
nu-json = { path = "../nu-json", version = "0.72.1" }
nu-table = { path = "../nu-table", version = "0.72.1" }
serde = { version="1.0.123", features=["derive"] } serde = { version="1.0.123", features=["derive"] }
# used only for text_style Alignments
tabled = { version = "0.10.0", features = ["color"], default-features = false }
nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
nu-ansi-term = "0.46.0"
nu-utils = { path = "../nu-utils", version = "0.75.0" }
nu-engine = { path = "../nu-engine", version = "0.75.0" }
nu-json = { path="../nu-json", version = "0.75.0" }
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.75.0" }

View File

@ -1,413 +1,89 @@
use crate::nu_style::{color_from_hex, color_string_to_nustyle}; use crate::{
use nu_ansi_term::{Color, Style}; nu_style::{color_from_hex, lookup_style},
use nu_protocol::Config; parse_nustyle, NuStyle,
use nu_table::{Alignment, TextStyle}; };
use nu_ansi_term::Style;
use nu_protocol::Value;
use std::collections::HashMap; use std::collections::HashMap;
pub fn lookup_ansi_color_style(s: &str) -> Style { pub fn lookup_ansi_color_style(s: &str) -> Style {
if s.starts_with('#') { if s.starts_with('#') {
match color_from_hex(s) { color_from_hex(s)
Ok(c) => match c { .ok()
Some(c) => c.normal(), .and_then(|c| c.map(|c| c.normal()))
None => Style::default(), .unwrap_or_default()
},
Err(_) => Style::default(),
}
} else if s.starts_with('{') { } else if s.starts_with('{') {
color_string_to_nustyle(s.to_string()) color_string_to_nustyle(s.to_string())
} else { } else {
match s { lookup_style(s)
"g" | "green" => Color::Green.normal(),
"gb" | "green_bold" => Color::Green.bold(),
"gu" | "green_underline" => Color::Green.underline(),
"gi" | "green_italic" => Color::Green.italic(),
"gd" | "green_dimmed" => Color::Green.dimmed(),
"gr" | "green_reverse" => Color::Green.reverse(),
"gbl" | "green_blink" => Color::Green.blink(),
"gst" | "green_strike" => Color::Green.strikethrough(),
"lg" | "light_green" => Color::LightGreen.normal(),
"lgb" | "light_green_bold" => Color::LightGreen.bold(),
"lgu" | "light_green_underline" => Color::LightGreen.underline(),
"lgi" | "light_green_italic" => Color::LightGreen.italic(),
"lgd" | "light_green_dimmed" => Color::LightGreen.dimmed(),
"lgr" | "light_green_reverse" => Color::LightGreen.reverse(),
"lgbl" | "light_green_blink" => Color::LightGreen.blink(),
"lgst" | "light_green_strike" => Color::LightGreen.strikethrough(),
"r" | "red" => Color::Red.normal(),
"rb" | "red_bold" => Color::Red.bold(),
"ru" | "red_underline" => Color::Red.underline(),
"ri" | "red_italic" => Color::Red.italic(),
"rd" | "red_dimmed" => Color::Red.dimmed(),
"rr" | "red_reverse" => Color::Red.reverse(),
"rbl" | "red_blink" => Color::Red.blink(),
"rst" | "red_strike" => Color::Red.strikethrough(),
"lr" | "light_red" => Color::LightRed.normal(),
"lrb" | "light_red_bold" => Color::LightRed.bold(),
"lru" | "light_red_underline" => Color::LightRed.underline(),
"lri" | "light_red_italic" => Color::LightRed.italic(),
"lrd" | "light_red_dimmed" => Color::LightRed.dimmed(),
"lrr" | "light_red_reverse" => Color::LightRed.reverse(),
"lrbl" | "light_red_blink" => Color::LightRed.blink(),
"lrst" | "light_red_strike" => Color::LightRed.strikethrough(),
"u" | "blue" => Color::Blue.normal(),
"ub" | "blue_bold" => Color::Blue.bold(),
"uu" | "blue_underline" => Color::Blue.underline(),
"ui" | "blue_italic" => Color::Blue.italic(),
"ud" | "blue_dimmed" => Color::Blue.dimmed(),
"ur" | "blue_reverse" => Color::Blue.reverse(),
"ubl" | "blue_blink" => Color::Blue.blink(),
"ust" | "blue_strike" => Color::Blue.strikethrough(),
"lu" | "light_blue" => Color::LightBlue.normal(),
"lub" | "light_blue_bold" => Color::LightBlue.bold(),
"luu" | "light_blue_underline" => Color::LightBlue.underline(),
"lui" | "light_blue_italic" => Color::LightBlue.italic(),
"lud" | "light_blue_dimmed" => Color::LightBlue.dimmed(),
"lur" | "light_blue_reverse" => Color::LightBlue.reverse(),
"lubl" | "light_blue_blink" => Color::LightBlue.blink(),
"lust" | "light_blue_strike" => Color::LightBlue.strikethrough(),
"b" | "black" => Color::Black.normal(),
"bb" | "black_bold" => Color::Black.bold(),
"bu" | "black_underline" => Color::Black.underline(),
"bi" | "black_italic" => Color::Black.italic(),
"bd" | "black_dimmed" => Color::Black.dimmed(),
"br" | "black_reverse" => Color::Black.reverse(),
"bbl" | "black_blink" => Color::Black.blink(),
"bst" | "black_strike" => Color::Black.strikethrough(),
"ligr" | "light_gray" => Color::LightGray.normal(),
"ligrb" | "light_gray_bold" => Color::LightGray.bold(),
"ligru" | "light_gray_underline" => Color::LightGray.underline(),
"ligri" | "light_gray_italic" => Color::LightGray.italic(),
"ligrd" | "light_gray_dimmed" => Color::LightGray.dimmed(),
"ligrr" | "light_gray_reverse" => Color::LightGray.reverse(),
"ligrbl" | "light_gray_blink" => Color::LightGray.blink(),
"ligrst" | "light_gray_strike" => Color::LightGray.strikethrough(),
"y" | "yellow" => Color::Yellow.normal(),
"yb" | "yellow_bold" => Color::Yellow.bold(),
"yu" | "yellow_underline" => Color::Yellow.underline(),
"yi" | "yellow_italic" => Color::Yellow.italic(),
"yd" | "yellow_dimmed" => Color::Yellow.dimmed(),
"yr" | "yellow_reverse" => Color::Yellow.reverse(),
"ybl" | "yellow_blink" => Color::Yellow.blink(),
"yst" | "yellow_strike" => Color::Yellow.strikethrough(),
"ly" | "light_yellow" => Color::LightYellow.normal(),
"lyb" | "light_yellow_bold" => Color::LightYellow.bold(),
"lyu" | "light_yellow_underline" => Color::LightYellow.underline(),
"lyi" | "light_yellow_italic" => Color::LightYellow.italic(),
"lyd" | "light_yellow_dimmed" => Color::LightYellow.dimmed(),
"lyr" | "light_yellow_reverse" => Color::LightYellow.reverse(),
"lybl" | "light_yellow_blink" => Color::LightYellow.blink(),
"lyst" | "light_yellow_strike" => Color::LightYellow.strikethrough(),
"p" | "purple" => Color::Purple.normal(),
"pb" | "purple_bold" => Color::Purple.bold(),
"pu" | "purple_underline" => Color::Purple.underline(),
"pi" | "purple_italic" => Color::Purple.italic(),
"pd" | "purple_dimmed" => Color::Purple.dimmed(),
"pr" | "purple_reverse" => Color::Purple.reverse(),
"pbl" | "purple_blink" => Color::Purple.blink(),
"pst" | "purple_strike" => Color::Purple.strikethrough(),
"lp" | "light_purple" => Color::LightPurple.normal(),
"lpb" | "light_purple_bold" => Color::LightPurple.bold(),
"lpu" | "light_purple_underline" => Color::LightPurple.underline(),
"lpi" | "light_purple_italic" => Color::LightPurple.italic(),
"lpd" | "light_purple_dimmed" => Color::LightPurple.dimmed(),
"lpr" | "light_purple_reverse" => Color::LightPurple.reverse(),
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
"c" | "cyan" => Color::Cyan.normal(),
"cb" | "cyan_bold" => Color::Cyan.bold(),
"cu" | "cyan_underline" => Color::Cyan.underline(),
"ci" | "cyan_italic" => Color::Cyan.italic(),
"cd" | "cyan_dimmed" => Color::Cyan.dimmed(),
"cr" | "cyan_reverse" => Color::Cyan.reverse(),
"cbl" | "cyan_blink" => Color::Cyan.blink(),
"cst" | "cyan_strike" => Color::Cyan.strikethrough(),
"lc" | "light_cyan" => Color::LightCyan.normal(),
"lcb" | "light_cyan_bold" => Color::LightCyan.bold(),
"lcu" | "light_cyan_underline" => Color::LightCyan.underline(),
"lci" | "light_cyan_italic" => Color::LightCyan.italic(),
"lcd" | "light_cyan_dimmed" => Color::LightCyan.dimmed(),
"lcr" | "light_cyan_reverse" => Color::LightCyan.reverse(),
"lcbl" | "light_cyan_blink" => Color::LightCyan.blink(),
"lcst" | "light_cyan_strike" => Color::LightCyan.strikethrough(),
"w" | "white" => Color::White.normal(),
"wb" | "white_bold" => Color::White.bold(),
"wu" | "white_underline" => Color::White.underline(),
"wi" | "white_italic" => Color::White.italic(),
"wd" | "white_dimmed" => Color::White.dimmed(),
"wr" | "white_reverse" => Color::White.reverse(),
"wbl" | "white_blink" => Color::White.blink(),
"wst" | "white_strike" => Color::White.strikethrough(),
"dgr" | "dark_gray" => Color::DarkGray.normal(),
"dgrb" | "dark_gray_bold" => Color::DarkGray.bold(),
"dgru" | "dark_gray_underline" => Color::DarkGray.underline(),
"dgri" | "dark_gray_italic" => Color::DarkGray.italic(),
"dgrd" | "dark_gray_dimmed" => Color::DarkGray.dimmed(),
"dgrr" | "dark_gray_reverse" => Color::DarkGray.reverse(),
"dgrbl" | "dark_gray_blink" => Color::DarkGray.blink(),
"dgrst" | "dark_gray_strike" => Color::DarkGray.strikethrough(),
"def" | "default" => Color::Default.normal(),
"defb" | "default_bold" => Color::Default.bold(),
"defu" | "default_underline" => Color::Default.underline(),
"defi" | "default_italic" => Color::Default.italic(),
"defd" | "default_dimmed" => Color::Default.dimmed(),
"defr" | "default_reverse" => Color::Default.reverse(),
_ => Color::White.normal(),
}
} }
} }
fn update_hashmap(key: &str, val: &str, hm: &mut HashMap<String, Style>) { pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style> {
// eprintln!("key: {}, val: {}", &key, &val);
let color = lookup_ansi_color_style(val);
if let Some(v) = hm.get_mut(key) {
*v = color;
} else {
hm.insert(key.to_string(), color);
}
}
pub fn get_color_config(config: &Config) -> HashMap<String, Style> {
let config = config;
// create the hashmap
let mut hm: HashMap<String, Style> = HashMap::new(); let mut hm: HashMap<String, Style> = HashMap::new();
// set some defaults
// hm.insert("primitive_line".to_string(), Color::White.normal());
// hm.insert("primitive_pattern".to_string(), Color::White.normal());
// hm.insert("primitive_path".to_string(), Color::White.normal());
hm.insert("separator".to_string(), Color::White.normal());
hm.insert(
"leading_trailing_space_bg".to_string(),
Style::default().on(Color::Rgb(128, 128, 128)),
);
hm.insert("header".to_string(), Color::Green.bold());
hm.insert("empty".to_string(), Color::Blue.normal());
hm.insert("bool".to_string(), Color::White.normal());
hm.insert("int".to_string(), Color::White.normal());
hm.insert("filesize".to_string(), Color::White.normal());
hm.insert("duration".to_string(), Color::White.normal());
hm.insert("date".to_string(), Color::White.normal());
hm.insert("range".to_string(), Color::White.normal());
hm.insert("float".to_string(), Color::White.normal());
hm.insert("string".to_string(), Color::White.normal());
hm.insert("nothing".to_string(), Color::White.normal());
hm.insert("binary".to_string(), Color::White.normal());
hm.insert("cellpath".to_string(), Color::White.normal());
hm.insert("row_index".to_string(), Color::Green.bold());
hm.insert("record".to_string(), Color::White.normal());
hm.insert("list".to_string(), Color::White.normal());
hm.insert("block".to_string(), Color::White.normal());
hm.insert("hints".to_string(), Color::DarkGray.normal());
for (key, value) in &config.color_config { for (key, value) in colors {
match value.as_string() { parse_map_entry(&mut hm, key, value);
Ok(value) => update_hashmap(key, &value, &mut hm),
Err(_) => continue,
}
} }
hm hm
} }
// This function will assign a text style to a primitive, or really any string that's fn parse_map_entry(hm: &mut HashMap<String, Style>, key: &str, value: &Value) {
// in the hashmap. The hashmap actually contains the style to be applied. let value = match value {
pub fn style_primitive(primitive: &str, color_hm: &HashMap<String, Style>) -> TextStyle { Value::String { val, .. } => Some(lookup_ansi_color_style(val)),
match primitive { Value::Record { cols, vals, .. } => get_style_from_value(cols, vals).map(parse_nustyle),
"bool" => { _ => None,
let style = color_hm.get(primitive); };
match style { if let Some(value) = value {
Some(s) => TextStyle::with_style(Alignment::Left, *s), hm.entry(key.to_owned()).or_insert(value);
None => TextStyle::basic_left(),
}
}
"int" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::basic_right(),
}
}
"filesize" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::basic_right(),
}
}
"duration" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"date" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"range" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"float" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::basic_right(),
}
}
"string" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"nothing" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
// not sure what to do with error
// "error" => {}
"binary" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"cellpath" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"row_index" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::new()
.alignment(Alignment::Right)
.fg(Color::Green)
.bold(Some(true)),
}
}
"record" | "list" | "block" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
// types in nushell but not in engine-q
// "Line" => {
// let style = color_hm.get("Primitive::Line");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "GlobPattern" => {
// let style = color_hm.get("Primitive::GlobPattern");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "FilePath" => {
// let style = color_hm.get("Primitive::FilePath");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "BeginningOfStream" => {
// let style = color_hm.get("Primitive::BeginningOfStream");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "EndOfStream" => {
// let style = color_hm.get("Primitive::EndOfStream");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
_ => TextStyle::basic_left(),
} }
} }
#[test] fn get_style_from_value(cols: &[String], vals: &[Value]) -> Option<NuStyle> {
fn test_hm() { let mut was_set = false;
use nu_ansi_term::{Color, Style}; let mut style = NuStyle::from(Style::default());
for (col, val) in cols.iter().zip(vals) {
match col.as_str() {
"bg" => {
if let Value::String { val, .. } = val {
style.bg = Some(val.clone());
was_set = true;
}
}
"fg" => {
if let Value::String { val, .. } = val {
style.fg = Some(val.clone());
was_set = true;
}
}
"attr" => {
if let Value::String { val, .. } = val {
style.attr = Some(val.clone());
was_set = true;
}
}
_ => (),
}
}
let mut hm: HashMap<String, Style> = HashMap::new(); if was_set {
hm.insert("primitive_int".to_string(), Color::White.normal()); Some(style)
hm.insert("primitive_decimal".to_string(), Color::White.normal()); } else {
hm.insert("primitive_filesize".to_string(), Color::White.normal()); None
hm.insert("primitive_string".to_string(), Color::White.normal()); }
hm.insert("primitive_line".to_string(), Color::White.normal()); }
hm.insert("primitive_columnpath".to_string(), Color::White.normal());
hm.insert("primitive_pattern".to_string(), Color::White.normal()); fn color_string_to_nustyle(color_string: String) -> Style {
hm.insert("primitive_boolean".to_string(), Color::White.normal()); // eprintln!("color_string: {}", &color_string);
hm.insert("primitive_date".to_string(), Color::White.normal()); if color_string.is_empty() {
hm.insert("primitive_duration".to_string(), Color::White.normal()); return Style::default();
hm.insert("primitive_range".to_string(), Color::White.normal()); }
hm.insert("primitive_path".to_string(), Color::White.normal());
hm.insert("primitive_binary".to_string(), Color::White.normal()); let nu_style = match nu_json::from_str::<NuStyle>(&color_string) {
hm.insert("separator".to_string(), Color::White.normal()); Ok(s) => s,
hm.insert("header_align".to_string(), Color::Green.bold()); Err(_) => return Style::default(),
hm.insert("header".to_string(), Color::Green.bold()); };
hm.insert("header_style".to_string(), Style::default());
hm.insert("row_index".to_string(), Color::Green.bold()); parse_nustyle(nu_style)
hm.insert(
"leading_trailing_space_bg".to_string(),
Style::default().on(Color::Rgb(128, 128, 128)),
);
update_hashmap("primitive_int", "green", &mut hm);
assert_eq!(hm["primitive_int"], Color::Green.normal());
} }

View File

@ -2,8 +2,12 @@ mod color_config;
mod matching_brackets_style; mod matching_brackets_style;
mod nu_style; mod nu_style;
mod shape_color; mod shape_color;
mod style_computer;
mod text_style;
pub use color_config::*; pub use color_config::*;
pub use matching_brackets_style::*; pub use matching_brackets_style::*;
pub use nu_style::*; pub use nu_style::*;
pub use shape_color::*; pub use shape_color::*;
pub use style_computer::*;
pub use text_style::*;

View File

@ -1,88 +1,113 @@
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use serde::Deserialize; use nu_protocol::Value;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, PartialEq, Eq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)]
pub struct NuStyle { pub struct NuStyle {
pub fg: Option<String>, pub fg: Option<String>,
pub bg: Option<String>, pub bg: Option<String>,
pub attr: Option<String>, pub attr: Option<String>,
} }
pub fn parse_nustyle(nu_style: NuStyle) -> Style { impl From<Style> for NuStyle {
// get the nu_ansi_term::Color foreground color fn from(s: Style) -> Self {
let fg_color = match nu_style.fg { Self {
Some(fg) => color_from_hex(&fg).unwrap_or_default(), bg: s.background.and_then(color_to_string),
_ => None, fg: s.foreground.and_then(color_to_string),
}; attr: style_get_attr(s),
// get the nu_ansi_term::Color background color
let bg_color = match nu_style.bg {
Some(bg) => color_from_hex(&bg).unwrap_or_default(),
_ => None,
};
// get the attributes
let color_attr = match nu_style.attr {
Some(attr) => attr,
_ => "".to_string(),
};
// setup the attributes available in nu_ansi_term::Style
let mut bold = false;
let mut dimmed = false;
let mut italic = false;
let mut underline = false;
let mut blink = false;
let mut reverse = false;
let mut hidden = false;
let mut strikethrough = false;
// since we can combine styles like bold-italic, iterate through the chars
// and set the bools for later use in the nu_ansi_term::Style application
for ch in color_attr.to_lowercase().chars() {
match ch {
'l' => blink = true,
'b' => bold = true,
'd' => dimmed = true,
'h' => hidden = true,
'i' => italic = true,
'r' => reverse = true,
's' => strikethrough = true,
'u' => underline = true,
'n' => (),
_ => (),
} }
} }
// here's where we build the nu_ansi_term::Style
Style {
foreground: fg_color,
background: bg_color,
is_blink: blink,
is_bold: bold,
is_dimmed: dimmed,
is_hidden: hidden,
is_italic: italic,
is_reverse: reverse,
is_strikethrough: strikethrough,
is_underline: underline,
}
} }
pub fn color_string_to_nustyle(color_string: String) -> Style { fn style_get_attr(s: Style) -> Option<String> {
// eprintln!("color_string: {}", &color_string); macro_rules! check {
if color_string.chars().count() < 1 { ($attrs:expr, $b:expr, $c:expr) => {
Style::default() if $b {
} else { $attrs.push($c);
let nu_style = match nu_json::from_str::<NuStyle>(&color_string) { }
Ok(s) => s,
Err(_) => NuStyle {
fg: None,
bg: None,
attr: None,
},
}; };
parse_nustyle(nu_style)
} }
let mut attrs = String::new();
check!(attrs, s.is_blink, 'l');
check!(attrs, s.is_bold, 'b');
check!(attrs, s.is_dimmed, 'd');
check!(attrs, s.is_reverse, 'r');
check!(attrs, s.is_strikethrough, 's');
check!(attrs, s.is_underline, 'u');
if attrs.is_empty() {
None
} else {
Some(attrs)
}
}
fn color_to_string(color: Color) -> Option<String> {
match color {
Color::Black => Some(String::from("black")),
Color::DarkGray => Some(String::from("dark_gray")),
Color::Red => Some(String::from("red")),
Color::LightRed => Some(String::from("light_red")),
Color::Green => Some(String::from("green")),
Color::LightGreen => Some(String::from("light_green")),
Color::Yellow => Some(String::from("yellow")),
Color::LightYellow => Some(String::from("light_yellow")),
Color::Blue => Some(String::from("blue")),
Color::LightBlue => Some(String::from("light_blue")),
Color::Purple => Some(String::from("purple")),
Color::LightPurple => Some(String::from("light_purple")),
Color::Magenta => Some(String::from("magenta")),
Color::LightMagenta => Some(String::from("light_magenta")),
Color::Cyan => Some(String::from("cyan")),
Color::LightCyan => Some(String::from("light_cyan")),
Color::White => Some(String::from("white")),
Color::LightGray => Some(String::from("light_gray")),
Color::Default => Some(String::from("default")),
Color::Rgb(r, g, b) => Some(format!("#{r:X}{g:X}{b:X}")),
Color::Fixed(_) => None,
}
}
pub fn parse_nustyle(nu_style: NuStyle) -> Style {
let mut style = Style {
foreground: nu_style.fg.and_then(|fg| lookup_color_str(&fg)),
background: nu_style.bg.and_then(|bg| lookup_color_str(&bg)),
..Default::default()
};
if let Some(attrs) = nu_style.attr {
fill_modifiers(&attrs, &mut style)
}
style
}
// Converts the color_config records, { fg, bg, attr }, into a Style.
pub fn color_record_to_nustyle(value: &Value) -> Style {
let mut fg = None;
let mut bg = None;
let mut attr = None;
let v = value.as_record();
if let Ok((cols, inner_vals)) = v {
for (k, v) in cols.iter().zip(inner_vals) {
// Because config already type-checked the color_config records, this doesn't bother giving errors
// if there are unrecognised keys or bad values.
if let Ok(v) = v.as_string() {
match k.as_str() {
"fg" => fg = Some(v),
"bg" => bg = Some(v),
"attr" => attr = Some(v),
_ => (),
}
}
}
}
parse_nustyle(NuStyle { fg, bg, attr })
} }
pub fn color_from_hex( pub fn color_from_hex(
@ -101,3 +126,471 @@ pub fn color_from_hex(
))) )))
} }
} }
pub fn lookup_style(s: &str) -> Style {
match s {
"g" | "green" => Color::Green.normal(),
"gb" | "green_bold" => Color::Green.bold(),
"gu" | "green_underline" => Color::Green.underline(),
"gi" | "green_italic" => Color::Green.italic(),
"gd" | "green_dimmed" => Color::Green.dimmed(),
"gr" | "green_reverse" => Color::Green.reverse(),
"gbl" | "green_blink" => Color::Green.blink(),
"gst" | "green_strike" => Color::Green.strikethrough(),
"lg" | "light_green" => Color::LightGreen.normal(),
"lgb" | "light_green_bold" => Color::LightGreen.bold(),
"lgu" | "light_green_underline" => Color::LightGreen.underline(),
"lgi" | "light_green_italic" => Color::LightGreen.italic(),
"lgd" | "light_green_dimmed" => Color::LightGreen.dimmed(),
"lgr" | "light_green_reverse" => Color::LightGreen.reverse(),
"lgbl" | "light_green_blink" => Color::LightGreen.blink(),
"lgst" | "light_green_strike" => Color::LightGreen.strikethrough(),
"r" | "red" => Color::Red.normal(),
"rb" | "red_bold" => Color::Red.bold(),
"ru" | "red_underline" => Color::Red.underline(),
"ri" | "red_italic" => Color::Red.italic(),
"rd" | "red_dimmed" => Color::Red.dimmed(),
"rr" | "red_reverse" => Color::Red.reverse(),
"rbl" | "red_blink" => Color::Red.blink(),
"rst" | "red_strike" => Color::Red.strikethrough(),
"lr" | "light_red" => Color::LightRed.normal(),
"lrb" | "light_red_bold" => Color::LightRed.bold(),
"lru" | "light_red_underline" => Color::LightRed.underline(),
"lri" | "light_red_italic" => Color::LightRed.italic(),
"lrd" | "light_red_dimmed" => Color::LightRed.dimmed(),
"lrr" | "light_red_reverse" => Color::LightRed.reverse(),
"lrbl" | "light_red_blink" => Color::LightRed.blink(),
"lrst" | "light_red_strike" => Color::LightRed.strikethrough(),
"u" | "blue" => Color::Blue.normal(),
"ub" | "blue_bold" => Color::Blue.bold(),
"uu" | "blue_underline" => Color::Blue.underline(),
"ui" | "blue_italic" => Color::Blue.italic(),
"ud" | "blue_dimmed" => Color::Blue.dimmed(),
"ur" | "blue_reverse" => Color::Blue.reverse(),
"ubl" | "blue_blink" => Color::Blue.blink(),
"ust" | "blue_strike" => Color::Blue.strikethrough(),
"lu" | "light_blue" => Color::LightBlue.normal(),
"lub" | "light_blue_bold" => Color::LightBlue.bold(),
"luu" | "light_blue_underline" => Color::LightBlue.underline(),
"lui" | "light_blue_italic" => Color::LightBlue.italic(),
"lud" | "light_blue_dimmed" => Color::LightBlue.dimmed(),
"lur" | "light_blue_reverse" => Color::LightBlue.reverse(),
"lubl" | "light_blue_blink" => Color::LightBlue.blink(),
"lust" | "light_blue_strike" => Color::LightBlue.strikethrough(),
"b" | "black" => Color::Black.normal(),
"bb" | "black_bold" => Color::Black.bold(),
"bu" | "black_underline" => Color::Black.underline(),
"bi" | "black_italic" => Color::Black.italic(),
"bd" | "black_dimmed" => Color::Black.dimmed(),
"br" | "black_reverse" => Color::Black.reverse(),
"bbl" | "black_blink" => Color::Black.blink(),
"bst" | "black_strike" => Color::Black.strikethrough(),
"ligr" | "light_gray" => Color::LightGray.normal(),
"ligrb" | "light_gray_bold" => Color::LightGray.bold(),
"ligru" | "light_gray_underline" => Color::LightGray.underline(),
"ligri" | "light_gray_italic" => Color::LightGray.italic(),
"ligrd" | "light_gray_dimmed" => Color::LightGray.dimmed(),
"ligrr" | "light_gray_reverse" => Color::LightGray.reverse(),
"ligrbl" | "light_gray_blink" => Color::LightGray.blink(),
"ligrst" | "light_gray_strike" => Color::LightGray.strikethrough(),
"y" | "yellow" => Color::Yellow.normal(),
"yb" | "yellow_bold" => Color::Yellow.bold(),
"yu" | "yellow_underline" => Color::Yellow.underline(),
"yi" | "yellow_italic" => Color::Yellow.italic(),
"yd" | "yellow_dimmed" => Color::Yellow.dimmed(),
"yr" | "yellow_reverse" => Color::Yellow.reverse(),
"ybl" | "yellow_blink" => Color::Yellow.blink(),
"yst" | "yellow_strike" => Color::Yellow.strikethrough(),
"ly" | "light_yellow" => Color::LightYellow.normal(),
"lyb" | "light_yellow_bold" => Color::LightYellow.bold(),
"lyu" | "light_yellow_underline" => Color::LightYellow.underline(),
"lyi" | "light_yellow_italic" => Color::LightYellow.italic(),
"lyd" | "light_yellow_dimmed" => Color::LightYellow.dimmed(),
"lyr" | "light_yellow_reverse" => Color::LightYellow.reverse(),
"lybl" | "light_yellow_blink" => Color::LightYellow.blink(),
"lyst" | "light_yellow_strike" => Color::LightYellow.strikethrough(),
"p" | "purple" => Color::Purple.normal(),
"pb" | "purple_bold" => Color::Purple.bold(),
"pu" | "purple_underline" => Color::Purple.underline(),
"pi" | "purple_italic" => Color::Purple.italic(),
"pd" | "purple_dimmed" => Color::Purple.dimmed(),
"pr" | "purple_reverse" => Color::Purple.reverse(),
"pbl" | "purple_blink" => Color::Purple.blink(),
"pst" | "purple_strike" => Color::Purple.strikethrough(),
"lp" | "light_purple" => Color::LightPurple.normal(),
"lpb" | "light_purple_bold" => Color::LightPurple.bold(),
"lpu" | "light_purple_underline" => Color::LightPurple.underline(),
"lpi" | "light_purple_italic" => Color::LightPurple.italic(),
"lpd" | "light_purple_dimmed" => Color::LightPurple.dimmed(),
"lpr" | "light_purple_reverse" => Color::LightPurple.reverse(),
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
"c" | "cyan" => Color::Cyan.normal(),
"cb" | "cyan_bold" => Color::Cyan.bold(),
"cu" | "cyan_underline" => Color::Cyan.underline(),
"ci" | "cyan_italic" => Color::Cyan.italic(),
"cd" | "cyan_dimmed" => Color::Cyan.dimmed(),
"cr" | "cyan_reverse" => Color::Cyan.reverse(),
"cbl" | "cyan_blink" => Color::Cyan.blink(),
"cst" | "cyan_strike" => Color::Cyan.strikethrough(),
"lc" | "light_cyan" => Color::LightCyan.normal(),
"lcb" | "light_cyan_bold" => Color::LightCyan.bold(),
"lcu" | "light_cyan_underline" => Color::LightCyan.underline(),
"lci" | "light_cyan_italic" => Color::LightCyan.italic(),
"lcd" | "light_cyan_dimmed" => Color::LightCyan.dimmed(),
"lcr" | "light_cyan_reverse" => Color::LightCyan.reverse(),
"lcbl" | "light_cyan_blink" => Color::LightCyan.blink(),
"lcst" | "light_cyan_strike" => Color::LightCyan.strikethrough(),
"w" | "white" => Color::White.normal(),
"wb" | "white_bold" => Color::White.bold(),
"wu" | "white_underline" => Color::White.underline(),
"wi" | "white_italic" => Color::White.italic(),
"wd" | "white_dimmed" => Color::White.dimmed(),
"wr" | "white_reverse" => Color::White.reverse(),
"wbl" | "white_blink" => Color::White.blink(),
"wst" | "white_strike" => Color::White.strikethrough(),
"dgr" | "dark_gray" => Color::DarkGray.normal(),
"dgrb" | "dark_gray_bold" => Color::DarkGray.bold(),
"dgru" | "dark_gray_underline" => Color::DarkGray.underline(),
"dgri" | "dark_gray_italic" => Color::DarkGray.italic(),
"dgrd" | "dark_gray_dimmed" => Color::DarkGray.dimmed(),
"dgrr" | "dark_gray_reverse" => Color::DarkGray.reverse(),
"dgrbl" | "dark_gray_blink" => Color::DarkGray.blink(),
"dgrst" | "dark_gray_strike" => Color::DarkGray.strikethrough(),
"def" | "default" => Color::Default.normal(),
"defb" | "default_bold" => Color::Default.bold(),
"defu" | "default_underline" => Color::Default.underline(),
"defi" | "default_italic" => Color::Default.italic(),
"defd" | "default_dimmed" => Color::Default.dimmed(),
"defr" | "default_reverse" => Color::Default.reverse(),
// Add xterm 256 colors adding an x prefix where the name conflicts
"xblack" | "xterm_black" => Color::Fixed(0).normal(),
"maroon" | "xterm_maroon" => Color::Fixed(1).normal(),
"xgreen" | "xterm_green" => Color::Fixed(2).normal(),
"olive" | "xterm_olive" => Color::Fixed(3).normal(),
"navy" | "xterm_navy" => Color::Fixed(4).normal(),
"xpurplea" | "xterm_purplea" => Color::Fixed(5).normal(),
"teal" | "xterm_teal" => Color::Fixed(6).normal(),
"silver" | "xterm_silver" => Color::Fixed(7).normal(),
"grey" | "xterm_grey" => Color::Fixed(8).normal(),
"xred" | "xterm_red" => Color::Fixed(9).normal(),
"lime" | "xterm_lime" => Color::Fixed(10).normal(),
"xyellow" | "xterm_yellow" => Color::Fixed(11).normal(),
"xblue" | "xterm_blue" => Color::Fixed(12).normal(),
"fuchsia" | "xterm_fuchsia" => Color::Fixed(13).normal(),
"aqua" | "xterm_aqua" => Color::Fixed(14).normal(),
"xwhite" | "xterm_white" => Color::Fixed(15).normal(),
"grey0" | "xterm_grey0" => Color::Fixed(16).normal(),
"navyblue" | "xterm_navyblue" => Color::Fixed(17).normal(),
"darkblue" | "xterm_darkblue" => Color::Fixed(18).normal(),
"blue3a" | "xterm_blue3a" => Color::Fixed(19).normal(),
"blue3b" | "xterm_blue3b" => Color::Fixed(20).normal(),
"blue1" | "xterm_blue1" => Color::Fixed(21).normal(),
"darkgreen" | "xterm_darkgreen" => Color::Fixed(22).normal(),
"deepskyblue4a" | "xterm_deepskyblue4a" => Color::Fixed(23).normal(),
"deepskyblue4b" | "xterm_deepskyblue4b" => Color::Fixed(24).normal(),
"deepskyblue4c" | "xterm_deepskyblue4c" => Color::Fixed(25).normal(),
"dodgerblue3" | "xterm_dodgerblue3" => Color::Fixed(26).normal(),
"dodgerblue2" | "xterm_dodgerblue2" => Color::Fixed(27).normal(),
"green4" | "xterm_green4" => Color::Fixed(28).normal(),
"springgreen4" | "xterm_springgreen4" => Color::Fixed(29).normal(),
"turquoise4" | "xterm_turquoise4" => Color::Fixed(30).normal(),
"deepskyblue3a" | "xterm_deepskyblue3a" => Color::Fixed(31).normal(),
"deepskyblue3b" | "xterm_deepskyblue3b" => Color::Fixed(32).normal(),
"dodgerblue1" | "xterm_dodgerblue1" => Color::Fixed(33).normal(),
"green3a" | "xterm_green3a" => Color::Fixed(34).normal(),
"springgreen3a" | "xterm_springgreen3a" => Color::Fixed(35).normal(),
"darkcyan" | "xterm_darkcyan" => Color::Fixed(36).normal(),
"lightseagreen" | "xterm_lightseagreen" => Color::Fixed(37).normal(),
"deepskyblue2" | "xterm_deepskyblue2" => Color::Fixed(38).normal(),
"deepskyblue1" | "xterm_deepskyblue1" => Color::Fixed(39).normal(),
"green3b" | "xterm_green3b" => Color::Fixed(40).normal(),
"springgreen3b" | "xterm_springgreen3b" => Color::Fixed(41).normal(),
"springgreen2a" | "xterm_springgreen2a" => Color::Fixed(42).normal(),
"cyan3" | "xterm_cyan3" => Color::Fixed(43).normal(),
"darkturquoise" | "xterm_darkturquoise" => Color::Fixed(44).normal(),
"turquoise2" | "xterm_turquoise2" => Color::Fixed(45).normal(),
"green1" | "xterm_green1" => Color::Fixed(46).normal(),
"springgreen2b" | "xterm_springgreen2b" => Color::Fixed(47).normal(),
"springgreen1" | "xterm_springgreen1" => Color::Fixed(48).normal(),
"mediumspringgreen" | "xterm_mediumspringgreen" => Color::Fixed(49).normal(),
"cyan2" | "xterm_cyan2" => Color::Fixed(50).normal(),
"cyan1" | "xterm_cyan1" => Color::Fixed(51).normal(),
"darkreda" | "xterm_darkreda" => Color::Fixed(52).normal(),
"deeppink4a" | "xterm_deeppink4a" => Color::Fixed(53).normal(),
"purple4a" | "xterm_purple4a" => Color::Fixed(54).normal(),
"purple4b" | "xterm_purple4b" => Color::Fixed(55).normal(),
"purple3" | "xterm_purple3" => Color::Fixed(56).normal(),
"blueviolet" | "xterm_blueviolet" => Color::Fixed(57).normal(),
"orange4a" | "xterm_orange4a" => Color::Fixed(58).normal(),
"grey37" | "xterm_grey37" => Color::Fixed(59).normal(),
"mediumpurple4" | "xterm_mediumpurple4" => Color::Fixed(60).normal(),
"slateblue3a" | "xterm_slateblue3a" => Color::Fixed(61).normal(),
"slateblue3b" | "xterm_slateblue3b" => Color::Fixed(62).normal(),
"royalblue1" | "xterm_royalblue1" => Color::Fixed(63).normal(),
"chartreuse4" | "xterm_chartreuse4" => Color::Fixed(64).normal(),
"darkseagreen4a" | "xterm_darkseagreen4a" => Color::Fixed(65).normal(),
"paleturquoise4" | "xterm_paleturquoise4" => Color::Fixed(66).normal(),
"steelblue" | "xterm_steelblue" => Color::Fixed(67).normal(),
"steelblue3" | "xterm_steelblue3" => Color::Fixed(68).normal(),
"cornflowerblue" | "xterm_cornflowerblue" => Color::Fixed(69).normal(),
"chartreuse3a" | "xterm_chartreuse3a" => Color::Fixed(70).normal(),
"darkseagreen4b" | "xterm_darkseagreen4b" => Color::Fixed(71).normal(),
"cadetbluea" | "xterm_cadetbluea" => Color::Fixed(72).normal(),
"cadetblueb" | "xterm_cadetblueb" => Color::Fixed(73).normal(),
"skyblue3" | "xterm_skyblue3" => Color::Fixed(74).normal(),
"steelblue1a" | "xterm_steelblue1a" => Color::Fixed(75).normal(),
"chartreuse3b" | "xterm_chartreuse3b" => Color::Fixed(76).normal(),
"palegreen3a" | "xterm_palegreen3a" => Color::Fixed(77).normal(),
"seagreen3" | "xterm_seagreen3" => Color::Fixed(78).normal(),
"aquamarine3" | "xterm_aquamarine3" => Color::Fixed(79).normal(),
"mediumturquoise" | "xterm_mediumturquoise" => Color::Fixed(80).normal(),
"steelblue1b" | "xterm_steelblue1b" => Color::Fixed(81).normal(),
"chartreuse2a" | "xterm_chartreuse2a" => Color::Fixed(82).normal(),
"seagreen2" | "xterm_seagreen2" => Color::Fixed(83).normal(),
"seagreen1a" | "xterm_seagreen1a" => Color::Fixed(84).normal(),
"seagreen1b" | "xterm_seagreen1b" => Color::Fixed(85).normal(),
"aquamarine1a" | "xterm_aquamarine1a" => Color::Fixed(86).normal(),
"darkslategray2" | "xterm_darkslategray2" => Color::Fixed(87).normal(),
"darkredb" | "xterm_darkredb" => Color::Fixed(88).normal(),
"deeppink4b" | "xterm_deeppink4b" => Color::Fixed(89).normal(),
"darkmagentaa" | "xterm_darkmagentaa" => Color::Fixed(90).normal(),
"darkmagentab" | "xterm_darkmagentab" => Color::Fixed(91).normal(),
"darkvioleta" | "xterm_darkvioleta" => Color::Fixed(92).normal(),
"xpurpleb" | "xterm_purpleb" => Color::Fixed(93).normal(),
"orange4b" | "xterm_orange4b" => Color::Fixed(94).normal(),
"lightpink4" | "xterm_lightpink4" => Color::Fixed(95).normal(),
"plum4" | "xterm_plum4" => Color::Fixed(96).normal(),
"mediumpurple3a" | "xterm_mediumpurple3a" => Color::Fixed(97).normal(),
"mediumpurple3b" | "xterm_mediumpurple3b" => Color::Fixed(98).normal(),
"slateblue1" | "xterm_slateblue1" => Color::Fixed(99).normal(),
"yellow4a" | "xterm_yellow4a" => Color::Fixed(100).normal(),
"wheat4" | "xterm_wheat4" => Color::Fixed(101).normal(),
"grey53" | "xterm_grey53" => Color::Fixed(102).normal(),
"lightslategrey" | "xterm_lightslategrey" => Color::Fixed(103).normal(),
"mediumpurple" | "xterm_mediumpurple" => Color::Fixed(104).normal(),
"lightslateblue" | "xterm_lightslateblue" => Color::Fixed(105).normal(),
"yellow4b" | "xterm_yellow4b" => Color::Fixed(106).normal(),
"darkolivegreen3a" | "xterm_darkolivegreen3a" => Color::Fixed(107).normal(),
"darkseagreen" | "xterm_darkseagreen" => Color::Fixed(108).normal(),
"lightskyblue3a" | "xterm_lightskyblue3a" => Color::Fixed(109).normal(),
"lightskyblue3b" | "xterm_lightskyblue3b" => Color::Fixed(110).normal(),
"skyblue2" | "xterm_skyblue2" => Color::Fixed(111).normal(),
"chartreuse2b" | "xterm_chartreuse2b" => Color::Fixed(112).normal(),
"darkolivegreen3b" | "xterm_darkolivegreen3b" => Color::Fixed(113).normal(),
"palegreen3b" | "xterm_palegreen3b" => Color::Fixed(114).normal(),
"darkseagreen3a" | "xterm_darkseagreen3a" => Color::Fixed(115).normal(),
"darkslategray3" | "xterm_darkslategray3" => Color::Fixed(116).normal(),
"skyblue1" | "xterm_skyblue1" => Color::Fixed(117).normal(),
"chartreuse1" | "xterm_chartreuse1" => Color::Fixed(118).normal(),
"lightgreena" | "xterm_lightgreena" => Color::Fixed(119).normal(),
"lightgreenb" | "xterm_lightgreenb" => Color::Fixed(120).normal(),
"palegreen1a" | "xterm_palegreen1a" => Color::Fixed(121).normal(),
"aquamarine1b" | "xterm_aquamarine1b" => Color::Fixed(122).normal(),
"darkslategray1" | "xterm_darkslategray1" => Color::Fixed(123).normal(),
"red3a" | "xterm_red3a" => Color::Fixed(124).normal(),
"deeppink4c" | "xterm_deeppink4c" => Color::Fixed(125).normal(),
"mediumvioletred" | "xterm_mediumvioletred" => Color::Fixed(126).normal(),
"magenta3" | "xterm_magenta3" => Color::Fixed(127).normal(),
"darkvioletb" | "xterm_darkvioletb" => Color::Fixed(128).normal(),
"purplec" | "xterm_purplec" => Color::Fixed(129).normal(),
"darkorange3a" | "xterm_darkorange3a" => Color::Fixed(130).normal(),
"indianreda" | "xterm_indianreda" => Color::Fixed(131).normal(),
"hotpink3a" | "xterm_hotpink3a" => Color::Fixed(132).normal(),
"mediumorchid3" | "xterm_mediumorchid3" => Color::Fixed(133).normal(),
"mediumorchid" | "xterm_mediumorchid" => Color::Fixed(134).normal(),
"mediumpurple2a" | "xterm_mediumpurple2a" => Color::Fixed(135).normal(),
"darkgoldenrod" | "xterm_darkgoldenrod" => Color::Fixed(136).normal(),
"lightsalmon3a" | "xterm_lightsalmon3a" => Color::Fixed(137).normal(),
"rosybrown" | "xterm_rosybrown" => Color::Fixed(138).normal(),
"grey63" | "xterm_grey63" => Color::Fixed(139).normal(),
"mediumpurple2b" | "xterm_mediumpurple2b" => Color::Fixed(140).normal(),
"mediumpurple1" | "xterm_mediumpurple1" => Color::Fixed(141).normal(),
"gold3a" | "xterm_gold3a" => Color::Fixed(142).normal(),
"darkkhaki" | "xterm_darkkhaki" => Color::Fixed(143).normal(),
"navajowhite3" | "xterm_navajowhite3" => Color::Fixed(144).normal(),
"grey69" | "xterm_grey69" => Color::Fixed(145).normal(),
"lightsteelblue3" | "xterm_lightsteelblue3" => Color::Fixed(146).normal(),
"lightsteelblue" | "xterm_lightsteelblue" => Color::Fixed(147).normal(),
"yellow3a" | "xterm_yellow3a" => Color::Fixed(148).normal(),
"darkolivegreen3c" | "xterm_darkolivegreen3c" => Color::Fixed(149).normal(),
"darkseagreen3b" | "xterm_darkseagreen3b" => Color::Fixed(150).normal(),
"darkseagreen2a" | "xterm_darkseagreen2a" => Color::Fixed(151).normal(),
"lightcyan3" | "xterm_lightcyan3" => Color::Fixed(152).normal(),
"lightskyblue1" | "xterm_lightskyblue1" => Color::Fixed(153).normal(),
"greenyellow" | "xterm_greenyellow" => Color::Fixed(154).normal(),
"darkolivegreen2" | "xterm_darkolivegreen2" => Color::Fixed(155).normal(),
"palegreen1b" | "xterm_palegreen1b" => Color::Fixed(156).normal(),
"darkseagreen2b" | "xterm_darkseagreen2b" => Color::Fixed(157).normal(),
"darkseagreen1a" | "xterm_darkseagreen1a" => Color::Fixed(158).normal(),
"paleturquoise1" | "xterm_paleturquoise1" => Color::Fixed(159).normal(),
"red3b" | "xterm_red3b" => Color::Fixed(160).normal(),
"deeppink3a" | "xterm_deeppink3a" => Color::Fixed(161).normal(),
"deeppink3b" | "xterm_deeppink3b" => Color::Fixed(162).normal(),
"magenta3a" | "xterm_magenta3a" => Color::Fixed(163).normal(),
"magenta3b" | "xterm_magenta3b" => Color::Fixed(164).normal(),
"magenta2a" | "xterm_magenta2a" => Color::Fixed(165).normal(),
"darkorange3b" | "xterm_darkorange3b" => Color::Fixed(166).normal(),
"indianredb" | "xterm_indianredb" => Color::Fixed(167).normal(),
"hotpink3b" | "xterm_hotpink3b" => Color::Fixed(168).normal(),
"hotpink2" | "xterm_hotpink2" => Color::Fixed(169).normal(),
"orchid" | "xterm_orchid" => Color::Fixed(170).normal(),
"mediumorchid1a" | "xterm_mediumorchid1a" => Color::Fixed(171).normal(),
"orange3" | "xterm_orange3" => Color::Fixed(172).normal(),
"lightsalmon3b" | "xterm_lightsalmon3b" => Color::Fixed(173).normal(),
"lightpink3" | "xterm_lightpink3" => Color::Fixed(174).normal(),
"pink3" | "xterm_pink3" => Color::Fixed(175).normal(),
"plum3" | "xterm_plum3" => Color::Fixed(176).normal(),
"violet" | "xterm_violet" => Color::Fixed(177).normal(),
"gold3b" | "xterm_gold3b" => Color::Fixed(178).normal(),
"lightgoldenrod3" | "xterm_lightgoldenrod3" => Color::Fixed(179).normal(),
"tan" | "xterm_tan" => Color::Fixed(180).normal(),
"mistyrose3" | "xterm_mistyrose3" => Color::Fixed(181).normal(),
"thistle3" | "xterm_thistle3" => Color::Fixed(182).normal(),
"plum2" | "xterm_plum2" => Color::Fixed(183).normal(),
"yellow3b" | "xterm_yellow3b" => Color::Fixed(184).normal(),
"khaki3" | "xterm_khaki3" => Color::Fixed(185).normal(),
"lightgoldenrod2" | "xterm_lightgoldenrod2" => Color::Fixed(186).normal(),
"lightyellow3" | "xterm_lightyellow3" => Color::Fixed(187).normal(),
"grey84" | "xterm_grey84" => Color::Fixed(188).normal(),
"lightsteelblue1" | "xterm_lightsteelblue1" => Color::Fixed(189).normal(),
"yellow2" | "xterm_yellow2" => Color::Fixed(190).normal(),
"darkolivegreen1a" | "xterm_darkolivegreen1a" => Color::Fixed(191).normal(),
"darkolivegreen1b" | "xterm_darkolivegreen1b" => Color::Fixed(192).normal(),
"darkseagreen1b" | "xterm_darkseagreen1b" => Color::Fixed(193).normal(),
"honeydew2" | "xterm_honeydew2" => Color::Fixed(194).normal(),
"lightcyan1" | "xterm_lightcyan1" => Color::Fixed(195).normal(),
"red1" | "xterm_red1" => Color::Fixed(196).normal(),
"deeppink2" | "xterm_deeppink2" => Color::Fixed(197).normal(),
"deeppink1a" | "xterm_deeppink1a" => Color::Fixed(198).normal(),
"deeppink1b" | "xterm_deeppink1b" => Color::Fixed(199).normal(),
"magenta2b" | "xterm_magenta2b" => Color::Fixed(200).normal(),
"magenta1" | "xterm_magenta1" => Color::Fixed(201).normal(),
"orangered1" | "xterm_orangered1" => Color::Fixed(202).normal(),
"indianred1a" | "xterm_indianred1a" => Color::Fixed(203).normal(),
"indianred1b" | "xterm_indianred1b" => Color::Fixed(204).normal(),
"hotpinka" | "xterm_hotpinka" => Color::Fixed(205).normal(),
"hotpinkb" | "xterm_hotpinkb" => Color::Fixed(206).normal(),
"mediumorchid1b" | "xterm_mediumorchid1b" => Color::Fixed(207).normal(),
"darkorange" | "xterm_darkorange" => Color::Fixed(208).normal(),
"salmon1" | "xterm_salmon1" => Color::Fixed(209).normal(),
"lightcoral" | "xterm_lightcoral" => Color::Fixed(210).normal(),
"palevioletred1" | "xterm_palevioletred1" => Color::Fixed(211).normal(),
"orchid2" | "xterm_orchid2" => Color::Fixed(212).normal(),
"orchid1" | "xterm_orchid1" => Color::Fixed(213).normal(),
"orange1" | "xterm_orange1" => Color::Fixed(214).normal(),
"sandybrown" | "xterm_sandybrown" => Color::Fixed(215).normal(),
"lightsalmon1" | "xterm_lightsalmon1" => Color::Fixed(216).normal(),
"lightpink1" | "xterm_lightpink1" => Color::Fixed(217).normal(),
"pink1" | "xterm_pink1" => Color::Fixed(218).normal(),
"plum1" | "xterm_plum1" => Color::Fixed(219).normal(),
"gold1" | "xterm_gold1" => Color::Fixed(220).normal(),
"lightgoldenrod2a" | "xterm_lightgoldenrod2a" => Color::Fixed(221).normal(),
"lightgoldenrod2b" | "xterm_lightgoldenrod2b" => Color::Fixed(222).normal(),
"navajowhite1" | "xterm_navajowhite1" => Color::Fixed(223).normal(),
"mistyrose1" | "xterm_mistyrose1" => Color::Fixed(224).normal(),
"thistle1" | "xterm_thistle1" => Color::Fixed(225).normal(),
"yellow1" | "xterm_yellow1" => Color::Fixed(226).normal(),
"lightgoldenrod1" | "xterm_lightgoldenrod1" => Color::Fixed(227).normal(),
"khaki1" | "xterm_khaki1" => Color::Fixed(228).normal(),
"wheat1" | "xterm_wheat1" => Color::Fixed(229).normal(),
"cornsilk1" | "xterm_cornsilk1" => Color::Fixed(230).normal(),
"grey100" | "xterm_grey100" => Color::Fixed(231).normal(),
"grey3" | "xterm_grey3" => Color::Fixed(232).normal(),
"grey7" | "xterm_grey7" => Color::Fixed(233).normal(),
"grey11" | "xterm_grey11" => Color::Fixed(234).normal(),
"grey15" | "xterm_grey15" => Color::Fixed(235).normal(),
"grey19" | "xterm_grey19" => Color::Fixed(236).normal(),
"grey23" | "xterm_grey23" => Color::Fixed(237).normal(),
"grey27" | "xterm_grey27" => Color::Fixed(238).normal(),
"grey30" | "xterm_grey30" => Color::Fixed(239).normal(),
"grey35" | "xterm_grey35" => Color::Fixed(240).normal(),
"grey39" | "xterm_grey39" => Color::Fixed(241).normal(),
"grey42" | "xterm_grey42" => Color::Fixed(242).normal(),
"grey46" | "xterm_grey46" => Color::Fixed(243).normal(),
"grey50" | "xterm_grey50" => Color::Fixed(244).normal(),
"grey54" | "xterm_grey54" => Color::Fixed(245).normal(),
"grey58" | "xterm_grey58" => Color::Fixed(246).normal(),
"grey62" | "xterm_grey62" => Color::Fixed(247).normal(),
"grey66" | "xterm_grey66" => Color::Fixed(248).normal(),
"grey70" | "xterm_grey70" => Color::Fixed(249).normal(),
"grey74" | "xterm_grey74" => Color::Fixed(250).normal(),
"grey78" | "xterm_grey78" => Color::Fixed(251).normal(),
"grey82" | "xterm_grey82" => Color::Fixed(252).normal(),
"grey85" | "xterm_grey85" => Color::Fixed(253).normal(),
"grey89" | "xterm_grey89" => Color::Fixed(254).normal(),
"grey93" | "xterm_grey93" => Color::Fixed(255).normal(),
_ => Color::White.normal(),
}
}
pub fn lookup_color(s: &str) -> Option<Color> {
let color = match s {
"g" | "green" => Color::Green,
"lg" | "light_green" => Color::LightGreen,
"r" | "red" => Color::Red,
"lr" | "light_red" => Color::LightRed,
"u" | "blue" => Color::Blue,
"lu" | "light_blue" => Color::LightBlue,
"b" | "black" => Color::Black,
"ligr" | "light_gray" => Color::LightGray,
"y" | "yellow" => Color::Yellow,
"ly" | "light_yellow" => Color::LightYellow,
"p" | "purple" => Color::Purple,
"lp" | "light_purple" => Color::LightPurple,
"c" | "cyan" => Color::Cyan,
"lc" | "light_cyan" => Color::LightCyan,
"w" | "white" => Color::White,
"dgr" | "dark_gray" => Color::DarkGray,
"def" | "default" => Color::Default,
_ => return None,
};
Some(color)
}
fn fill_modifiers(attrs: &str, style: &mut Style) {
// setup the attributes available in nu_ansi_term::Style
//
// since we can combine styles like bold-italic, iterate through the chars
// and set the bools for later use in the nu_ansi_term::Style application
for ch in attrs.to_lowercase().chars() {
match ch {
'l' => style.is_blink = true,
'b' => style.is_bold = true,
'd' => style.is_dimmed = true,
'h' => style.is_hidden = true,
'i' => style.is_italic = true,
'r' => style.is_reverse = true,
's' => style.is_strikethrough = true,
'u' => style.is_underline = true,
'n' => (),
_ => (),
}
}
}
fn lookup_color_str(s: &str) -> Option<Color> {
if s.starts_with('#') {
color_from_hex(s).ok().and_then(|c| c)
} else {
lookup_color(s)
}
}

View File

@ -1,45 +1,56 @@
use crate::color_config::lookup_ansi_color_style; use crate::{color_config::lookup_ansi_color_style, color_record_to_nustyle};
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use nu_protocol::Config; use nu_protocol::{Config, Value};
// The default colors for shapes, used when there is no config for them.
pub fn default_shape_color(shape: String) -> Style {
match shape.as_ref() {
"shape_and" => Style::new().fg(Color::Purple).bold(),
"shape_binary" => Style::new().fg(Color::Purple).bold(),
"shape_block" => Style::new().fg(Color::Blue).bold(),
"shape_bool" => Style::new().fg(Color::LightCyan),
"shape_custom" => Style::new().fg(Color::Green),
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_directory" => Style::new().fg(Color::Cyan),
"shape_external" => Style::new().fg(Color::Cyan),
"shape_externalarg" => Style::new().fg(Color::Green).bold(),
"shape_filepath" => Style::new().fg(Color::Cyan),
"shape_flag" => Style::new().fg(Color::Blue).bold(),
"shape_float" => Style::new().fg(Color::Purple).bold(),
"shape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(),
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
"shape_int" => Style::new().fg(Color::Purple).bold(),
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
"shape_list" => Style::new().fg(Color::Cyan).bold(),
"shape_literal" => Style::new().fg(Color::Blue),
"shape_nothing" => Style::new().fg(Color::LightCyan),
"shape_operator" => Style::new().fg(Color::Yellow),
"shape_or" => Style::new().fg(Color::Purple).bold(),
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
"shape_range" => Style::new().fg(Color::Yellow).bold(),
"shape_record" => Style::new().fg(Color::Cyan).bold(),
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
"shape_signature" => Style::new().fg(Color::Green).bold(),
"shape_string" => Style::new().fg(Color::Green),
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
"shape_table" => Style::new().fg(Color::Blue).bold(),
"shape_variable" => Style::new().fg(Color::Purple),
_ => Style::default(),
}
}
pub fn get_shape_color(shape: String, conf: &Config) -> Style { pub fn get_shape_color(shape: String, conf: &Config) -> Style {
match conf.color_config.get(shape.as_str()) { match conf.color_config.get(shape.as_str()) {
Some(int_color) => match int_color.as_string() { Some(int_color) => {
Ok(int_color) => lookup_ansi_color_style(&int_color), // Shapes do not use color_config closures, currently.
Err(_) => Style::default(), match int_color {
}, Value::Record { .. } => color_record_to_nustyle(int_color),
None => match shape.as_ref() { Value::String { val, .. } => lookup_ansi_color_style(val),
"shape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(), // Defer to the default in the event of incorrect types being given
"shape_binary" => Style::new().fg(Color::Purple).bold(), // (i.e. treat null, etc. as the value being unset)
"shape_bool" => Style::new().fg(Color::LightCyan), _ => default_shape_color(shape),
"shape_int" => Style::new().fg(Color::Purple).bold(), }
"shape_float" => Style::new().fg(Color::Purple).bold(), }
"shape_range" => Style::new().fg(Color::Yellow).bold(), None => default_shape_color(shape),
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
"shape_external" => Style::new().fg(Color::Cyan),
"shape_externalarg" => Style::new().fg(Color::Green).bold(),
"shape_literal" => Style::new().fg(Color::Blue),
"shape_operator" => Style::new().fg(Color::Yellow),
"shape_signature" => Style::new().fg(Color::Green).bold(),
"shape_string" => Style::new().fg(Color::Green),
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_list" => Style::new().fg(Color::Cyan).bold(),
"shape_table" => Style::new().fg(Color::Blue).bold(),
"shape_record" => Style::new().fg(Color::Cyan).bold(),
"shape_block" => Style::new().fg(Color::Blue).bold(),
"shape_filepath" => Style::new().fg(Color::Cyan),
"shape_directory" => Style::new().fg(Color::Cyan),
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
"shape_variable" => Style::new().fg(Color::Purple),
"shape_flag" => Style::new().fg(Color::Blue).bold(),
"shape_custom" => Style::new().fg(Color::Green),
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
"shape_and" => Style::new().fg(Color::Purple).bold(),
"shape_or" => Style::new().fg(Color::Purple).bold(),
"shape_nothing" => Style::new().fg(Color::LightCyan),
_ => Style::default(),
},
} }
} }

View File

@ -0,0 +1,285 @@
use crate::{color_record_to_nustyle, lookup_ansi_color_style, TextStyle};
use nu_ansi_term::{Color, Style};
use nu_engine::eval_block;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
CliError, IntoPipelineData, Value,
};
use tabled::alignment::AlignmentHorizontal;
use std::{
collections::HashMap,
fmt::{Debug, Formatter, Result},
};
// ComputableStyle represents the valid user style types: a single color value, or a closure which
// takes an input value and produces a color value. The latter represents a value which
// is computed at use-time.
#[derive(Debug, Clone)]
pub enum ComputableStyle {
Static(Style),
Closure(Value),
}
// macro used for adding initial values to the style hashmap
macro_rules! initial {
($a:expr, $b:expr) => {
($a.to_string(), ComputableStyle::Static($b))
};
}
// An alias for the mapping used internally by StyleComputer.
pub type StyleMapping = HashMap<String, ComputableStyle>;
//
// A StyleComputer is an all-in-one way to compute styles. A nu command can
// simply create it with from_config(), and then use it with compute().
// It stores the engine state and stack needed to run closures that
// may be defined as a user style.
//
pub struct StyleComputer<'a> {
engine_state: &'a EngineState,
stack: &'a Stack,
map: StyleMapping,
}
impl<'a> StyleComputer<'a> {
// This is NOT meant to be used in most cases - please use from_config() instead.
// This only exists for testing purposes.
pub fn new(
engine_state: &'a EngineState,
stack: &'a Stack,
map: StyleMapping,
) -> StyleComputer<'a> {
StyleComputer {
engine_state,
stack,
map,
}
}
// The main method. Takes a string name which maps to a color_config style name,
// and a Nu value to pipe into any closures that may have been defined there.
pub fn compute(&self, style_name: &str, value: &Value) -> Style {
match self.map.get(style_name) {
// Static values require no computation.
Some(ComputableStyle::Static(s)) => *s,
// Closures are run here.
Some(ComputableStyle::Closure(Value::Closure {
val: block_id,
captures,
span,
})) => {
let block = self.engine_state.get_block(*block_id).clone();
// Because captures_to_stack() clones, we don't need to use with_env() here
// (contrast with_env() usage in `each` or `do`).
let mut stack = self.stack.captures_to_stack(captures);
// Support 1-argument blocks as well as 0-argument blocks.
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, value.clone());
}
}
// Run the block.
match eval_block(
self.engine_state,
&mut stack,
&block,
value.clone().into_pipeline_data(),
false,
false,
) {
Ok(v) => {
let value = v.into_value(*span);
// These should be the same color data forms supported by color_config.
match value {
Value::Record { .. } => color_record_to_nustyle(&value),
Value::String { val, .. } => lookup_ansi_color_style(&val),
_ => Style::default(),
}
}
// This is basically a copy of nu_cli::report_error(), but that isn't usable due to
// dependencies. While crudely spitting out a bunch of errors like this is not ideal,
// currently hook closure errors behave roughly the same.
Err(e) => {
eprintln!(
"Error: {:?}",
CliError(&e, &StateWorkingSet::new(self.engine_state))
);
Style::default()
}
}
}
// There should be no other kinds of values (due to create_map() in config.rs filtering them out)
// so this is just a fallback.
_ => Style::default(),
}
}
// Used only by the `table` command.
pub fn style_primitive(&self, value: &Value) -> TextStyle {
let s = self.compute(&value.get_type().to_string(), value);
match *value {
Value::Bool { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Int { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Filesize { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Duration { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Date { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Range { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Float { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::String { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Nothing { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Binary { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::CellPath { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Record { .. } | Value::List { .. } | Value::Block { .. } => {
TextStyle::with_style(AlignmentHorizontal::Left, s)
}
_ => TextStyle::basic_left(),
}
}
// The main constructor.
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
let config = engine_state.get_config();
// Create the hashmap
let mut map: StyleMapping = HashMap::from([
initial!("separator", Color::White.normal()),
initial!(
"leading_trailing_space_bg",
Style::default().on(Color::Rgb(128, 128, 128))
),
initial!("header", Color::White.normal()),
initial!("empty", Color::White.normal()),
initial!("bool", Color::White.normal()),
initial!("int", Color::White.normal()),
initial!("filesize", Color::White.normal()),
initial!("duration", Color::White.normal()),
initial!("date", Color::White.normal()),
initial!("range", Color::White.normal()),
initial!("float", Color::White.normal()),
initial!("string", Color::White.normal()),
initial!("nothing", Color::White.normal()),
initial!("binary", Color::White.normal()),
initial!("cellpath", Color::White.normal()),
initial!("row_index", Color::Green.bold()),
initial!("record", Color::White.normal()),
initial!("list", Color::White.normal()),
initial!("block", Color::White.normal()),
initial!("hints", Color::DarkGray.normal()),
]);
for (key, value) in &config.color_config {
match value {
Value::Closure { .. } => {
map.insert(key.to_string(), ComputableStyle::Closure(value.clone()));
}
Value::Record { .. } => {
map.insert(
key.to_string(),
ComputableStyle::Static(color_record_to_nustyle(value)),
);
}
Value::String { val, .. } => {
// update the stylemap with the found key
let color = lookup_ansi_color_style(val.as_str());
if let Some(v) = map.get_mut(key) {
*v = ComputableStyle::Static(color);
} else {
map.insert(key.to_string(), ComputableStyle::Static(color));
}
}
// This should never occur.
_ => (),
}
}
StyleComputer::new(engine_state, stack, map)
}
}
// Because EngineState doesn't have Debug (Dec 2022),
// this incomplete representation must be used.
impl<'a> Debug for StyleComputer<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("StyleComputer")
.field("map", &self.map)
.finish()
}
}
#[test]
fn test_computable_style_static() {
use nu_protocol::Span;
let style1 = Style::default().italic();
let style2 = Style::default().underline();
// Create a "dummy" style_computer for this test.
let dummy_engine_state = EngineState::new();
let dummy_stack = Stack::new();
let style_computer = StyleComputer::new(
&dummy_engine_state,
&dummy_stack,
HashMap::from([
("string".into(), ComputableStyle::Static(style1)),
("row_index".into(), ComputableStyle::Static(style2)),
]),
);
assert_eq!(
style_computer.compute("string", &Value::nothing(Span::unknown())),
style1
);
assert_eq!(
style_computer.compute("row_index", &Value::nothing(Span::unknown())),
style2
);
}
// Because each closure currently runs in a separate environment, checks that the closures have run
// must use the filesystem.
#[test]
fn test_computable_style_closure_basic() {
use nu_test_support::{nu, nu_repl_code, playground::Playground};
Playground::setup("computable_style_closure_basic", |dirs, _| {
let inp = [
r#"let-env config = {
color_config: {
string: {|e| touch ($e + '.obj'); 'red' }
}
};"#,
"[bell book candle] | table | ignore",
"ls | get name | to nuon",
];
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
assert_eq!(actual_repl.err, "");
assert_eq!(actual_repl.out, "[bell.obj, book.obj, candle.obj]");
});
}
#[test]
fn test_computable_style_closure_errors() {
use nu_test_support::{nu, nu_repl_code};
let inp = [
r#"let-env config = {
color_config: {
string: {|e| $e + 2 }
}
};"#,
"[bell] | table",
];
let actual_repl = nu!(cwd: ".", nu_repl_code(&inp));
// Check that the error was printed
assert!(actual_repl.err.contains("type mismatch for operator"));
// Check that the value was printed
assert!(actual_repl.out.contains("bell"));
}

View File

@ -1,4 +1,5 @@
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use std::fmt::Display;
pub type Alignment = tabled::alignment::AlignmentHorizontal; pub type Alignment = tabled::alignment::AlignmentHorizontal;
@ -239,3 +240,23 @@ impl Default for TextStyle {
Self::new() 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,92 +5,97 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-command" name = "nu-command"
version = "0.72.1" version = "0.75.0"
build = "build.rs" build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
nu-color-config = { path = "../nu-color-config", version = "0.72.1" } nu-color-config = { path = "../nu-color-config", version = "0.75.0" }
nu-engine = { path = "../nu-engine", version = "0.72.1" } nu-engine = { path = "../nu-engine", version = "0.75.0" }
nu-glob = { path = "../nu-glob", version = "0.72.1" } nu-glob = { path = "../nu-glob", version = "0.75.0" }
nu-json = { path = "../nu-json", version = "0.72.1" } nu-json = { path = "../nu-json", version = "0.75.0" }
nu-parser = { path = "../nu-parser", version = "0.72.1" } nu-parser = { path = "../nu-parser", version = "0.75.0" }
nu-path = { path = "../nu-path", version = "0.72.1" } nu-path = { path = "../nu-path", version = "0.75.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.72.1" } nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.75.0" }
nu-protocol = { path = "../nu-protocol", version = "0.72.1" } nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
nu-system = { path = "../nu-system", version = "0.72.1" } nu-system = { path = "../nu-system", version = "0.75.0" }
nu-table = { path = "../nu-table", version = "0.72.1" } nu-table = { path = "../nu-table", version = "0.75.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.72.1" } nu-term-grid = { path = "../nu-term-grid", version = "0.75.0" }
nu-utils = { path = "../nu-utils", version = "0.72.1" } nu-utils = { path = "../nu-utils", version = "0.75.0" }
nu-explore = { path = "../nu-explore", version = "0.75.0" }
nu-ansi-term = "0.46.0" nu-ansi-term = "0.46.0"
num-format = { version = "0.4.3" } num-format = { version = "0.4.3" }
# Potential dependencies for extras # Potential dependencies for extras
alphanumeric-sort = "1.4.4" alphanumeric-sort = "1.4.4"
base64 = "0.13.0" atty = "0.2.14"
base64 = "0.21.0"
byteorder = "1.4.3" byteorder = "1.4.3"
bytesize = "1.1.0" bytesize = "1.1.0"
calamine = "0.18.0" calamine = "0.19.1"
chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false } chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false }
chrono-humanize = "0.2.1" chrono-humanize = "0.2.1"
chrono-tz = "0.6.3" chrono-tz = "0.8.1"
crossterm = "0.24.0" crossterm = "0.24.0"
csv = "1.1.6" csv = "1.1.6"
dialoguer = { default-features = false, version = "0.9.0" } dialoguer = { default-features = false, version = "0.10.3" }
digest = { default-features = false, version = "0.10.0" } digest = { default-features = false, version = "0.10.0" }
dtparse = "1.2.0" dtparse = "1.2.0"
eml-parser = "0.1.0" eml-parser = "0.1.0"
encoding_rs = "0.8.30" encoding_rs = "0.8.30"
fancy-regex = "0.10.0" fancy-regex = "0.11.0"
filesize = "0.2.0" filesize = "0.2.0"
filetime = "0.2.15" filetime = "0.2.15"
fs_extra = "1.2.0" fs_extra = "1.2.0"
htmlescape = "0.3.1" htmlescape = "0.3.1"
ical = "0.7.0" ical = "0.7.0"
indexmap = { version="1.7", features=["serde-1"] } indexmap = { version = "1.7", features = ["serde-1"] }
indicatif = "0.17.2"
Inflector = "0.11" Inflector = "0.11"
is-root = "0.1.2" is-root = "0.1.2"
itertools = "0.10.0" itertools = "0.10.0"
lazy_static = "1.4.0"
log = "0.4.14" log = "0.4.14"
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false } lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
md5 = { package = "md-5", version = "0.10.0" } md5 = { package = "md-5", version = "0.10.0" }
meval = "0.2.0"
mime = "0.3.16" mime = "0.3.16"
mime_guess = "2.0.4"
notify = "4.0.17" notify = "4.0.17"
num = { version = "0.4.0", optional = true } num = { version = "0.4.0", optional = true }
num-traits = "0.2.14" num-traits = "0.2.14"
once_cell = "1.0" once_cell = "1.17"
open = "3.2.0"
pathdiff = "0.2.1" pathdiff = "0.2.1"
powierza-coefficient = "1.0.1" powierza-coefficient = "1.0.2"
quick-xml = "0.23.0" quick-xml = "0.27"
rand = "0.8" rand = "0.8"
rayon = "1.5.1" rayon = "1.6.1"
reqwest = {version = "0.11", features = ["blocking", "json"] } regex = "1.7.1"
roxmltree = "0.14.0" reqwest = { version = "0.11", features = ["blocking", "json"] }
roxmltree = "0.17.0"
rust-embed = "6.3.0" rust-embed = "6.3.0"
same-file = "1.0.6" same-file = "1.0.6"
serde = { version="1.0.123", features=["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_ini = "0.2.0" serde_ini = "0.2.0"
serde_urlencoded = "0.7.0" serde_urlencoded = "0.7.0"
serde_yaml = "0.9.4" serde_yaml = "0.9.4"
sha2 = "0.10.0" sha2 = "0.10.0"
# Disable default features b/c the default features build Git (very slow to compile) # Disable default features b/c the default features build Git (very slow to compile)
shadow-rs = { version = "0.16.1", default-features = false } shadow-rs = { version = "0.20.0", default-features = false }
sysinfo = "0.26.2" sysinfo = "0.27.7"
terminal_size = "0.2.1" terminal_size = "0.2.1"
thiserror = "1.0.31" thiserror = "1.0.31"
titlecase = "2.0.0" titlecase = "2.0.0"
toml = "0.5.8" toml = "0.5.8"
unicode-segmentation = "1.8.0" unicode-segmentation = "1.8.0"
url = "2.2.1" url = "2.2.1"
uuid = { version = "1.1.2", features = ["v4"] } percent-encoding = "2.2.0"
uuid = { version = "1.2.2", features = ["v4"] }
which = { version = "4.3.0", optional = true } which = { version = "4.3.0", optional = true }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]} reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
wax = { version = "0.5.0" } wax = { version = "0.5.0" }
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true } rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.23.0", features = ["serde"], optional = true } sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winreg = "0.10.1" winreg = "0.10.1"
@ -101,11 +106,11 @@ users = "0.11.0"
libc = "0.2" libc = "0.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
version = "3.0.0" version = "3.0.1"
optional = true optional = true
[dependencies.polars] [dependencies.polars]
version = "0.25.0" version = "0.26.1"
optional = true optional = true
features = [ features = [
"arg_where", "arg_where",
@ -136,29 +141,25 @@ features = [
] ]
[target.'cfg(windows)'.dependencies.windows] [target.'cfg(windows)'.dependencies.windows]
version = "0.43.0" version = "0.44.0"
features = [ features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_SystemServices",
]
[features] [features]
trash-support = ["trash"] trash-support = ["trash"]
which-support = ["which"] which-support = ["which"]
plugin = ["nu-parser/plugin"] plugin = ["nu-parser/plugin"]
dataframe = ["polars", "num", "sqlparser"] dataframe = ["polars", "num", "sqlparser"]
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?
[build-dependencies] [build-dependencies]
shadow-rs = { version = "0.16.1", default-features = false } shadow-rs = { version = "0.20.0", default-features = false }
[dev-dependencies] [dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.72.1" } nu-test-support = { path = "../nu-test-support", version = "0.75.0" }
hamcrest2 = "0.3.0" hamcrest2 = "0.3.0"
dirs-next = "2.0.0" dirs-next = "2.0.0"
proptest = "1.0.0" proptest = "1.0.0"
quickcheck = "1.0.3" quickcheck = "1.0.3"
quickcheck_macros = "1.0.0" quickcheck_macros = "1.0.0"
rstest = {version = "0.15.0", default-features = false} rstest = { version = "0.15.0", default-features = false }

View File

@ -4,7 +4,7 @@ fn main() -> shadow_rs::SdResult<()> {
// Look up the current Git commit ourselves instead of relying on shadow_rs, // Look up the current Git commit ourselves instead of relying on shadow_rs,
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2) // because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
let hash = get_git_hash().unwrap_or_default(); let hash = get_git_hash().unwrap_or_default();
println!("cargo:rustc-env=NU_COMMIT_HASH={}", hash); println!("cargo:rustc-env=NU_COMMIT_HASH={hash}");
shadow_rs::new() shadow_rs::new()
} }

View File

@ -43,6 +43,10 @@ impl Command for SubCommand {
let head = call.head; let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?; let target: i64 = call.req(engine_state, stack, 0)?;
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, target, head), move |value| operate(value, target, head),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -54,10 +58,7 @@ impl Command for SubCommand {
Example { Example {
description: "Apply bits and to two numbers", description: "Apply bits and to two numbers",
example: "2 | bits and 2", example: "2 | bits and 2",
result: Some(Value::Int { result: Some(Value::test_int(2)),
val: 2,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Apply logical and to a list of numbers", description: "Apply logical and to a list of numbers",
@ -77,13 +78,15 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
val: val & target, val: val & target,
span, span,
}, },
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Only integer values are supported, input type: {:?}", other.get_type().to_string(),
other.get_type() head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(head), other.expect_span(),
), ),
}, },
} }

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value, Category, IntoPipelineData, PipelineData, Signature, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,13 +14,19 @@ impl Command for Bits {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits").category(Category::Bits) Signature::build("bits")
.category(Category::Bits)
.input_output_types(vec![(Type::Nothing, Type::String)])
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Various commands for working with bits" "Various commands for working with bits"
} }
fn extra_usage(&self) -> &str {
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -41,15 +47,3 @@ impl Command for Bits {
.into_pipeline_data()) .into_pipeline_data())
} }
} }
#[cfg(test)]
mod test {
use crate::Bits;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Bits {})
}
}

View File

@ -56,11 +56,17 @@ impl Command for SubCommand {
if let Some(val) = number_bytes { if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(), "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span, val.span,
)); ));
} }
} }
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, head, signed, bytes_len), move |value| operate(value, head, signed, bytes_len),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -140,14 +146,17 @@ fn operate(value: Value, head: Span, signed: bool, number_size: NumberBytes) ->
Value::Int { val: out_val, span } Value::Int { val: out_val, span }
} }
} }
other => Value::Error { other => match other {
error: ShellError::UnsupportedInput( // Propagate errors inside the value
format!( Value::Error { .. } => other,
"Only numerical values are supported, input type: {:?}", _ => Value::Error {
other.get_type() error: ShellError::OnlySupportsThisInputType(
"numeric".into(),
other.get_type().to_string(),
head,
other.expect_span(),
), ),
other.span().unwrap_or(head), },
),
}, },
} }
} }

View File

@ -43,6 +43,10 @@ impl Command for SubCommand {
let head = call.head; let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?; let target: i64 = call.req(engine_state, stack, 0)?;
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, target, head), move |value| operate(value, target, head),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -54,10 +58,7 @@ impl Command for SubCommand {
Example { Example {
description: "Apply bits or to two numbers", description: "Apply bits or to two numbers",
example: "2 | bits or 6", example: "2 | bits or 6",
result: Some(Value::Int { result: Some(Value::test_int(6)),
val: 6,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Apply logical or to a list of numbers", description: "Apply logical or to a list of numbers",
@ -77,13 +78,15 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
val: val | target, val: val | target,
span, span,
}, },
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Only integer values are supported, input type: {:?}", other.get_type().to_string(),
other.get_type() head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(head), other.expect_span(),
), ),
}, },
} }

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes { if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(), "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span, val.span,
)); ));
} }
} }
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, bits, head, signed, bytes_len), move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -76,10 +81,7 @@ impl Command for SubCommand {
Example { Example {
description: "Rotate left a number with 2 bits", description: "Rotate left a number with 2 bits",
example: "17 | bits rol 2", example: "17 | bits rol 2",
result: Some(Value::Int { result: Some(Value::test_int(68)),
val: 68,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Rotate left a list of numbers with 2 bits", description: "Rotate left a list of numbers with 2 bits",
@ -104,8 +106,7 @@ where
error: ShellError::GenericError( error: ShellError::GenericError(
"Rotate left result beyond the range of 64 bit signed number".to_string(), "Rotate left result beyond the range of 64 bit signed number".to_string(),
format!( format!(
"{} of the specified number of bytes rotate left {} bits exceed limit", "{val} of the specified number of bytes rotate left {bits} bits exceed limit"
val, bits
), ),
Some(span), Some(span),
None, None,
@ -130,16 +131,18 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedOne => get_rotate_left(val as i8, bits, span), SignedOne => get_rotate_left(val as i8, bits, span),
SignedTwo => get_rotate_left(val as i16, bits, span), SignedTwo => get_rotate_left(val as i16, bits, span),
SignedFour => get_rotate_left(val as i32, bits, span), SignedFour => get_rotate_left(val as i32, bits, span),
SignedEight => get_rotate_left(val as i64, bits, span), SignedEight => get_rotate_left(val, bits, span),
} }
} }
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Only integer values are supported, input type: {:?}", other.get_type().to_string(),
other.get_type() head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(head), other.expect_span(),
), ),
}, },
} }

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes { if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(), "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span, val.span,
)); ));
} }
} }
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, bits, head, signed, bytes_len), move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -76,10 +81,7 @@ impl Command for SubCommand {
Example { Example {
description: "Rotate right a number with 60 bits", description: "Rotate right a number with 60 bits",
example: "17 | bits ror 60", example: "17 | bits ror 60",
result: Some(Value::Int { result: Some(Value::test_int(272)),
val: 272,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Rotate right a list of numbers of one byte", description: "Rotate right a list of numbers of one byte",
@ -108,8 +110,7 @@ where
error: ShellError::GenericError( error: ShellError::GenericError(
"Rotate right result beyond the range of 64 bit signed number".to_string(), "Rotate right result beyond the range of 64 bit signed number".to_string(),
format!( format!(
"{} of the specified number of bytes rotate right {} bits exceed limit", "{val} of the specified number of bytes rotate right {bits} bits exceed limit"
val, bits
), ),
Some(span), Some(span),
None, None,
@ -134,16 +135,18 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedOne => get_rotate_right(val as i8, bits, span), SignedOne => get_rotate_right(val as i8, bits, span),
SignedTwo => get_rotate_right(val as i16, bits, span), SignedTwo => get_rotate_right(val as i16, bits, span),
SignedFour => get_rotate_right(val as i32, bits, span), SignedFour => get_rotate_right(val as i32, bits, span),
SignedEight => get_rotate_right(val as i64, bits, span), SignedEight => get_rotate_right(val, bits, span),
} }
} }
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Only integer values are supported, input type: {:?}", other.get_type().to_string(),
other.get_type() head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(head), other.expect_span(),
), ),
}, },
} }

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes { if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(), "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span, val.span,
)); ));
} }
} }
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, bits, head, signed, bytes_len), move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -76,26 +81,17 @@ impl Command for SubCommand {
Example { Example {
description: "Shift left a number by 7 bits", description: "Shift left a number by 7 bits",
example: "2 | bits shl 7", example: "2 | bits shl 7",
result: Some(Value::Int { result: Some(Value::test_int(256)),
val: 256,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift left a number with 1 byte by 7 bits", description: "Shift left a number with 1 byte by 7 bits",
example: "2 | bits shl 7 -n 1", example: "2 | bits shl 7 -n 1",
result: Some(Value::Int { result: Some(Value::test_int(0)),
val: 0,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift left a signed number by 1 bit", description: "Shift left a signed number by 1 bit",
example: "0x7F | bits shl 1 -s", example: "0x7F | bits shl 1 -s",
result: Some(Value::Int { result: Some(Value::test_int(254)),
val: 254,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift left a list of numbers", description: "Shift left a list of numbers",
@ -122,8 +118,7 @@ where
error: ShellError::GenericError( error: ShellError::GenericError(
"Shift left result beyond the range of 64 bit signed number".to_string(), "Shift left result beyond the range of 64 bit signed number".to_string(),
format!( format!(
"{} of the specified number of bytes shift left {} bits exceed limit", "{val} of the specified number of bytes shift left {bits} bits exceed limit"
val, bits
), ),
Some(span), Some(span),
None, None,
@ -135,10 +130,7 @@ where
None => Value::Error { None => Value::Error {
error: ShellError::GenericError( error: ShellError::GenericError(
"Shift left failed".to_string(), "Shift left failed".to_string(),
format!( format!("{val} shift left {bits} bits failed, you may shift too many bits"),
"{} shift left {} bits failed, you may shift too many bits",
val, bits
),
Some(span), Some(span),
None, None,
Vec::new(), Vec::new(),
@ -162,16 +154,18 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedOne => get_shift_left(val as i8, bits, span), SignedOne => get_shift_left(val as i8, bits, span),
SignedTwo => get_shift_left(val as i16, bits, span), SignedTwo => get_shift_left(val as i16, bits, span),
SignedFour => get_shift_left(val as i32, bits, span), SignedFour => get_shift_left(val as i32, bits, span),
SignedEight => get_shift_left(val as i64, bits, span), SignedEight => get_shift_left(val, bits, span),
} }
} }
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Only integer values are supported, input type: {:?}", other.get_type().to_string(),
other.get_type() head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(head), other.expect_span(),
), ),
}, },
} }

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes { if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(), "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span, val.span,
)); ));
} }
} }
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, bits, head, signed, bytes_len), move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -76,10 +81,7 @@ impl Command for SubCommand {
Example { Example {
description: "Shift right a number with 2 bits", description: "Shift right a number with 2 bits",
example: "8 | bits shr 2", example: "8 | bits shr 2",
result: Some(Value::Int { result: Some(Value::test_int(2)),
val: 2,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift right a list of numbers", description: "Shift right a list of numbers",
@ -106,8 +108,7 @@ where
error: ShellError::GenericError( error: ShellError::GenericError(
"Shift right result beyond the range of 64 bit signed number".to_string(), "Shift right result beyond the range of 64 bit signed number".to_string(),
format!( format!(
"{} of the specified number of bytes shift right {} bits exceed limit", "{val} of the specified number of bytes shift right {bits} bits exceed limit"
val, bits
), ),
Some(span), Some(span),
None, None,
@ -119,10 +120,7 @@ where
None => Value::Error { None => Value::Error {
error: ShellError::GenericError( error: ShellError::GenericError(
"Shift right failed".to_string(), "Shift right failed".to_string(),
format!( format!("{val} shift right {bits} bits failed, you may shift too many bits"),
"{} shift right {} bits failed, you may shift too many bits",
val, bits
),
Some(span), Some(span),
None, None,
Vec::new(), Vec::new(),
@ -146,16 +144,18 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedOne => get_shift_right(val as i8, bits, span), SignedOne => get_shift_right(val as i8, bits, span),
SignedTwo => get_shift_right(val as i16, bits, span), SignedTwo => get_shift_right(val as i16, bits, span),
SignedFour => get_shift_right(val as i32, bits, span), SignedFour => get_shift_right(val as i32, bits, span),
SignedEight => get_shift_right(val as i64, bits, span), SignedEight => get_shift_right(val, bits, span),
} }
} }
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Only integer values are supported, input type: {:?}", other.get_type().to_string(),
other.get_type() head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(head), other.expect_span(),
), ),
}, },
} }

View File

@ -42,7 +42,10 @@ impl Command for SubCommand {
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head; let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?; let target: i64 = call.req(engine_state, stack, 0)?;
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map( input.map(
move |value| operate(value, target, head), move |value| operate(value, target, head),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
@ -54,10 +57,7 @@ impl Command for SubCommand {
Example { Example {
description: "Apply bits xor to two numbers", description: "Apply bits xor to two numbers",
example: "2 | bits xor 2", example: "2 | bits xor 2",
result: Some(Value::Int { result: Some(Value::test_int(0)),
val: 0,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Apply logical xor to a list of numbers", description: "Apply logical xor to a list of numbers",
@ -77,13 +77,15 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
val: val ^ target, val: val ^ target,
span, span,
}, },
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Only integer values are supported, input type: {:?}", other.get_type().to_string(),
other.get_type() head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(head), other.expect_span(),
), ),
}, },
} }

View File

@ -122,13 +122,15 @@ fn add(val: &Value, args: &Arguments, span: Span) -> Value {
val, val,
span: val_span, span: val_span,
} => add_impl(val, args, *val_span), } => add_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -30,7 +30,9 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
Value::List { mut vals, span } => { Value::List { mut vals, span } => {
if vals.len() != 2 { if vals.len() != 2 {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"More than two indices given".to_string(), "More than two indices in range".to_string(),
"value originates from here".to_string(),
head,
span, span,
)); ));
} else { } else {
@ -38,10 +40,14 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
let end = match end { let end = match end {
Value::Int { val, .. } => val.to_string(), Value::Int { val, .. } => val.to_string(),
Value::String { val, .. } => val, Value::String { val, .. } => val,
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(error),
other => { other => {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"could not perform subbytes. Expecting a string or int".to_string(), "Only string or list<int> ranges are supported".into(),
other.span().unwrap_or(head), format!("input type: {:?}", other.get_type()),
head,
other.expect_span(),
)) ))
} }
}; };
@ -49,10 +55,14 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
let start = match start { let start = match start {
Value::Int { val, .. } => val.to_string(), Value::Int { val, .. } => val.to_string(),
Value::String { val, .. } => val, Value::String { val, .. } => val,
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(error),
other => { other => {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"could not perform subbytes. Expecting a string or int".to_string(), "Only string or list<int> ranges are supported".into(),
other.span().unwrap_or(head), format!("input type: {:?}", other.get_type()),
head,
other.expect_span(),
)) ))
} }
}; };
@ -60,21 +70,27 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
} }
} }
Value::String { val, span } => { Value::String { val, span } => {
let splitted_result = val.split_once(','); let split_result = val.split_once(',');
match splitted_result { match split_result {
Some((start, end)) => (start.to_string(), end.to_string(), span), Some((start, end)) => (start.to_string(), end.to_string(), span),
None => { None => {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"could not perform subbytes".to_string(), "could not perform subbytes".to_string(),
"with this range".to_string(),
head,
span, span,
)) ))
} }
} }
} }
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(error),
other => { other => {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
"could not perform subbytes".to_string(), "could not perform subbytes".to_string(),
other.span().unwrap_or(head), "with this range".to_string(),
head,
other.expect_span(),
)) ))
} }
}; };
@ -82,28 +98,26 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
let start: isize = if start.is_empty() || start == "_" { let start: isize = if start.is_empty() || start == "_" {
0 0
} else { } else {
match start.trim().parse() { start.trim().parse().map_err(|_| {
Ok(s) => s, ShellError::UnsupportedInput(
Err(_) => { "could not perform subbytes".to_string(),
return Err(ShellError::UnsupportedInput( "with this range".to_string(),
"could not perform subbytes".to_string(), head,
span, span,
)) )
} })?
}
}; };
let end: isize = if end.is_empty() || end == "_" { let end: isize = if end.is_empty() || end == "_" {
isize::max_value() isize::max_value()
} else { } else {
match end.trim().parse() { end.trim().parse().map_err(|_| {
Ok(s) => s, ShellError::UnsupportedInput(
Err(_) => { "could not perform subbytes".to_string(),
return Err(ShellError::UnsupportedInput( "with this range".to_string(),
"could not perform subbytes".to_string(), head,
span, span,
)) )
} })?
}
}; };
Ok((start, end, span)) Ok((start, end, span))
} }
@ -232,13 +246,15 @@ fn at(val: &Value, args: &Arguments, span: Span) -> Value {
val, val,
span: val_span, span: val_span,
} => at_impl(val, args, *val_span), } => at_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }
@ -262,7 +278,7 @@ fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
match start.cmp(&end) { match start.cmp(&end) {
Ordering::Equal => Value::Binary { val: vec![], span }, Ordering::Equal => Value::Binary { val: vec![], span },
Ordering::Greater => Value::Error { Ordering::Greater => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::TypeMismatch(
"End must be greater than or equal to Start".to_string(), "End must be greater than or equal to Start".to_string(),
arg.arg_span, arg.arg_span,
), ),

View File

@ -52,10 +52,12 @@ impl Command for BytesBuild {
let val = eval_expression(engine_state, stack, expr)?; let val = eval_expression(engine_state, stack, expr)?;
match val { match val {
Value::Binary { mut val, .. } => output.append(&mut val), Value::Binary { mut val, .. } => output.append(&mut val),
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(error),
other => { other => {
return Err(ShellError::UnsupportedInput( return Err(ShellError::TypeMismatch(
"only support expression which yields to binary data".to_string(), "only binary data arguments are supported".to_string(),
other.span().unwrap_or(call.head), other.expect_span(),
)) ))
} }
} }

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value, Category, IntoPipelineData, PipelineData, Signature, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,13 +14,19 @@ impl Command for Bytes {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes").category(Category::Bytes) Signature::build("bytes")
.category(Category::Bytes)
.input_output_types(vec![(Type::Nothing, Type::String)])
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Various commands for working with byte data" "Various commands for working with byte data"
} }
fn extra_usage(&self) -> &str {
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -41,15 +47,3 @@ impl Command for Bytes {
.into_pipeline_data()) .into_pipeline_data())
} }
} }
#[cfg(test)]
mod test {
use crate::Bytes;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Bytes {})
}
}

View File

@ -54,14 +54,16 @@ impl Command for BytesCollect {
output_binary.append(&mut work_sep) output_binary.append(&mut work_sep)
} }
} }
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(error),
other => { other => {
return Err(ShellError::UnsupportedInput( return Err(ShellError::OnlySupportsThisInputType(
format!( "integer".into(),
"The element type is {}, this command only works with bytes.", other.get_type().to_string(),
other.get_type() call.head,
), // This line requires the Value::Error match above.
other.span().unwrap_or(call.head), other.expect_span(),
)) ));
} }
} }
} }

View File

@ -68,26 +68,17 @@ impl Command for BytesEndsWith {
Example { Example {
description: "Checks if binary ends with `0x[AA]`", description: "Checks if binary ends with `0x[AA]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]", example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]",
result: Some(Value::Bool { result: Some(Value::test_bool(true)),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary ends with `0x[FF AA AA]`", description: "Checks if binary ends with `0x[FF AA AA]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]", example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]",
result: Some(Value::Bool { result: Some(Value::test_bool(true)),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary ends with `0x[11]`", description: "Checks if binary ends with `0x[11]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[11]", example: "0x[1F FF AA AA] | bytes ends-with 0x[11]",
result: Some(Value::Bool { result: Some(Value::test_bool(false)),
val: false,
span: Span::test_data(),
}),
}, },
] ]
} }
@ -98,17 +89,16 @@ fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
Value::Binary { Value::Binary {
val, val,
span: val_span, span: val_span,
} => Value::Bool { } => Value::boolean(val.ends_with(&args.pattern), *val_span),
val: val.ends_with(&args.pattern), // Propagate errors by explicitly matching them before the final case.
span: *val_span, Value::Error { .. } => val.clone(),
},
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "binary".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -132,13 +132,15 @@ fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
val, val,
span: val_span, span: val_span,
} => index_of_impl(val, args, *val_span), } => index_of_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "binary".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -70,17 +70,16 @@ fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
Value::Binary { Value::Binary {
val, val,
span: val_span, span: val_span,
} => Value::Int { } => Value::int(val.len() as i64, *val_span),
val: val.len() as i64, // Propagate errors by explicitly matching them before the final case.
span: *val_span, Value::Error { .. } => val.clone(),
},
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "binary".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -61,7 +61,7 @@ impl Command for BytesRemove {
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths); let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?; let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if pattern_to_remove.item.is_empty() { if pattern_to_remove.item.is_empty() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::TypeMismatch(
"the pattern to remove cannot be empty".to_string(), "the pattern to remove cannot be empty".to_string(),
pattern_to_remove.span, pattern_to_remove.span,
)); ));
@ -139,13 +139,15 @@ fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
val, val,
span: val_span, span: val_span,
} => remove_impl(val, args, *val_span), } => remove_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "binary".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }
@ -159,7 +161,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
// Note: // Note:
// remove_all from start and end will generate the same result. // remove_all from start and end will generate the same result.
// so we'll put `remove_all` relative logic into else clouse. // so we'll put `remove_all` relative logic into else clause.
if arg.end && !remove_all { if arg.end && !remove_all {
let (mut left, mut right) = ( let (mut left, mut right) = (
input.len() as isize - arg.pattern.len() as isize, input.len() as isize - arg.pattern.len() as isize,
@ -170,7 +172,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
left -= 1; left -= 1;
right -= 1; right -= 1;
} }
// append the remaining thing to result, this can be happeneed when // append the remaining thing to result, this can be happening when
// we have something to remove and remove_all is False. // we have something to remove and remove_all is False.
let mut remain = input[..left as usize].iter().copied().rev().collect(); let mut remain = input[..left as usize].iter().copied().rev().collect();
result.append(&mut remain); result.append(&mut remain);
@ -191,7 +193,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
right += 1; right += 1;
} }
} }
// append the remaing thing to result, this can happened when // append the remaining thing to result, this can happened when
// we have something to remove and remove_all is False. // we have something to remove and remove_all is False.
let mut remain = input[left..].to_vec(); let mut remain = input[left..].to_vec();
result.append(&mut remain); result.append(&mut remain);

View File

@ -61,7 +61,7 @@ impl Command for BytesReplace {
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths); let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?; let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if find.item.is_empty() { if find.item.is_empty() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::TypeMismatch(
"the pattern to find cannot be empty".to_string(), "the pattern to find cannot be empty".to_string(),
find.span, find.span,
)); ));
@ -130,13 +130,15 @@ fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
val, val,
span: val_span, span: val_span,
} => replace_impl(val, args, *val_span), } => replace_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "binary".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -81,13 +81,15 @@ fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
span: *val_span, span: *val_span,
} }
} }
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "binary".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -74,26 +74,17 @@ impl Command for BytesStartsWith {
Example { Example {
description: "Checks if binary starts with `0x[1F FF AA]`", description: "Checks if binary starts with `0x[1F FF AA]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]", example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
result: Some(Value::Bool { result: Some(Value::test_bool(true)),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary starts with `0x[1F]`", description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]", example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
result: Some(Value::Bool { result: Some(Value::test_bool(true)),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary starts with `0x[1F]`", description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]", example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
result: Some(Value::Bool { result: Some(Value::test_bool(false)),
val: false,
span: Span::test_data(),
}),
}, },
] ]
} }
@ -104,17 +95,16 @@ fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
Value::Binary { Value::Binary {
val, val,
span: val_span, span: val_span,
} => Value::Bool { } => Value::boolean(val.starts_with(&args.pattern), *val_span),
val: val.starts_with(&args.pattern), // Propagate errors by explicitly matching them before the final case.
span: *val_span, Value::Error { .. } => val.clone(),
},
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!( "binary".into(),
"Input's type is {}. This command only works with bytes.", other.get_type().to_string(),
other.get_type()
),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -53,7 +53,7 @@ impl Default for HashableValue {
fn default() -> Self { fn default() -> Self {
HashableValue::Bool { HashableValue::Bool {
val: false, val: false,
span: Span { start: 0, end: 0 }, span: Span::unknown(),
} }
} }
} }
@ -78,13 +78,14 @@ impl HashableValue {
Value::String { val, span } => Ok(HashableValue::String { val, span }), Value::String { val, span } => Ok(HashableValue::String { val, span }),
Value::Binary { val, span } => Ok(HashableValue::Binary { val, span }), Value::Binary { val, span } => Ok(HashableValue::Binary { val, span }),
_ => { // Explicitly propagate errors instead of dropping them.
let input_span = value.span().unwrap_or(span); Value::Error { error } => Err(error),
Err(ShellError::UnsupportedInput( _ => Err(ShellError::UnsupportedInput(
format!("input value {value:?} is not hashable"), "input value is not hashable".into(),
input_span, format!("input type: {:?}", value.get_type()),
)) span,
} value.expect_span(),
)),
} }
} }
@ -214,7 +215,7 @@ mod test {
]; ];
for (val, expect_hashable_val) in values.into_iter() { for (val, expect_hashable_val) in values.into_iter() {
assert_eq!( assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 }).unwrap(), HashableValue::from_value(val, Span::unknown()).unwrap(),
expect_hashable_val expect_hashable_val
); );
} }
@ -245,7 +246,7 @@ mod test {
}, },
]; ];
for v in values { for v in values {
assert!(HashableValue::from_value(v, Span { start: 0, end: 0 }).is_err()) assert!(HashableValue::from_value(v, Span::unknown()).is_err())
} }
} }
@ -266,7 +267,7 @@ mod test {
for val in values.into_iter() { for val in values.into_iter() {
let expected_val = val.clone(); let expected_val = val.clone();
assert_eq!( assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 }) HashableValue::from_value(val, Span::unknown())
.unwrap() .unwrap()
.into_value(), .into_value(),
expected_val expected_val
@ -279,14 +280,11 @@ mod test {
assert_eq!( assert_eq!(
HashableValue::Bool { HashableValue::Bool {
val: true, val: true,
span: Span { start: 0, end: 1 } span: Span::new(0, 1)
}, },
HashableValue::Bool { HashableValue::Bool {
val: true, val: true,
span: Span { span: Span::new(90, 1000)
start: 90,
end: 1000
}
} }
) )
} }
@ -299,7 +297,7 @@ mod test {
assert!(set.contains(&HashableValue::Bool { val: true, span })); assert!(set.contains(&HashableValue::Bool { val: true, span }));
// hashable value doesn't care about span. // hashable value doesn't care about span.
let diff_span = Span { start: 1, end: 2 }; let diff_span = Span::new(1, 2);
set.insert(HashableValue::Bool { set.insert(HashableValue::Bool {
val: true, val: true,
span: diff_span, span: diff_span,

View File

@ -50,7 +50,7 @@ impl Command for Histogram {
}, },
Example { Example {
description: "Compute a histogram for a list of numbers", description: "Compute a histogram for a list of numbers",
example: "echo [1 2 1] | histogram", example: "[1 2 1] | histogram",
result: Some(Value::List { result: Some(Value::List {
vals: vec![Value::Record { vals: vec![Value::Record {
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()], cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
@ -80,7 +80,7 @@ impl Command for Histogram {
}, },
Example { Example {
description: "Compute a histogram for a list of numbers, and percentage is based on the maximum value", description: "Compute a histogram for a list of numbers, and percentage is based on the maximum value",
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative", example: "[1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative",
result: None, result: None,
} }
] ]
@ -98,12 +98,11 @@ impl Command for Histogram {
let frequency_name_arg = call.opt::<Spanned<String>>(engine_state, stack, 1)?; let frequency_name_arg = call.opt::<Spanned<String>>(engine_state, stack, 1)?;
let frequency_column_name = match frequency_name_arg { let frequency_column_name = match frequency_name_arg {
Some(inner) => { Some(inner) => {
let span = inner.span;
if ["value", "count", "quantile", "percentage"].contains(&inner.item.as_str()) { if ["value", "count", "quantile", "percentage"].contains(&inner.item.as_str()) {
return Err(ShellError::UnsupportedInput( return Err(ShellError::TypeMismatch(
"frequency-column-name can't be 'value', 'count' or 'percentage'" "frequency-column-name can't be 'value', 'count' or 'percentage'"
.to_string(), .to_string(),
span, inner.span,
)); ));
} }
inner.item inner.item
@ -119,7 +118,7 @@ impl Command for Histogram {
"normalize" => PercentageCalcMethod::Normalize, "normalize" => PercentageCalcMethod::Normalize,
"relative" => PercentageCalcMethod::Relative, "relative" => PercentageCalcMethod::Relative,
_ => { _ => {
return Err(ShellError::UnsupportedInput( return Err(ShellError::TypeMismatch(
"calc method can only be 'normalize' or 'relative'".to_string(), "calc method can only be 'normalize' or 'relative'".to_string(),
inner.span, inner.span,
)) ))
@ -130,16 +129,15 @@ impl Command for Histogram {
let span = call.head; let span = call.head;
let data_as_value = input.into_value(span); let data_as_value = input.into_value(span);
// `input` is not a list, here we can return an error. // `input` is not a list, here we can return an error.
match data_as_value.as_list() { run_histogram(
Ok(list_value) => run_histogram( data_as_value.as_list()?.to_vec(),
list_value.to_vec(), column_name,
column_name, frequency_column_name,
frequency_column_name, calc_method,
calc_method, span,
span, // Note that as_list() filters out Value::Error here.
), data_as_value.expect_span(),
Err(e) => Err(e), )
}
} }
} }
@ -149,6 +147,7 @@ fn run_histogram(
freq_column: String, freq_column: String,
calc_method: PercentageCalcMethod, calc_method: PercentageCalcMethod,
head_span: Span, head_span: Span,
list_span: Span,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let mut inputs = vec![]; let mut inputs = vec![];
// convert from inputs to hashable values. // convert from inputs to hashable values.
@ -157,14 +156,24 @@ fn run_histogram(
// some invalid input scenario needs to handle: // some invalid input scenario needs to handle:
// Expect input is a list of hashable value, if one value is not hashable, throw out error. // Expect input is a list of hashable value, if one value is not hashable, throw out error.
for v in values { for v in values {
let current_span = v.span().unwrap_or(head_span); match v {
inputs.push(HashableValue::from_value(v, head_span).map_err(|_| { // Propagate existing errors.
ShellError::UnsupportedInput( Value::Error { error } => return Err(error),
"--column-name is not provided, can only support a list of simple value." _ => {
.to_string(), let t = v.get_type();
current_span, let span = v.expect_span();
) inputs.push(HashableValue::from_value(v, head_span).map_err(|_| {
})?); ShellError::UnsupportedInput(
"Since --column-name was not provided, only lists of hashable values are supported.".to_string(),
format!(
"input type: {t:?}"
),
head_span,
span,
)
})?)
}
}
} }
} }
Some(ref col) => { Some(ref col) => {
@ -186,14 +195,17 @@ fn run_histogram(
} }
} }
} }
// Propagate existing errors.
Value::Error { error } => return Err(error),
_ => continue, _ => continue,
} }
} }
if inputs.is_empty() { if inputs.is_empty() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::CantFindColumn(
format!("expect input is table, and inputs doesn't contain any value which has {col_name} column"), col_name.clone(),
head_span, head_span,
list_span,
)); ));
} }
} }
@ -249,25 +261,33 @@ fn histogram_impl(
let percentage = format!("{:.2}%", quantile * 100_f64); let percentage = format!("{:.2}%", quantile * 100_f64);
let freq = "*".repeat((MAX_FREQ_COUNT * quantile).floor() as usize); let freq = "*".repeat((MAX_FREQ_COUNT * quantile).floor() as usize);
result.push(Value::Record { result.push((
cols: result_cols.clone(), count, // attach count first for easily sorting.
vals: vec![ Value::Record {
val.into_value(), cols: result_cols.clone(),
Value::Int { val: count, span }, vals: vec![
Value::Float { val.into_value(),
val: quantile, Value::Int { val: count, span },
span, Value::Float {
}, val: quantile,
Value::String { span,
val: percentage, },
span, Value::String {
}, val: percentage,
Value::String { val: freq, span }, span,
], },
span, Value::String { val: freq, span },
}); ],
span,
},
));
} }
Value::List { vals: result, span }.into_pipeline_data() result.sort_by(|a, b| b.0.cmp(&a.0));
Value::List {
vals: result.into_iter().map(|x| x.1).collect(),
span,
}
.into_pipeline_data()
} }
#[cfg(test)] #[cfg(test)]

View File

@ -44,38 +44,14 @@ impl Command for Fmt {
"upperhex".into(), "upperhex".into(),
], ],
vals: vec![ vals: vec![
Value::String { Value::test_string("0b101010"),
val: "0b101010".to_string(), Value::test_string("42"),
span: Span::test_data(), Value::test_string("42"),
}, Value::test_string("4.2e1"),
Value::String { Value::test_string("0x2a"),
val: "42".to_string(), Value::test_string("0o52"),
span: Span::test_data(), Value::test_string("4.2E1"),
}, Value::test_string("0x2A"),
Value::String {
val: "42".to_string(),
span: Span::test_data(),
},
Value::String {
val: "4.2e1".to_string(),
span: Span::test_data(),
},
Value::String {
val: "0x2a".to_string(),
span: Span::test_data(),
},
Value::String {
val: "0o52".to_string(),
span: Span::test_data(),
},
Value::String {
val: "4.2E1".to_string(),
span: Span::test_data(),
},
Value::String {
val: "0x2A".to_string(),
span: Span::test_data(),
},
], ],
span: Span::test_data(), span: Span::test_data(),
}), }),
@ -108,10 +84,15 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input { match input {
Value::Int { val, .. } => fmt_it(*val, span), Value::Int { val, .. } => fmt_it(*val, span),
Value::Filesize { val, .. } => fmt_it(*val, span), Value::Filesize { val, .. } => fmt_it(*val, span),
_ => Value::Error { // Propagate errors by explicitly matching them before the final case.
error: ShellError::UnsupportedInput( Value::Error { .. } => input.clone(),
format!("unsupported input type: {:?}", input.get_type()), other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"integer or filesize".into(),
other.get_type().to_string(),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }
@ -122,31 +103,31 @@ fn fmt_it(num: i64, span: Span) -> Value {
let mut vals = vec![]; let mut vals = vec![];
cols.push("binary".into()); cols.push("binary".into());
vals.push(Value::string(format!("{:#b}", num), span)); vals.push(Value::string(format!("{num:#b}"), span));
cols.push("debug".into()); cols.push("debug".into());
vals.push(Value::string(format!("{:#?}", num), span)); vals.push(Value::string(format!("{num:#?}"), span));
cols.push("display".into()); cols.push("display".into());
vals.push(Value::string(format!("{}", num), span)); vals.push(Value::string(format!("{num}"), span));
cols.push("lowerexp".into()); cols.push("lowerexp".into());
vals.push(Value::string(format!("{:#e}", num), span)); vals.push(Value::string(format!("{num:#e}"), span));
cols.push("lowerhex".into()); cols.push("lowerhex".into());
vals.push(Value::string(format!("{:#x}", num), span)); vals.push(Value::string(format!("{num:#x}"), span));
cols.push("octal".into()); cols.push("octal".into());
vals.push(Value::string(format!("{:#o}", num), span)); vals.push(Value::string(format!("{num:#o}"), span));
// cols.push("pointer".into()); // cols.push("pointer".into());
// vals.push(Value::string(format!("{:#p}", &num), span)); // vals.push(Value::string(format!("{:#p}", &num), span));
cols.push("upperexp".into()); cols.push("upperexp".into());
vals.push(Value::string(format!("{:#E}", num), span)); vals.push(Value::string(format!("{num:#E}"), span));
cols.push("upperhex".into()); cols.push("upperhex".into());
vals.push(Value::string(format!("{:#X}", num), span)); vals.push(Value::string(format!("{num:#X}"), span));
Value::Record { cols, vals, span } Value::Record { cols, vals, span }
} }

View File

@ -177,13 +177,24 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
val: int_to_endian(i64::from(*val)), val: int_to_endian(i64::from(*val)),
span, span,
}, },
Value::Duration { val, .. } => Value::Binary {
val: int_to_endian(*val),
span,
},
Value::Date { val, .. } => Value::Binary { Value::Date { val, .. } => Value::Binary {
val: val.format("%c").to_string().as_bytes().to_vec(), val: val.format("%c").to_string().as_bytes().to_vec(),
span, span,
}, },
// Propagate errors by explicitly matching them before the final case.
_ => Value::Error { Value::Error { .. } => input.clone(),
error: ShellError::UnsupportedInput("'into binary' for unsupported type".into(), span), other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"integer, float, filesize, string, date, duration, binary or bool".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
}, },
} }
} }

View File

@ -54,7 +54,7 @@ impl Command for SubCommand {
vec![ vec![
Example { Example {
description: "Convert value to boolean in table", description: "Convert value to boolean in table",
example: "echo [[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value", example: "[[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value",
result: Some(Value::List { result: Some(Value::List {
vals: vec![ vals: vec![
Value::Record { Value::Record {
@ -163,10 +163,15 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
Ok(val) => Value::Bool { val, span }, Ok(val) => Value::Bool { val, span },
Err(error) => Value::Error { error }, Err(error) => Value::Error { error },
}, },
_ => Value::Error { // Propagate errors by explicitly matching them before the final case.
error: ShellError::UnsupportedInput( Value::Error { .. } => input.clone(),
"'into bool' does not support this input".into(), other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"bool, integer, float or string".into(),
other.get_type().to_string(),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value, Category, IntoPipelineData, PipelineData, Signature, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,13 +14,19 @@ impl Command for Into {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into").category(Category::Conversions) Signature::build("into")
.category(Category::Conversions)
.input_output_types(vec![(Type::Nothing, Type::String)])
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Commands to convert data from one type to another." "Commands to convert data from one type to another."
} }
fn extra_usage(&self) -> &str {
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -41,15 +47,3 @@ impl Command for Into {
.into_pipeline_data()) .into_pipeline_data())
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Into {})
}
}

View File

@ -147,41 +147,19 @@ impl Command for SubCommand {
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
let example_result_1 = |secs: i64, nsecs: u32| { let example_result_1 = |secs: i64, nsecs: u32| match Utc.timestamp_opt(secs, nsecs) {
let dt = match Utc.timestamp_opt(secs, nsecs) { LocalResult::Single(dt) => Some(Value::Date {
LocalResult::Single(dt) => Some(dt), val: dt.into(),
_ => None, span: Span::test_data(),
}; }),
match dt { _ => panic!("datetime: help example is invalid"),
Some(dt) => Some(Value::Date {
val: dt.into(),
span: Span::test_data(),
}),
None => Some(Value::Error {
error: ShellError::UnsupportedInput(
"The given datetime representation is unsupported.".to_string(),
Span::test_data(),
),
}),
}
}; };
let example_result_2 = |millis: i64| { let example_result_2 = |millis: i64| match Utc.timestamp_millis_opt(millis) {
let dt = match Utc.timestamp_millis_opt(millis) { LocalResult::Single(dt) => Some(Value::Date {
LocalResult::Single(dt) => Some(dt), val: dt.into(),
_ => None, span: Span::test_data(),
}; }),
match dt { _ => panic!("datetime: help example is invalid"),
Some(dt) => Some(Value::Date {
val: dt.into(),
span: Span::test_data(),
}),
None => Some(Value::Error {
error: ShellError::UnsupportedInput(
"The given datetime representation is unsupported.".to_string(),
Span::test_data(),
),
}),
}
}; };
vec![ vec![
Example { Example {
@ -213,7 +191,7 @@ impl Command for SubCommand {
}, },
Example { Example {
description: description:
"Convert timestamps like the sqlite history t", "Convert a millisecond-precise timestamp",
example: "1656165681720 | into datetime", example: "1656165681720 | into datetime",
result: example_result_2(1656165681720) result: example_result_2(1656165681720)
}, },
@ -231,11 +209,16 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
let timestamp = match input { let timestamp = match input {
Value::Int { val, .. } => Ok(*val), Value::Int { val, .. } => Ok(*val),
Value::String { val, .. } => val.parse::<i64>(), Value::String { val, .. } => val.parse::<i64>(),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => return input.clone(),
other => { other => {
return Value::Error { return Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!("Expected string or int, got {} instead", other.get_type()), "string and integer".into(),
other.get_type().to_string(),
head, head,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}; };
} }
@ -248,113 +231,68 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
if ts.abs() > TIMESTAMP_BOUND { if ts.abs() > TIMESTAMP_BOUND {
return Value::Error { return Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::UnsupportedInput(
"Given timestamp is out of range, it should between -8e+12 and 8e+12" "timestamp is out of range; it should between -8e+12 and 8e+12".to_string(),
.to_string(), format!("timestamp is {ts:?}"),
head, head,
// Again, can safely unwrap this from here on
input.expect_span(),
), ),
}; };
} }
macro_rules! match_datetime {
($expr:expr) => {
match $expr {
LocalResult::Single(dt) => Value::Date {
val: dt.into(),
span: head,
},
_ => {
return Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".into(),
format!("timestamp is {:?}", ts),
head,
head,
),
};
}
}
};
}
return match timezone { return match timezone {
// default to UTC // default to UTC
None => { None => {
// be able to convert chrono::Utc::now() // be able to convert chrono::Utc::now()
let dt = match ts.to_string().len() { match ts.to_string().len() {
x if x > 13 => Utc.timestamp_nanos(ts).into(), x if x > 13 => Value::Date {
x if x > 10 => match Utc.timestamp_millis_opt(ts) { val: Utc.timestamp_nanos(ts).into(),
LocalResult::Single(dt) => dt.into(), span: head,
_ => {
return Value::Error {
// This error message is from chrono
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
head,
),
};
}
}, },
_ => match Utc.timestamp_opt(ts, 0) { x if x > 10 => match_datetime!(Utc.timestamp_millis_opt(ts)),
LocalResult::Single(dt) => dt.into(), _ => match_datetime!(Utc.timestamp_opt(ts, 0)),
_ => {
return Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
head,
),
}
}
},
};
Value::Date {
val: dt,
span: head,
} }
} }
Some(Spanned { item, span }) => match item { Some(Spanned { item, span }) => match item {
Zone::Utc => match Utc.timestamp_opt(ts, 0) { Zone::Utc => match_datetime!(Utc.timestamp_opt(ts, 0)),
LocalResult::Single(val) => Value::Date { Zone::Local => match_datetime!(Local.timestamp_opt(ts, 0)),
val: val.into(),
span: head,
},
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
Zone::Local => match Local.timestamp_opt(ts, 0) {
LocalResult::Single(val) => Value::Date {
val: val.into(),
span: head,
},
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
Zone::East(i) => match FixedOffset::east_opt((*i as i32) * HOUR) { Zone::East(i) => match FixedOffset::east_opt((*i as i32) * HOUR) {
Some(eastoffset) => match eastoffset.timestamp_opt(ts, 0) { Some(eastoffset) => match_datetime!(eastoffset.timestamp_opt(ts, 0)),
LocalResult::Single(val) => Value::Date { val, span: head },
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
None => Value::Error { None => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::DatetimeParseError(*span),
"The given local datetime representation is invalid.".to_string(),
*span,
),
}, },
}, },
Zone::West(i) => match FixedOffset::west_opt((*i as i32) * HOUR) { Zone::West(i) => match FixedOffset::west_opt((*i as i32) * HOUR) {
Some(westoffset) => match westoffset.timestamp_opt(ts, 0) { Some(westoffset) => match_datetime!(westoffset.timestamp_opt(ts, 0)),
LocalResult::Single(val) => Value::Date { val, span: head },
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
None => Value::Error { None => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::DatetimeParseError(*span),
"The given local datetime representation is invalid.".to_string(),
*span,
),
}, },
}, },
Zone::Error => Value::Error { Zone::Error => Value::Error {
error: ShellError::UnsupportedInput( // This is an argument error, not an input error
"Cannot convert given timezone or offset to timestamp".to_string(), error: ShellError::TypeMismatch(
"Invalid timezone or offset".to_string(),
*span, *span,
), ),
}, },
@ -391,10 +329,15 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
}, },
} }
} }
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
format!("Expected string, got {} instead", other.get_type()), "string".into(),
other.get_type().to_string(),
head, head,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }

View File

@ -86,7 +86,7 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
let other = s.trim(); let other = s.trim();
match other.parse::<f64>() { match other.parse::<f64>() {
Ok(x) => Value::Float { val: x, span: head }, Ok(x) => Value::float(x, head),
Err(reason) => Value::Error { Err(reason) => Value::Error {
error: ShellError::CantConvert( error: ShellError::CantConvert(
"float".to_string(), "float".to_string(),
@ -97,10 +97,7 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
}, },
} }
} }
Value::Int { val: v, span } => Value::Float { Value::Int { val: v, span } => Value::float(*v as f64, *span),
val: *v as f64,
span: *span,
},
Value::Bool { val: b, span } => Value::Float { Value::Bool { val: b, span } => Value::Float {
val: match b { val: match b {
true => 1.0, true => 1.0,
@ -108,18 +105,17 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
}, },
span: *span, span: *span,
}, },
other => { // Propagate errors by explicitly matching them before the final case.
let span = other.span(); Value::Error { .. } => input.clone(),
match span { other => Value::Error {
Ok(s) => { error: ShellError::OnlySupportsThisInputType(
let got = format!("Expected a string, got {} instead", other.get_type()); "string, integer or bool".into(),
Value::Error { other.get_type().to_string(),
error: ShellError::UnsupportedInput(got, s), head,
} // This line requires the Value::Error match above.
} other.expect_span(),
Err(e) => Value::Error { error: e }, ),
} },
}
} }
} }

View File

@ -65,7 +65,8 @@ impl Command for SubCommand {
vec![ vec![
Example { Example {
description: "Convert string to duration in table", description: "Convert string to duration in table",
example: "echo [[value]; ['1sec'] ['2min'] ['3hr'] ['4day'] ['5wk']] | into duration value", example:
"[[value]; ['1sec'] ['2min'] ['3hr'] ['4day'] ['5wk']] | into duration value",
result: Some(Value::List { result: Some(Value::List {
vals: vec![ vals: vec![
Value::Record { Value::Record {
@ -467,9 +468,11 @@ fn action(
} }
} else { } else {
Value::Error { Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::CantConvert(
"'into duration' does not support this string input".into(), "string".into(),
"duration".into(),
span, span,
None,
), ),
} }
} }
@ -480,10 +483,15 @@ fn action(
} }
} }
} }
_ => Value::Error { // Propagate errors by explicitly matching them before the final case.
error: ShellError::UnsupportedInput( Value::Error { .. } => input.clone(),
"'into duration' does not support this input".into(), other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"string or duration".into(),
other.get_type().to_string(),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }
@ -502,7 +510,7 @@ mod test {
#[test] #[test]
fn turns_ns_to_duration() { fn turns_ns_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("3ns"); let word = Value::test_string("3ns");
let expected = Value::Duration { val: 3, span }; let expected = Value::Duration { val: 3, span };
let convert_duration = None; let convert_duration = None;
@ -513,7 +521,7 @@ mod test {
#[test] #[test]
fn turns_us_to_duration() { fn turns_us_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("4us"); let word = Value::test_string("4us");
let expected = Value::Duration { let expected = Value::Duration {
val: 4 * 1000, val: 4 * 1000,
@ -527,7 +535,7 @@ mod test {
#[test] #[test]
fn turns_ms_to_duration() { fn turns_ms_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("5ms"); let word = Value::test_string("5ms");
let expected = Value::Duration { let expected = Value::Duration {
val: 5 * 1000 * 1000, val: 5 * 1000 * 1000,
@ -541,7 +549,7 @@ mod test {
#[test] #[test]
fn turns_sec_to_duration() { fn turns_sec_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("1sec"); let word = Value::test_string("1sec");
let expected = Value::Duration { let expected = Value::Duration {
val: 1000 * 1000 * 1000, val: 1000 * 1000 * 1000,
@ -555,7 +563,7 @@ mod test {
#[test] #[test]
fn turns_min_to_duration() { fn turns_min_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("7min"); let word = Value::test_string("7min");
let expected = Value::Duration { let expected = Value::Duration {
val: 7 * 60 * 1000 * 1000 * 1000, val: 7 * 60 * 1000 * 1000 * 1000,
@ -569,7 +577,7 @@ mod test {
#[test] #[test]
fn turns_hr_to_duration() { fn turns_hr_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("42hr"); let word = Value::test_string("42hr");
let expected = Value::Duration { let expected = Value::Duration {
val: 42 * 60 * 60 * 1000 * 1000 * 1000, val: 42 * 60 * 60 * 1000 * 1000 * 1000,
@ -583,7 +591,7 @@ mod test {
#[test] #[test]
fn turns_day_to_duration() { fn turns_day_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 5);
let word = Value::test_string("123day"); let word = Value::test_string("123day");
let expected = Value::Duration { let expected = Value::Duration {
val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000, val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000,
@ -597,7 +605,7 @@ mod test {
#[test] #[test]
fn turns_wk_to_duration() { fn turns_wk_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("3wk"); let word = Value::test_string("3wk");
let expected = Value::Duration { let expected = Value::Duration {
val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000, val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000,

View File

@ -116,20 +116,18 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
val: 0, val: 0,
span: value_span, span: value_span,
}, },
_ => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
"'into filesize' for unsupported type".into(), "string and integer".into(),
other.get_type().to_string(),
span,
value_span, value_span,
), ),
}, },
} }
} else { } else {
Value::Error { // Propagate existing errors
error: ShellError::UnsupportedInput( input.clone()
"'into filesize' for unsupported type".into(),
span,
),
}
} }
} }
fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> { fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {

View File

@ -70,7 +70,7 @@ impl Command for SubCommand {
let radix: u32 = match radix { let radix: u32 = match radix {
Some(Value::Int { val, span }) => { Some(Value::Int { val, span }) => {
if !(2..=36).contains(&val) { if !(2..=36).contains(&val) {
return Err(ShellError::UnsupportedInput( return Err(ShellError::TypeMismatch(
"Radix must lie in the range [2, 36]".to_string(), "Radix must lie in the range [2, 36]".to_string(),
span, span,
)); ));
@ -92,7 +92,7 @@ impl Command for SubCommand {
vec![ vec![
Example { Example {
description: "Convert string to integer in table", description: "Convert string to integer in table",
example: "echo [[num]; ['-5'] [4] [1.5]] | into int num", example: "[[num]; ['-5'] [4] [1.5]] | into int num",
result: None, result: None,
}, },
Example { Example {
@ -113,10 +113,7 @@ impl Command for SubCommand {
Example { Example {
description: "Convert file size to integer", description: "Convert file size to integer",
example: "4KB | into int", example: "4KB | into int",
result: Some(Value::Int { result: Some(Value::test_int(4000)),
val: 4000,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Convert bool to integer", description: "Convert bool to integer",
@ -190,9 +187,11 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Ok(v) => v, Ok(v) => v,
_ => { _ => {
return Value::Error { return Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::CantConvert(
"Could not convert float to integer".to_string(), "float".to_string(),
"integer".to_string(),
span, span,
None,
), ),
} }
} }
@ -222,6 +221,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
val: val.timestamp(), val: val.timestamp(),
span, span,
}, },
Value::Duration { val, .. } => Value::Int { val: *val, span },
Value::Binary { val, span } => { Value::Binary { val, span } => {
use byteorder::{BigEndian, ByteOrder, LittleEndian}; use byteorder::{BigEndian, ByteOrder, LittleEndian};
@ -233,26 +233,25 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
} }
val.resize(8, 0); val.resize(8, 0);
Value::Int { Value::int(LittleEndian::read_i64(&val), *span)
val: LittleEndian::read_i64(&val),
span: *span,
}
} else { } else {
while val.len() < 8 { while val.len() < 8 {
val.insert(0, 0); val.insert(0, 0);
} }
val.resize(8, 0); val.resize(8, 0);
Value::Int { Value::int(BigEndian::read_i64(&val), *span)
val: BigEndian::read_i64(&val),
span: *span,
}
} }
} }
_ => Value::Error { // Propagate errors by explicitly matching them before the final case.
error: ShellError::UnsupportedInput( Value::Error { .. } => input.clone(),
format!("'into int' for unsupported type '{}'", input.get_type()), other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"integer, float, filesize, date, string, binary, duration or bool".into(),
other.get_type().to_string(),
span, span,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
} }
@ -269,13 +268,13 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
// octal // octal
{ {
match int_from_string(val, head) { match int_from_string(val, head) {
Ok(x) => return Value::Int { val: x, span: head }, Ok(x) => return Value::int(x, head),
Err(e) => return Value::Error { error: e }, Err(e) => return Value::Error { error: e },
} }
} else if val.starts_with("00") { } else if val.starts_with("00") {
// It's a padded string // It's a padded string
match i64::from_str_radix(val, radix) { match i64::from_str_radix(val, radix) {
Ok(n) => return Value::Int { val: n, span: head }, Ok(n) => return Value::int(n, head),
Err(e) => { Err(e) => {
return Value::Error { return Value::Error {
error: ShellError::CantConvert( error: ShellError::CantConvert(
@ -290,17 +289,22 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
} }
val.to_string() val.to_string()
} }
_ => { // Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => return input.clone(),
other => {
return Value::Error { return Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
"only strings or integers are supported".to_string(), "string and integer".into(),
other.get_type().to_string(),
head, head,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
} };
} }
}; };
match i64::from_str_radix(i.trim(), radix) { match i64::from_str_radix(i.trim(), radix) {
Ok(n) => Value::Int { val: n, span: head }, Ok(n) => Value::int(n, head),
Err(_reason) => Value::Error { Err(_reason) => Value::Error {
error: ShellError::CantConvert("string".to_string(), "int".to_string(), head, None), error: ShellError::CantConvert("string".to_string(), "int".to_string(), head, None),
}, },
@ -363,8 +367,7 @@ fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {
"string".to_string(), "string".to_string(),
span, span,
Some(format!( Some(format!(
r#"string "{}" does not represent a valid integer"#, r#"string "{trimmed}" does not represent a valid integer"#
trimmed
)), )),
)), )),
}, },

View File

@ -49,7 +49,7 @@ impl Command for SubCommand {
vec![ vec![
Example { Example {
description: "Convert from one row table to record", description: "Convert from one row table to record",
example: "echo [[value]; [false]] | into record", example: "[[value]; [false]] | into record",
result: Some(Value::Record { result: Some(Value::Record {
cols: vec!["value".to_string()], cols: vec!["value".to_string()],
vals: vec![Value::boolean(false, span)], vals: vec![Value::boolean(false, span)],
@ -183,10 +183,14 @@ fn into_record(
Value::Record { cols, vals, span } Value::Record { cols, vals, span }
} }
Value::Record { cols, vals, span } => Value::Record { cols, vals, span }, Value::Record { cols, vals, span } => Value::Record { cols, vals, span },
Value::Error { .. } => input,
other => Value::Error { other => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::OnlySupportsThisInputType(
"'into record' does not support this input".into(), "string".into(),
other.span().unwrap_or(call.head), other.get_type().to_string(),
call.head,
// This line requires the Value::Error match above.
other.expect_span(),
), ),
}, },
}; };

View File

@ -79,34 +79,22 @@ impl Command for SubCommand {
Example { Example {
description: "convert integer to string and append three decimal places", description: "convert integer to string and append three decimal places",
example: "5 | into string -d 3", example: "5 | into string -d 3",
result: Some(Value::String { result: Some(Value::test_string("5.000")),
val: "5.000".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert decimal to string and round to nearest integer", description: "convert decimal to string and round to nearest integer",
example: "1.7 | into string -d 0", example: "1.7 | into string -d 0",
result: Some(Value::String { result: Some(Value::test_string("2")),
val: "2".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert decimal to string", description: "convert decimal to string",
example: "1.7 | into string -d 1", example: "1.7 | into string -d 1",
result: Some(Value::String { result: Some(Value::test_string("1.7")),
val: "1.7".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert decimal to string and limit to 2 decimals", description: "convert decimal to string and limit to 2 decimals",
example: "1.734 | into string -d 2", example: "1.734 | into string -d 2",
result: Some(Value::String { result: Some(Value::test_string("1.73")),
val: "1.73".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "try to convert decimal to string and provide negative decimal points", description: "try to convert decimal to string and provide negative decimal points",
@ -123,26 +111,17 @@ impl Command for SubCommand {
Example { Example {
description: "convert decimal to string", description: "convert decimal to string",
example: "4.3 | into string", example: "4.3 | into string",
result: Some(Value::String { result: Some(Value::test_string("4.3")),
val: "4.3".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert string to string", description: "convert string to string",
example: "'1234' | into string", example: "'1234' | into string",
result: Some(Value::String { result: Some(Value::test_string("1234")),
val: "1234".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert boolean to string", description: "convert boolean to string",
example: "true | into string", example: "true | into string",
result: Some(Value::String { result: Some(Value::test_string("true")),
val: "true".to_string(),
span: Span::test_data(),
}),
}, },
// TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032 // TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032
// Example { // Example {
@ -175,7 +154,7 @@ fn string_helper(
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?; let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
if let Some(decimal_val) = decimals_value { if let Some(decimal_val) = decimals_value {
if decimals && decimal_val.is_negative() { if decimals && decimal_val.is_negative() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::TypeMismatch(
"Cannot accept negative integers for decimals arguments".to_string(), "Cannot accept negative integers for decimals arguments".to_string(),
head, head,
)); ));
@ -227,7 +206,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
if decimals { if decimals {
let decimal_value = digits.unwrap_or(2) as usize; let decimal_value = digits.unwrap_or(2) as usize;
Value::String { Value::String {
val: format!("{:.*}", decimal_value, val), val: format!("{val:.decimal_value$}"),
span, span,
} }
} else { } else {
@ -255,12 +234,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
span, span,
}, },
Value::Error { error } => Value::String { Value::Error { error } => Value::String {
val: { val: into_code(error).unwrap_or_default(),
match into_code(error) {
Some(code) => code,
None => "".to_string(),
}
},
span, span,
}, },
Value::Nothing { .. } => Value::String { Value::Nothing { .. } => Value::String {
@ -272,9 +246,11 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
vals: _, vals: _,
span: _, span: _,
} => Value::Error { } => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::CantConvert(
"Cannot convert Record into string".to_string(), "record".into(),
"string".into(),
span, span,
Some("try using the `to nuon` command".into()),
), ),
}, },
Value::Binary { .. } => Value::Error { Value::Binary { .. } => Value::Error {

View File

@ -43,10 +43,10 @@ impl Command for Alias {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -37,14 +37,13 @@ impl Command for Ast {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head;
let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?; let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?;
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]); let (output, err) = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]);
eprintln!("output: {:#?}\nerror: {:#?}", output, err); eprintln!("output: {output:#?}\nerror: {err:#?}");
Ok(PipelineData::new(head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -0,0 +1,104 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Const;
impl Command for Const {
fn name(&self) -> &str {
"const"
}
fn usage(&self) -> &str {
"Create a parse-time constant."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("const")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("const_name", SyntaxShape::VarWithOptType, "constant name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by constant value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn search_terms(&self) -> Vec<&str> {
vec!["set", "let"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let var_id = call
.positional_nth(0)
.expect("checked through parser")
.as_var()
.expect("internal error: missing variable");
if let Some(constval) = engine_state.find_constant(var_id, &[]) {
// Instead of creating a second copy of the value in the stack, we could change
// stack.get_var() to check engine_state.find_constant().
stack.add_var(var_id, constval.clone());
Ok(PipelineData::empty())
} else {
Err(ShellError::NushellFailedSpanned(
"Missing Constant".to_string(),
"constant not added by the parser".to_string(),
call.head,
))
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create a new parse-time constant.",
example: "const x = 10",
result: None,
},
Example {
description: "Create a composite constant value",
example: "const x = { a: 10, b: 20 }",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use nu_protocol::engine::CommandType;
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Const {})
}
#[test]
fn test_command_type() {
assert!(matches!(Const.command_type(), CommandType::Keyword));
}
}

View File

@ -39,6 +39,8 @@ impl Command for Debug {
let config = engine_state.get_config().clone(); let config = engine_state.get_config().clone();
let raw = call.has_flag("raw"); let raw = call.has_flag("raw");
// Should PipelineData::Empty result in an error here?
input.map( input.map(
move |x| { move |x| {
if raw { if raw {
@ -74,7 +76,7 @@ impl Command for Debug {
}, },
Example { Example {
description: "Debug print a table", description: "Debug print a table",
example: "echo [[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug", example: "[[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug",
result: Some(Value::List { result: Some(Value::List {
vals: vec![ vals: vec![
Value::test_string("{version: 0.1.0, patch: false}"), Value::test_string("{version: 0.1.0, patch: false}"),

View File

@ -36,10 +36,10 @@ impl Command for Def {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct DefEnv; pub struct DefEnv;
@ -62,20 +62,17 @@ def-env cd_with_fallback [arg = ""] {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "Set environment variable by call a custom command", description: "Set environment variable by call a custom command",
example: r#"def-env foo [] { let-env BAR = "BAZ" }; foo; $env.BAR"#, example: r#"def-env foo [] { let-env BAR = "BAZ" }; foo; $env.BAR"#,
result: Some(Value::String { result: Some(Value::test_string("BAZ")),
val: "BAZ".to_string(),
span: Span::test_data(),
}),
}] }]
} }
} }

View File

@ -19,6 +19,11 @@ impl Command for Describe {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("describe") Signature::build("describe")
.input_output_types(vec![(Type::Any, Type::String)]) .input_output_types(vec![(Type::Any, Type::String)])
.switch(
"no-collect",
"do not collect streams of structured data",
Some('n'),
)
.category(Category::Core) .category(Category::Core)
} }
@ -30,32 +35,58 @@ impl Command for Describe {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
if matches!(input, PipelineData::ExternalStream { .. }) {
Ok(PipelineData::Value(
Value::string("raw input", call.head),
None,
))
} else {
let value = input.into_value(call.head);
let description = match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
};
Ok(Value::String { let no_collect: bool = call.has_flag("no-collect");
val: description,
span: head, let description = match input {
PipelineData::ExternalStream { .. } => "raw input".into(),
PipelineData::ListStream(_, _) => {
if no_collect {
"stream".into()
} else {
let value = input.into_value(head);
let base_description = match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
};
format!("{base_description} (stream)")
}
} }
.into_pipeline_data()) _ => {
let value = input.into_value(head);
match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
}
}
};
Ok(Value::String {
val: description,
span: head,
} }
.into_pipeline_data())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![
description: "Describe the type of a string", Example {
example: "'hello' | describe", description: "Describe the type of a string",
result: Some(Value::test_string("string")), example: "'hello' | describe",
}] result: Some(Value::test_string("string")),
},
Example {
description: "Describe a stream of data, collecting it first",
example: "[1 2 3] | each {|i| $i} | describe",
result: Some(Value::test_string("list<int> (stream)")),
},
Example {
description: "Describe the input but do not collect streams",
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
result: Some(Value::test_string("stream")),
},
]
} }
fn search_terms(&self) -> Vec<&str> { fn search_terms(&self) -> Vec<&str> {

View File

@ -1,8 +1,11 @@
use std::thread;
use nu_engine::{eval_block_with_early_return, CallExt}; use nu_engine::{eval_block_with_early_return, CallExt};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::engine::{Closure, Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape,
Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,7 +17,7 @@ impl Command for Do {
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Run a block" "Run a closure, providing it with the pipeline input"
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
@ -23,25 +26,25 @@ impl Command for Do {
.input_output_types(vec![(Type::Any, Type::Any)]) .input_output_types(vec![(Type::Any, Type::Any)])
.switch( .switch(
"ignore-errors", "ignore-errors",
"ignore errors as the block runs", "ignore errors as the closure runs",
Some('i'), Some('i'),
) )
.switch( .switch(
"ignore-shell-errors", "ignore-shell-errors",
"ignore shell errors as the block runs", "ignore shell errors as the closure runs",
Some('s'), Some('s'),
) )
.switch( .switch(
"ignore-program-errors", "ignore-program-errors",
"ignore program errors as the block runs", "ignore external program errors as the closure runs",
Some('p'), Some('p'),
) )
.switch( .switch(
"capture-errors", "capture-errors",
"capture errors as the block runs and return it", "catch errors as the closure runs, and return them",
Some('c'), Some('c'),
) )
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block") .rest("rest", SyntaxShape::Any, "the parameter(s) for the closure")
.category(Category::Core) .category(Category::Core)
} }
@ -106,7 +109,7 @@ impl Command for Do {
block, block,
input, input,
call.redirect_stdout, call.redirect_stdout,
capture_errors || ignore_shell_errors || ignore_program_errors, call.redirect_stdout,
); );
match result { match result {
@ -118,6 +121,58 @@ impl Command for Do {
metadata, metadata,
trim_end_newline, trim_end_newline,
}) if capture_errors => { }) if capture_errors => {
// Use a thread to receive stdout message.
// Or we may get a deadlock if child process sends out too much bytes to stderr.
//
// For example: in normal linux system, stderr pipe's limit is 65535 bytes.
// if child process sends out 65536 bytes, the process will be hanged because no consumer
// consumes the first 65535 bytes
// So we need a thread to receive stdout message, then the current thread can continue to consume
// stderr messages.
let stdout_handler = stdout.map(|stdout_stream| {
thread::Builder::new()
.name("stderr redirector".to_string())
.spawn(move || {
let ctrlc = stdout_stream.ctrlc.clone();
let span = stdout_stream.span;
RawStream::new(
Box::new(
vec![stdout_stream.into_bytes().map(|s| s.item)].into_iter(),
),
ctrlc,
span,
None,
)
})
.expect("Failed to create thread")
});
// Intercept stderr so we can return it in the error if the exit code is non-zero.
// The threading issues mentioned above dictate why we also need to intercept stdout.
let mut stderr_ctrlc = None;
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => {
stderr_ctrlc = stderr_stream.ctrlc.clone();
stderr_stream.into_string().map(|s| s.item)?
}
};
let stdout = if let Some(handle) = stdout_handler {
match handle.join() {
Err(err) => {
return Err(ShellError::ExternalCommand(
"Fail to receive external commands stdout message".to_string(),
format!("{err:?}"),
span,
));
}
Ok(res) => Some(res),
}
} else {
None
};
let mut exit_code_ctrlc = None; let mut exit_code_ctrlc = None;
let exit_code: Vec<Value> = match exit_code { let exit_code: Vec<Value> = match exit_code {
None => vec![], None => vec![],
@ -128,11 +183,6 @@ impl Command for Do {
}; };
if let Some(Value::Int { val: code, .. }) = exit_code.last() { if let Some(Value::Int { val: code, .. }) = exit_code.last() {
if *code != 0 { if *code != 0 {
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => stderr_stream.into_string().map(|s| s.item)?,
};
return Err(ShellError::ExternalCommand( return Err(ShellError::ExternalCommand(
"External command failed".to_string(), "External command failed".to_string(),
stderr_msg, stderr_msg,
@ -143,7 +193,12 @@ impl Command for Do {
Ok(PipelineData::ExternalStream { Ok(PipelineData::ExternalStream {
stdout, stdout,
stderr, stderr: Some(RawStream::new(
Box::new(vec![Ok(stderr_msg.into_bytes())].into_iter()),
stderr_ctrlc,
span,
None,
)),
exit_code: Some(ListStream::from_stream( exit_code: Some(ListStream::from_stream(
exit_code.into_iter(), exit_code.into_iter(),
exit_code_ctrlc, exit_code_ctrlc,
@ -168,10 +223,9 @@ impl Command for Do {
metadata, metadata,
trim_end_newline, trim_end_newline,
}), }),
Ok(PipelineData::Value(Value::Error { .. }, ..)) if ignore_shell_errors => { Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_shell_errors => {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
Err(_) if ignore_shell_errors => Ok(PipelineData::new(call.head)),
r => r, r => r,
} }
} }
@ -179,22 +233,27 @@ impl Command for Do {
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "Run the block", description: "Run the closure",
example: r#"do { echo hello }"#, example: r#"do { echo hello }"#,
result: Some(Value::test_string("hello")), result: Some(Value::test_string("hello")),
}, },
Example { Example {
description: "Run the block and ignore both shell and program errors", description: "Run a stored first-class closure",
example: r#"let text = "I am enclosed"; let hello = {|| echo $text}; do $hello"#,
result: Some(Value::test_string("I am enclosed")),
},
Example {
description: "Run the closure and ignore both shell and external program errors",
example: r#"do -i { thisisnotarealcommand }"#, example: r#"do -i { thisisnotarealcommand }"#,
result: None, result: None,
}, },
Example { Example {
description: "Run the block and ignore shell errors", description: "Run the closure and ignore shell errors",
example: r#"do -s { thisisnotarealcommand }"#, example: r#"do -s { thisisnotarealcommand }"#,
result: None, result: None,
}, },
Example { Example {
description: "Run the block and ignore program errors", description: "Run the closure and ignore external program errors",
example: r#"do -p { nu -c 'exit 1' }; echo "I'll still run""#, example: r#"do -p { nu -c 'exit 1' }; echo "I'll still run""#,
result: None, result: None,
}, },
@ -204,12 +263,12 @@ impl Command for Do {
result: None, result: None,
}, },
Example { Example {
description: "Run the block, with a positional parameter", description: "Run the closure, with a positional parameter",
example: r#"do {|x| 100 + $x } 77"#, example: r#"do {|x| 100 + $x } 77"#,
result: Some(Value::test_int(177)), result: Some(Value::test_int(177)),
}, },
Example { Example {
description: "Run the block, with input", description: "Run the closure, with input",
example: r#"77 | do {|x| 100 + $in }"#, example: r#"77 | do {|x| 100 + $in }"#,
result: None, // TODO: returns 177 result: None, // TODO: returns 177
}, },

View File

@ -51,13 +51,7 @@ little reason to use this over just writing the values as-is."#
std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None), std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None),
// When there are no elements, we echo the empty string // When there are no elements, we echo the empty string
std::cmp::Ordering::Less => PipelineData::Value( std::cmp::Ordering::Less => PipelineData::Value(Value::string("", call.head), None),
Value::String {
val: "".to_string(),
span: call.head,
},
None,
),
} }
}) })
} }

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -15,6 +15,7 @@ impl Command for ErrorMake {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("error make") 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, "the error to create")
.switch( .switch(
"unspanned", "unspanned",
@ -108,10 +109,7 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
) => Some(ShellError::GenericError( ) => Some(ShellError::GenericError(
message, message,
label_text, label_text,
Some(Span { Some(Span::new(start as usize, end as usize)),
start: start as usize,
end: end as usize,
}),
None, None,
Vec::new(), Vec::new(),
)), )),

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, Signature, Span, Type, Value, Category, Example, IntoPipelineData, PipelineData, Signature, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -56,10 +56,7 @@ impl Command for ExportCommand {
vec![Example { vec![Example {
description: "Export a definition from a module", description: "Export a definition from a module",
example: r#"module utils { export def my-command [] { "hello" } }; use utils my-command; my-command"#, example: r#"module utils { export def my-command [] { "hello" } }; use utils my-command; my-command"#,
result: Some(Value::String { result: Some(Value::test_string("hello")),
val: "hello".to_string(),
span: Span::test_data(),
}),
}] }]
} }

View File

@ -39,10 +39,10 @@ impl Command for ExportAlias {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportDef; pub struct ExportDef;
@ -36,20 +36,17 @@ impl Command for ExportDef {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "Define a custom command in a module and call it", description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#, example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::String { result: Some(Value::test_string("foo")),
val: "foo".to_string(),
span: Span::test_data(),
}),
}] }]
} }

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportDefEnv; pub struct ExportDefEnv;
@ -62,20 +62,17 @@ export def-env cd_with_fallback [arg = ""] {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "Define a custom command that participates in the environment in a module and call it", description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#, example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::String { result: Some(Value::test_string("BAZ")),
val: "BAZ".to_string(),
span: Span::test_data(),
}),
}] }]
} }

View File

@ -35,10 +35,10 @@ impl Command for ExportExtern {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportUse; pub struct ExportUse;
@ -17,7 +17,12 @@ impl Command for ExportUse {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("export use") Signature::build("export use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)]) .input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("pattern", SyntaxShape::ImportPattern, "import pattern") .required("module", SyntaxShape::String, "Module or module file")
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
)
.category(Category::Core) .category(Category::Core)
} }
@ -34,10 +39,10 @@ impl Command for ExportUse {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -48,10 +53,7 @@ impl Command for ExportUse {
use eggs foo use eggs foo
foo foo
"#, "#,
result: Some(Value::String { result: Some(Value::test_string("foo")),
val: "foo".to_string(),
span: Span::test_data(),
}),
}] }]
} }

View File

@ -35,10 +35,10 @@ impl Command for Extern {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -2,7 +2,7 @@ use nu_engine::{eval_block, eval_expression, CallExt};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack}; use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value, Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -19,6 +19,8 @@ impl Command for For {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("for") Signature::build("for")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required( .required(
"var_name", "var_name",
SyntaxShape::VarWithOptType, SyntaxShape::VarWithOptType,
@ -91,13 +93,7 @@ impl Command for For {
if numbered { if numbered {
Value::Record { Value::Record {
cols: vec!["index".into(), "item".into()], cols: vec!["index".into(), "item".into()],
vals: vec![ vals: vec![Value::int(idx as i64, head), x],
Value::Int {
val: idx as i64,
span: head,
},
x,
],
span: head, span: head,
} }
} else { } else {
@ -110,7 +106,7 @@ impl Command for For {
&engine_state, &engine_state,
stack, stack,
&block, &block,
PipelineData::new(head), PipelineData::empty(),
redirect_stdout, redirect_stdout,
redirect_stderr, redirect_stderr,
) { ) {
@ -124,7 +120,10 @@ impl Command for For {
return Err(err); return Err(err);
} }
Ok(pipeline) => { Ok(pipeline) => {
pipeline.into_value(head); let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
} }
} }
} }
@ -136,13 +135,7 @@ impl Command for For {
if numbered { if numbered {
Value::Record { Value::Record {
cols: vec!["index".into(), "item".into()], cols: vec!["index".into(), "item".into()],
vals: vec![ vals: vec![Value::int(idx as i64, head), x],
Value::Int {
val: idx as i64,
span: head,
},
x,
],
span: head, span: head,
} }
} else { } else {
@ -155,7 +148,7 @@ impl Command for For {
&engine_state, &engine_state,
stack, stack,
&block, &block,
PipelineData::new(head), PipelineData::empty(),
redirect_stdout, redirect_stdout,
redirect_stderr, redirect_stderr,
) { ) {
@ -169,7 +162,10 @@ impl Command for For {
return Err(err); return Err(err);
} }
Ok(pipeline) => { Ok(pipeline) => {
pipeline.into_value(head); let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
} }
} }
} }
@ -181,14 +177,14 @@ impl Command for For {
&engine_state, &engine_state,
stack, stack,
&block, &block,
PipelineData::new(head), PipelineData::empty(),
redirect_stdout, redirect_stdout,
redirect_stderr, redirect_stderr,
)? )?
.into_value(head); .into_value(head);
} }
} }
Ok(PipelineData::new(head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -1,17 +1,18 @@
use crate::help_aliases::help_aliases;
use crate::help_commands::help_commands;
use crate::help_modules::help_modules;
use fancy_regex::Regex; use fancy_regex::Regex;
use nu_ansi_term::{ use nu_ansi_term::{
Color::{Default, Red, White}, Color::{Red, White},
Style, Style,
}; };
use nu_color_config::get_color_config; use nu_engine::CallExt;
use nu_engine::{get_full_help, CallExt};
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, span, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, SyntaxShape, Type, Value,
}; };
use std::borrow::Borrow;
#[derive(Clone)] #[derive(Clone)]
pub struct Help; pub struct Help;
@ -26,7 +27,7 @@ impl Command for Help {
.rest( .rest(
"rest", "rest",
SyntaxShape::String, SyntaxShape::String,
"the name of command to get help on", "the name of command, alias or module to get help on",
) )
.named( .named(
"find", "find",
@ -38,7 +39,11 @@ impl Command for Help {
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Display help information about commands." "Display help information about different parts of Nushell."
}
fn extra_usage(&self) -> &str {
r#"`help word` searches for "word" in commands, aliases and modules, in that order."#
} }
fn run( fn run(
@ -48,275 +53,18 @@ impl Command for Help {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
help(engine_state, stack, call) let head = call.head;
} let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
fn examples(&self) -> Vec<Example> { if rest.is_empty() && find.is_none() {
vec![ let msg = r#"Welcome to Nushell.
Example {
description: "show all commands and sub-commands",
example: "help commands",
result: None,
},
Example {
description: "show help for single command",
example: "help match",
result: None,
},
Example {
description: "show help for single sub-command",
example: "help str lpad",
result: None,
},
Example {
description: "search for string in command names, usage and search terms",
example: "help --find char",
result: None,
},
]
}
}
fn help(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
let commands = engine_state.get_decl_ids_sorted(false);
let config = engine_state.get_config();
let color_hm = get_color_config(config);
let default_style = Style::new().fg(Default).on(Default);
let string_style = match color_hm.get("string") {
Some(style) => style,
None => &default_style,
};
if let Some(f) = find {
let org_search_string = f.item.clone();
let search_string = f.item.to_lowercase();
let mut found_cmds_vec = Vec::new();
for decl_id in commands {
let mut cols = vec![];
let mut vals = vec![];
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(decl.borrow());
let signatures = sig.to_string();
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
let matches_term = if !search_terms.is_empty() {
search_terms
.iter()
.any(|term| term.to_lowercase().contains(&search_string))
} else {
false
};
let key_match = key.to_lowercase().contains(&search_string);
let usage_match = usage.to_lowercase().contains(&search_string);
if key_match || usage_match || matches_term {
cols.push("name".into());
vals.push(Value::String {
val: if key_match {
highlight_search_string(&key, &org_search_string, string_style)?
} else {
key
},
span: head,
});
cols.push("category".into());
vals.push(Value::String {
val: sig.category.to_string(),
span: head,
});
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span: head,
});
cols.push("usage".into());
vals.push(Value::String {
val: if usage_match {
highlight_search_string(&usage, &org_search_string, string_style)?
} else {
usage
},
span: head,
});
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span: head,
});
cols.push("search_terms".into());
vals.push(if search_terms.is_empty() {
Value::nothing(head)
} else {
Value::String {
val: if matches_term {
search_terms
.iter()
.map(|term| {
if term.to_lowercase().contains(&search_string) {
match highlight_search_string(
term,
&org_search_string,
string_style,
) {
Ok(s) => s,
Err(_) => {
string_style.paint(term.to_string()).to_string()
}
}
} else {
string_style.paint(term.to_string()).to_string()
}
})
.collect::<Vec<_>>()
.join(", ")
} else {
search_terms.join(", ")
},
span: head,
}
});
found_cmds_vec.push(Value::Record {
cols,
vals,
span: head,
});
}
}
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if !rest.is_empty() {
let mut found_cmds_vec = Vec::new();
if rest[0].item == "commands" {
for decl_id in commands {
let mut cols = vec![];
let mut vals = vec![];
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(decl.borrow());
let signatures = sig.to_string();
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
cols.push("name".into());
vals.push(Value::String {
val: key,
span: head,
});
cols.push("category".into());
vals.push(Value::String {
val: sig.category.to_string(),
span: head,
});
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span: head,
});
cols.push("usage".into());
vals.push(Value::String {
val: usage,
span: head,
});
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span: head,
});
cols.push("search_terms".into());
vals.push(if search_terms.is_empty() {
Value::nothing(head)
} else {
Value::String {
val: search_terms.join(", "),
span: head,
}
});
found_cmds_vec.push(Value::Record {
cols,
vals,
span: head,
});
}
Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut name = String::new();
for r in &rest {
if !name.is_empty() {
name.push(' ');
}
name.push_str(&r.item);
}
let output = engine_state
.get_signatures_with_examples(false)
.iter()
.filter(|(signature, _, _, _, _)| signature.name == name)
.map(|(signature, examples, _, _, is_parser_keyword)| {
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
})
.collect::<Vec<String>>();
if !output.is_empty() {
Ok(Value::String {
val: output.join("======================\n\n"),
span: call.head,
}
.into_pipeline_data())
} else {
Err(ShellError::CommandNotFound(span(&[
rest[0].span,
rest[rest.len() - 1].span,
])))
}
}
} else {
let msg = r#"Welcome to Nushell.
Here are some tips to help you get started. Here are some tips to help you get started.
* help -h or help help - show available `help` subcommands and examples
* help commands - list all available commands * help commands - list all available commands
* help <command name> - display help about a particular command * help <name> - display help about a particular command, alias, or module
* help --find <text to search> - search through all of help * help --find <text to search> - search through all help commands table
Nushell works on the idea of a "pipeline". Pipelines are commands connected with the '|' character. Nushell works on the idea of a "pipeline". Pipelines are commands connected with the '|' character.
Each stage in the pipeline works together to load, parse, and display information to you. Each stage in the pipeline works together to load, parse, and display information to you.
@ -334,12 +82,111 @@ Get the processes on your system actively using CPU:
You can also learn more at https://www.nushell.sh/book/"#; You can also learn more at https://www.nushell.sh/book/"#;
Ok(Value::String { Ok(Value::string(msg, head).into_pipeline_data())
val: msg.into(), } else if find.is_some() {
span: head, help_commands(engine_state, stack, call)
} else {
let result = help_commands(engine_state, stack, call);
let result = if let Err(ShellError::CommandNotFound(_)) = result {
help_aliases(engine_state, stack, call)
} else {
result
};
let result = if let Err(ShellError::AliasNotFound(_)) = result {
help_modules(engine_state, stack, call)
} else {
result
};
if let Err(ShellError::ModuleNotFoundAtRuntime(_, _)) = result {
let rest_spans: Vec<Span> = rest.iter().map(|arg| arg.span).collect();
Err(ShellError::NotFound(span(&rest_spans)))
} else {
result
}
} }
.into_pipeline_data())
} }
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show help for single command, alias, or module",
example: "help match",
result: None,
},
Example {
description: "show help for single sub-command, alias, or module",
example: "help str lpad",
result: None,
},
Example {
description: "search for string in command names, usage and search terms",
example: "help --find char",
result: None,
},
]
}
}
pub fn highlight_search_in_table(
table: Vec<Value>, // list of records
search_string: &str,
searched_cols: &[&str],
string_style: &Style,
) -> Result<Vec<Value>, ShellError> {
let orig_search_string = search_string;
let search_string = search_string.to_lowercase();
let mut matches = vec![];
for record in table {
let (cols, mut vals, record_span) = if let Value::Record { cols, vals, span } = record {
(cols, vals, span)
} else {
return Err(ShellError::NushellFailedSpanned(
"Expected record".to_string(),
format!("got {}", record.get_type()),
record.span()?,
));
};
let has_match = cols.iter().zip(vals.iter_mut()).fold(
Ok(false),
|acc: Result<bool, ShellError>, (col, val)| {
if searched_cols.contains(&col.as_str()) {
if let Value::String { val: s, span } = val {
if s.to_lowercase().contains(&search_string) {
*val = Value::String {
val: highlight_search_string(s, orig_search_string, string_style)?,
span: *span,
};
Ok(true)
} else {
// column does not contain the searched string
acc
}
} else {
// ignore non-string values
acc
}
} else {
// don't search this column
acc
}
},
)?;
if has_match {
matches.push(Value::Record {
cols,
vals,
span: record_span,
});
}
}
Ok(matches)
} }
// Highlight the search string using ANSI escape sequences and regular expressions. // Highlight the search string using ANSI escape sequences and regular expressions.
@ -348,7 +195,7 @@ pub fn highlight_search_string(
needle: &str, needle: &str,
string_style: &Style, string_style: &Style,
) -> Result<String, ShellError> { ) -> Result<String, ShellError> {
let regex_string = format!("(?i){}", needle); let regex_string = format!("(?i){needle}");
let regex = match Regex::new(&regex_string) { let regex = match Regex::new(&regex_string) {
Ok(regex) => regex, Ok(regex) => regex,
Err(err) => { Err(err) => {

View File

@ -0,0 +1,181 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{scope::ScopeData, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use std::borrow::Cow;
#[derive(Clone)]
pub struct HelpAliases;
impl Command for HelpAliases {
fn name(&self) -> &str {
"help aliases"
}
fn usage(&self) -> &str {
"Show help on nushell aliases."
}
fn signature(&self) -> Signature {
Signature::build("help aliases")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of alias to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in alias names and usage",
Some('f'),
)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show all aliases",
example: "help aliases",
result: None,
},
Example {
description: "show help for single alias",
example: "help aliases my-alias",
result: None,
},
Example {
description: "search for string in alias names and usages",
example: "help aliases --find my-alias",
result: None,
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_aliases(engine_state, stack, call)
}
}
pub fn help_aliases(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
// 🚩The following two-lines are copied from filters/find.rs:
let style_computer = StyleComputer::from_config(engine_state, stack);
// Currently, search results all use the same style.
// Also note that this sample string is passed into user-written code (the closure that may or may not be
// defined for "string").
let string_style = style_computer.compute("string", &Value::string("search result", head));
if let Some(f) = find {
let all_cmds_vec = build_help_aliases(engine_state, stack, head);
let found_cmds_vec =
highlight_search_in_table(all_cmds_vec, &f.item, &["name", "usage"], &string_style)?;
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if rest.is_empty() {
let found_cmds_vec = build_help_aliases(engine_state, stack, head);
Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut name = String::new();
for r in &rest {
if !name.is_empty() {
name.push(' ');
}
name.push_str(&r.item);
}
let alias_id = if let Some(id) = engine_state.find_alias(name.as_bytes(), &[]) {
id
} else {
return Err(ShellError::AliasNotFound(span(
&rest.iter().map(|r| r.span).collect::<Vec<Span>>(),
)));
};
let alias_expansion = engine_state
.get_alias(alias_id)
.iter()
.map(|span| String::from_utf8_lossy(engine_state.get_span_contents(span)))
.collect::<Vec<Cow<str>>>()
.join(" ");
let alias_usage = engine_state.build_alias_usage(alias_id);
// TODO: merge this into documentation.rs at some point
const G: &str = "\x1b[32m"; // green
const C: &str = "\x1b[36m"; // cyan
const RESET: &str = "\x1b[0m"; // reset
let mut long_desc = String::new();
if let Some((usage, extra_usage)) = alias_usage {
long_desc.push_str(&usage);
long_desc.push_str("\n\n");
if !extra_usage.is_empty() {
long_desc.push_str(&extra_usage);
long_desc.push_str("\n\n");
}
}
long_desc.push_str(&format!("{G}Alias{RESET}: {C}{name}{RESET}"));
long_desc.push_str("\n\n");
long_desc.push_str(&format!("{G}Expansion{RESET}:\n {alias_expansion}"));
let config = engine_state.get_config();
if !config.use_ansi_coloring {
long_desc = nu_utils::strip_ansi_string_likely(long_desc);
}
Ok(Value::String {
val: long_desc,
span: call.head,
}
.into_pipeline_data())
}
}
fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
let mut scope_data = ScopeData::new(engine_state, stack);
scope_data.populate_aliases();
scope_data.collect_aliases(span)
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpAliases;
use crate::test_examples;
test_examples(HelpAliases {})
}
}

Some files were not shown because too many files have changed in this diff Show More