Compare commits

..

156 Commits

Author SHA1 Message Date
c986426478 Bump version for 0.103.0 release (#15340) 2025-03-18 20:12:52 -04:00
09674a0026 Feature-gate job unfreeze behind "os" (#15339)
# Description

The `job unfreeze` command relies on the `os` feature of the
`nu-protocol` crate, which means that `nu-command` doesn't compile with
`--no-default-features`. This PR gates `job unfreeze` behind
`nu-command`'s `os` feature to avoid this.

No user-facing changes, no tests needed.
2025-03-18 19:02:04 -04:00
9cca4ec18b Pin reedline to 0.39.0 for release (#15338) 2025-03-18 18:32:01 +01:00
90c86e6cbf build(deps): bump zip from 2.2.1 to 2.4.1 (#15335)
Bumps [zip](https://github.com/zip-rs/zip2) from 2.2.1 to 2.4.1.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2025-03-18 03:44:50 +00:00
4cb195a998 Disallow DTD by default in from xml (#15325)
# Description


Follow-up to #15272, changing default to disallow DTD as discussed.
Especially applicable for the `http get` case.

# User-Facing Changes

Changes behavior introduced in #15272, so release notes need to be
updated to reflect this
2025-03-17 14:16:17 +01:00
f7f09292d6 Add category to pwd and banner commands (#15330)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

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

Add category to `pwd` and `banner` commands, fix broken of command docs
updating here:
https://github.com/nushell/nushell.github.io/actions/runs/13884819349/job/38848104064#step:5:18
The error was caused by these two commands have no category
2025-03-17 20:22:01 +08:00
2c35e07c2d fix(lsp): ansi strip on hover text (#15331)
Fixes messed ansi escapes in hover text (manpage):

<img width="392" alt="image"
src="https://github.com/user-attachments/assets/37c16520-d499-4079-93d9-0eccd1cfa8de"
/>

# Description

That bug is introduced in #15115.

Also refactored the hover related code to a separate file, just like
other features.

# User-Facing Changes

# Tests + Formatting

# After Submitting
2025-03-17 06:54:48 -05:00
c949d2e893 into string should not modify strings (#15320)
# Description

`into string` should not modify input strings (even with the
`--group-digits` flag). It's a conversion command, not a formatting
command.

# User-Facing Changes

- For strings, the same behavior from 0.102.0 is preserved.
- Errors are no longer turned into strings, but rather they are returned
as is.

# After Submitting

Create a `format int` and/or `format float` command and so that the
`--group-digits` flag can be transferred to one of those commands.
2025-03-16 20:11:05 +00:00
83de8560ee Unify closure serializing logic for to nuon, to msgpack, and to json (#15285)
# Description
Before this PR, `to msgpack`/`to msgpackz` and `to json` serialize
closures as `nil`/`null` respectively, when the `--serialize` option
isn't passed. This PR makes it an error to serialize closures to msgpack
or JSON without the `--serialize` flag, which is the behavior of `to
nuon`.

This PR also adds the `--serialize` flag to `to msgpack`.

This PR also changes `to nuon` and `to json` to return an error if they
cannot find the block contents of a closure, rather than serializing an
empty string or an error string, respectively. This behavior is
replicated for `to msgpack`.

It also changes `to nuon`'s error message for serializing closures
without `--serialize` to be the same as the new errors for `to json` and
`to msgpack`.

# User-Facing Changes

* Add `--serialize` flag to `to msgpack`, similar to the `--serialize`
flag for `to nuon` and `to json`.
* Serializing closures to JSON or msgpack without `--serialize`

Partially fixes #11738
2025-03-16 20:15:02 +01:00
00e5e6d719 Update toolkit.nu add nu_plugin_polars plugin for build and install (#15324)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

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

Update `toolkit.nu` add `nu_plugin_polars` plugin for build and install

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

`toolkit install --all` and `toolkit build --all` will have
`nu_plugin_polars` included
2025-03-16 13:44:41 -05:00
1dd861b10f Close find handle in ls windows unsafe code (#15314)
While inspecting the Windows specific code of `ls` for #15311 I stumbled
upon an unrelated issue in the alternate metadata gathering on Windows
(added by #5703).

The handle created by performing `FindFirstFileW` was never closed,
leading to a potential leak. Fixed by running `FindClose` as soon as the
operation succeeds.

https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilew#remarks
2025-03-16 16:33:36 +01:00
42aa2ff5ba remove mimalloc allocator (#15317)
# Description

This PR removes the mimalloc allocator due to run-away memory leaks
recently found.

closes #15311

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-03-15 09:32:55 -05:00
74f62305b2 fix(completion): more quoting for file_completion/directory_completion (#15299)
# Description

Found inconsistent behaviors of `directory_completion` and
`file_completion`, https://github.com/nushell/nushell/issues/13951

https://github.com/nushell/reedline/pull/886

Also there're failing cases with such file names/dir names `foo(`,
`foo{`, `foo[`.
I think it doesn't harm to be more conservative at adding quotes, even
if it might be unnecessary for paired names like `foo{}`.

# User-Facing Changes

# Tests + Formatting

Adjusted

# After Submitting
2025-03-15 15:17:59 +01:00
8f634f4140 refactor: rename subcommand structs (#15309)
Came from [this
discussion](https://discord.com/channels/601130461678272522/1348791953784836147/1349699872059691038)
on discord with @fdncred

# Description
Small refactoring where I rename commands from "SubCommand" to its
proper name. Motivations: better clarity (although subjective), better
searchable, consistency.

The only commands I didn't touch were "split list" and "ansi gradient"
because of name clashes.

# User-Facing Changes
None

# Tests + Formatting
cargo fmt and clippy OK

# After Submitting
nothing required
2025-03-14 02:00:35 +01:00
33001d1992 build(deps): bump titlecase from 3.3.0 to 3.4.0 (#15295) 2025-03-13 19:41:30 +00:00
f4b7333dc8 build(deps): bump scraper from 0.22.0 to 0.23.1 (#15294) 2025-03-13 19:40:56 +00:00
3dde851381 Bump reedline for recent completion fix (#15310)
Pulls in nushell/reedline#886

Related #13630
2025-03-13 20:31:52 +01:00
029f3843d3 Add default --empty to handle empty values (#15223)
# Description

Adds a new `--empty/-e` flag to the `default` command.

# User-Facing Changes

Before:

```nushell
$env.FOO = ""
$env.FOO = $env.FOO? | default bar
$env.FOO
# => Empty string
```

After:

```nushell
$env.FOO = ""
$env.FOO = $env.FOO? | default -e bar
$env.FOO
# => bar
```

* Uses `val.is_empty`, which means that empty lists and records are also
replaced
* Empty values in tables (with a column specifier) are also replaced.

# Tests + Formatting

7 tests added and 1 updated + 1 new example

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

# After Submitting

N/A
2025-03-13 19:50:50 +01:00
0f6996b70d Support for reading Categorical and Enum types (#15292)
# fixes https://github.com/nushell/nushell/issues/15281

# Description
Provides the ability read dataframes with Categorical and Enum data

The ability to write Categorical and Enum data will provided in a future
PR
2025-03-12 22:11:00 +01:00
9160f36ea5 Remove into bits after deprecation (#15039)
# Description
Follow up to https://github.com/nushell/nushell/pull/14634
# User-Facing Changes
`into bits` will be gone for good.

Use it under the new name `format bits`

## Note

Can be removed ahead of the `0.103.0` release as it was deprecated with
`0.102.0`
2025-03-12 22:01:14 +01:00
7f346dbf4c Remove fmt after deprecation (#15040)
# Description
Follow up to https://github.com/nushell/nushell/pull/14875
# User-Facing Changes
`fmt` will be gone for good.

Use it under the new name `format number`

## Note
Can be removed ahead of the `0.103.0` release as it was deprecated with
`0.102.0`
2025-03-12 21:43:12 +01:00
03888b9d81 Remove range command after deprecation (#15038)
# Description
Follow up to https://github.com/nushell/nushell/pull/14825

# User-Facing Changes
`range` is gone for good.

Use `slice` as a one-for-one replacement.
2025-03-12 21:42:49 +01:00
966cebec34 Adds polars list-contains command (#15304)
# Description

This  PR adds the `polars list-contains` command. It works like this:

```
~/Projects/nushell/nushell> let df = [[a]; [[a,b,c]] [[b,c,d]] [[c,d,f]]] | polars into-df -s {a: list<str>};
~/Projects/nushell/nushell> $df | polars with-column [(polars col a | polars list-contains (polars lit a) | polars as b)] | polars collect
╭───┬───────────┬───────╮
│ # │     a     │   b   │
├───┼───────────┼───────┤
│ 0 │ ╭───┬───╮ │ true  │
│   │ │ 0 │ a │ │       │
│   │ │ 1 │ b │ │       │
│   │ │ 2 │ c │ │       │
│   │ ╰───┴───╯ │       │
│ 1 │ ╭───┬───╮ │ false │
│   │ │ 0 │ b │ │       │
│   │ │ 1 │ c │ │       │
│   │ │ 2 │ d │ │       │
│   │ ╰───┴───╯ │       │
│ 2 │ ╭───┬───╮ │ false │
│   │ │ 0 │ c │ │       │
│   │ │ 1 │ d │ │       │
│   │ │ 2 │ f │ │       │
│   │ ╰───┴───╯ │       │
╰───┴───────────┴───────╯
```

or 

```
~/Projects/nushell/nushell> let df = [[a, b]; [[a,b,c], a] [[b,c,d], f] [[c,d,f], f]] | polars into-df -s {a: list<str>, b: str}
~/Projects/nushell/nushell> $df | polars with-column [(polars col a | polars list-contains b | polars as c)] | polars collect
╭───┬───────────┬───┬───────╮
│ # │     a     │ b │   c   │
├───┼───────────┼───┼───────┤
│ 0 │ ╭───┬───╮ │ a │ true  │
│   │ │ 0 │ a │ │   │       │
│   │ │ 1 │ b │ │   │       │
│   │ │ 2 │ c │ │   │       │
│   │ ╰───┴───╯ │   │       │
│ 1 │ ╭───┬───╮ │ f │ false │
│   │ │ 0 │ b │ │   │       │
│   │ │ 1 │ c │ │   │       │
│   │ │ 2 │ d │ │   │       │
│   │ ╰───┴───╯ │   │       │
│ 2 │ ╭───┬───╮ │ f │ true  │
│   │ │ 0 │ c │ │   │       │
│   │ │ 1 │ d │ │   │       │
│   │ │ 2 │ f │ │   │       │
│   │ ╰───┴───╯ │   │       │
╰───┴───────────┴───┴───────╯
```

or

```
~/Projects/nushell/nushell> let df = [[a, b]; [[1,2,3], 4] [[2,4,1], 2] [[2,1,6], 3]] | polars into-df -s {a: list<i64>, b: i64}
~/Projects/nushell/nushell> $df | polars with-column [(polars col a | polars list-contains ((polars col b) * 2) | polars as c)] | polars collect
╭───┬───────────┬───┬───────╮
│ # │     a     │ b │   c   │
├───┼───────────┼───┼───────┤
│ 0 │ ╭───┬───╮ │ 4 │ false │
│   │ │ 0 │ 1 │ │   │       │
│   │ │ 1 │ 2 │ │   │       │
│   │ │ 2 │ 3 │ │   │       │
│   │ ╰───┴───╯ │   │       │
│ 1 │ ╭───┬───╮ │ 2 │ true  │
│   │ │ 0 │ 2 │ │   │       │
│   │ │ 1 │ 4 │ │   │       │
│   │ │ 2 │ 1 │ │   │       │
│   │ ╰───┴───╯ │   │       │
│ 2 │ ╭───┬───╮ │ 3 │ true  │
│   │ │ 0 │ 2 │ │   │       │
│   │ │ 1 │ 1 │ │   │       │
│   │ │ 2 │ 6 │ │   │       │
│   │ ╰───┴───╯ │   │       │
╰───┴───────────┴───┴───────╯
```

Let me know what you think. I'm a bit surprised that a list by default
seems to get converted to "object" when doing `into-df` which is why I
added the extra `-s` flag every time to explicitly force it into a list.
2025-03-12 08:25:03 -07:00
44b7cfd696 refactor: tree-sitter-nu friendly alternative expressions (#15301)
# Description

Choose more tree-sitter-nu-friendly (if not better) expressions in nu
scripts.
The changes made in this PR all come from known issues of
`tree-sitter-nu`.

1. nested single/double quotes:
https://github.com/nushell/tree-sitter-nu/issues/125
2. module path of `use` command:
https://github.com/nushell/tree-sitter-nu/issues/165
3. where predicates of boolean column:
https://github.com/nushell/tree-sitter-nu/issues/177
4. `error make` keyword:
https://github.com/nushell/tree-sitter-nu/issues/179

Those issues are either hard to fix or "not planned" for syntactical
precision considerations ATM.

# User-Facing Changes

Should be none

# Tests + Formatting

# After Submitting
2025-03-12 08:48:19 -05:00
a17ffdfe56 Include symlinks in directory completions (#15268)
Fixes #15077

# Description

Symlinks are currently not shown in directory completions. #14667
modified completions so that symlinks wouldn't be suggested with
trailing slashes, but it did this by treating symlinks as files. This PR
includes symlinks to directories when completing directories, but still
suggests them without trailing slashes.

# User-Facing Changes

Directory completions will once again include symlinks.

# Tests + Formatting

# After Submitting
2025-03-12 08:13:41 -05:00
430b2746b8 Parse XML documents with DTDs by default, and add --disallow-dtd flag (#15272)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

# 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.
-->
This PR allows `from xml` to parse XML documents with [document type
declarations](https://en.wikipedia.org/wiki/Document_type_declaration)
by default. This is especially notable since many HTML documents start
with `<!DOCTYPE html>`, and `roxmltree` should be able to parse some
simple HTML documents. The security concerns with DTDs are [XXE
attacks](https://en.wikipedia.org/wiki/XML_external_entity_attack), and
[exponential entity expansion
attacks](https://en.wikipedia.org/wiki/Billion_laughs_attack).
`roxmltree` [doesn't
support](d2c7801624/src/tokenizer.rs (L535-L547))
external entities (it parses them, but doesn't do anything with them),
so it is not vulnerable to XXE attacks. Additionally, `roxmltree` has
[some
safeguards](d2c7801624/src/parse.rs (L424-L452))
in place to prevent exponential entity expansion, so enabling DTDs by
default is relatively safe. The worst case is no worse than running
`loop {}`, so I think allowing DTDs by default is best, and DTDs can
still be disabled with `--disallow-dtd` if needed.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
* Allows `from xml` to parse XML documents with [document type
declarations](https://en.wikipedia.org/wiki/Document_type_declaration)
by default, and adds a `--disallow-dtd` flag to disallow parsing
documents with DTDs.

This PR also improves the errors in `from xml` by pointing at the issue
in the XML source. Example:

```
$ open --raw foo.xml | from xml 
Error:   × Failed to parse XML
   ╭─[2:7]
 1 │ <html>
 2 │     <p<>hi</p>
   ·       ▲
   ·       ╰── Unexpected character <, expected a whitespace
 3 │ </html>
   ╰────
```

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
N/A
2025-03-12 08:09:55 -05:00
1e566adcfc fix(completion): full set of operators for type any (#15303)
# Description

As elaborated
[here](https://github.com/nushell/nushell/issues/13676#issuecomment-2717096417),
a full set probably is a more thoughtful approximation for unknown
types.

# User-Facing Changes

# Tests + Formatting

Adjusted

# After Submitting
2025-03-12 08:04:20 -05:00
789781665d fix(lsp): find_id for custom def in custom def (#15289)
# Description

Enables hover/rename/references for:

```nushell
def foo [] {
  def bar [] { }
     # |____________ this custom command
}
```

# User-Facing Changes

# Tests + Formatting

+1

# After Submitting
2025-03-12 07:35:28 -05:00
e926919582 polars open: exposing the ability to configure hive settings. (#15255)
# Description
Exposes parameters for working with
[hive](https://docs.pola.rs/user-guide/io/hive/#scanning-hive-partitioned-data)
partitioning.

# User-Facing Changes
- Added flags `--hive-enabled`, `--hive-start-idx`, `--hive-schema`,
`--hive-try-parse-dates` to `polars open`
2025-03-11 14:18:36 -07:00
8d5d01bbc9 Fix improper application of local timezone offset to Unix epochs (#15283)
Fix failing test by ignoring the local offset when converting times, but still displaying the
resulting date in the local timezone (including applicable DST offset).

# User-Facing Changes

Fix: Unix Epochs now convert consistently regardless of whether DST is
in effect in the local timezone or not.
2025-03-11 11:57:37 -04:00
58f7cfd099 Test on Beta Toolchain (#15280)
# Description
In the [Nushell core team meeting
2025-02-19](https://hackmd.io/r3V83bMdQqKMwFxz90nBDg?view) we decided to
run tests on the beta toolchain to contribute to the Rust project as a
whole. These tests do not need to succeed for us to go further but allow
us to investigate if the beta toolchain broke something.

# User-Facing Changes

None.

# Tests + Formatting


Just a new workflow.

# After Submitting

Watch out for modification of this file changing the notified person
2025-03-11 14:55:35 +01:00
b432866dc9 bugfix: math commands now return error with infinite range [#15135] (#15236)
### Description
Fixes issue #15135

Result

![image](https://github.com/user-attachments/assets/9ff4397f-db79-46df-b1da-2d09f50dd63f)

Also this works with other commands: min, max, sum, product, avg...

### User-Facing Changes
Error is returned, instead of console completely blocked and having to
be killed
I chose "Incorrect value", because commands accept inputs of range type,
just cannot work with unbounded ranges.

### Tests + Formatting
- ran cargo fmt, clippy
- added tests
2025-03-11 14:40:26 +01:00
81e496673e refactor(lsp): span fix made easy by bumping lsp-textdocument to 0.4.2 (#15287)
# Description

The upstream crate fixed a bug of position calc, which made some extra
checking in lsp unnecessary.
Also moved some follow-up fixing of #15238 from #15270 here, as it has
something to do with previous position calc bug.

# User-Facing Changes

# Tests + Formatting

Adjusted

# After Submitting
2025-03-11 06:13:58 -05:00
2dab65f852 Polars: Map pq extension to parquet files (#15284)
# Description
Files with the extension pq will automatically be treated as parquet
files.

closes #15282
2025-03-10 16:25:34 -05:00
95dcb2fd6c Add filesize.show_unit config option (#15276)
# Description

Continuation of #15271. This PR adds the
`$env.config.filesize.show_unit` option to allow the ability to omit the
filesize unit. Useful if `$env.config.filesize.unit` is set to a fixed
unit, and you don't want the same unit repeated over and over.

# User-Facing Changes

- Adds the `$env.config.filesize.show_unit` option.
2025-03-09 17:34:55 -05:00
d97b2e3c60 Respect system locale when formatting file sizes via config (#15271)
# Description

Commands and other pieces of code using `$env.config.format.filesize` to
format filesizes now respect the system locale when formatting the
numeric portion of a file size.

# User-Facing Changes

- System locale is respected when using `$env.config.format.filesize` to
format file sizes.
- Formatting a file size with a binary unit is now exact for large file
sizes and units.
- The output of `to text` is no longer dependent on the config.
2025-03-09 15:43:02 -05:00
4fe7865ad0 allow --group-digits to be used in into string (#15265)
# Description

This PR allows the `into string` command to pass the `--group-digits`
flag which already existed in this code but was hard coded to `false`.

Now you can do things like
```nushell
❯ 1234567890 | into string --group-digits
1,234,567,890
❯ ls | into string size --group-digits | last 5
╭─#─┬────────name─────────┬─type─┬──size──┬───modified───╮
│ 0 │ README.md           │ file │ 12,606 │ 4 weeks ago  │
│ 1 │ rust-toolchain.toml │ file │ 1,125  │ 2 weeks ago  │
│ 2 │ SECURITY.md         │ file │ 2,712  │ 7 months ago │
│ 3 │ toolkit.nu          │ file │ 21,929 │ 2 months ago │
│ 4 │ typos.toml          │ file │ 542    │ 7 months ago │
╰─#─┴────────name─────────┴─type─┴──size──┴───modified───╯
❯ "12345" | into string --group-digits
12,345
```
# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-03-07 12:43:35 -06:00
d122bc3d89 fix: security_audit, bump ring from 0.17.8 to 0.17.13 (#15263)
Fixes this:

<div class="Box p-3 markdown-body f5 mb-4">
          <h2 dir="auto">Vulnerabilities</h2>
<h3 dir="auto"><a
href="https://rustsec.org/advisories/RUSTSEC-2025-0009.html"
rel="nofollow">RUSTSEC-2025-0009</a></h3>
<blockquote>
<p dir="auto">Some AES functions may panic when overflow checking is
enabled.</p>
</blockquote>
<markdown-accessiblity-table data-catalyst=""><table role="table">
<thead>
<tr>
<th>Details</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Package</td>
<td><code class="notranslate">ring</code></td>
</tr>
<tr>
<td>Version</td>
<td><code class="notranslate">0.17.8</code></td>
</tr>
<tr>
<td>URL</td>
<td><a
href="https://github.com/briansmith/ring/blob/main/RELEASES.md#version-01712-2025-03-05">https://github.com/briansmith/ring/blob/main/RELEASES.md#version-01712-2025-03-05</a></td>
</tr>
<tr>
<td>Date</td>
<td>2025-03-06</td>
</tr>
<tr>
<td>Patched versions</td>
<td><code class="notranslate">&gt;=0.17.12</code></td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto"><code
class="notranslate">ring::aead::quic::HeaderProtectionKey::new_mask()</code>
may panic when overflow<br>
checking is enabled. In the QUIC protocol, an attacker can induce this
panic by<br>
sending a specially-crafted packet. Even unintentionally it is likely to
occur<br>
in 1 out of every 2**32 packets sent and/or received.</p>
<p dir="auto">On 64-bit targets operations using <code
class="notranslate">ring::aead::{AES_128_GCM, AES_256_GCM}</code>
may<br>
panic when overflow checking is enabled, when encrypting/decrypting
approximately<br>
68,719,476,700 bytes (about 64 gigabytes) of data in a single chunk.
Protocols<br>
like TLS and SSH are not affected by this because those protocols break
large<br>
amounts of data into small chunks. Similarly, most applications will
not<br>
attempt to encrypt/decrypt 64GB of data in one chunk.</p>
<p dir="auto">Overflow checking is not enabled in release mode by
default, but<br>
<code class="notranslate">RUSTFLAGS=&amp;quot;-C
overflow-checks&amp;quot;</code> or <code
class="notranslate">overflow-checks = true</code> in the Cargo.toml<br>
profile can override this. Overflow checking is usually enabled by
default in<br>
debug mode.</p>
        </div>
2025-03-07 08:55:57 -06:00
7d17c2eb5e add a helpful msg to indicate a job has been frozen (#15206)
# Description
As stated in the title, when pressing ctrl-z, I sometimes feel confused
because I return to the REPL without any message. I don't know if the
process has been killed or suspended.

This PR aims to add a message to notify the user that the process has
been frozen.

# User-Facing Changes
After pressing `ctrl-z`.  A message will be printed in repl.


![图片](https://github.com/user-attachments/assets/5fe502eb-439e-4022-889f-64ba52cc2825)

# Tests + Formatting
NaN

# After Submitting
NaN
2025-03-06 11:20:58 -05:00
0e6e9abc12 bugfix: add "to yml" command (#15254)
# Description
This fixes #15240, which can be closed after merge.

# User-Facing Changes
- user get now use `to yml` -> exactly the same as `to yaml`


![2025-03-06_00h01_27](https://github.com/user-attachments/assets/e002a96a-26dd-4f9c-9b45-b456a95be158)

# Tests + Formatting
Cargo fmt and clippy 🆗 
I added a test in the only place I could find where `to yaml` was
already tested.

I didn't see the `save.rs::convert_to_extension` function tested
anywhere, but maybe I missed it.

# After Submitting

Not sure this needs an update on the documentation  What do you
suggest?

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2025-03-06 14:32:36 +01:00
f3982278e8 feat(random uuid): add support for uuid versions other than 4. (#15239)
This PR implements the changes proposed in #15112 without any breaking
changes. Should close #15112 post the review.

# Description

Added functionality to generate `uuid` versions 1, 3, 4, 5, 7 instead of
just the version 4.
- Users can now add a `-v n` flag to specify the version of uuid they
want to generate and it maintains backward compatibility by returning a
v4 uuid by default if no flags are passed.
- Versions 3 and 5 have the additional but required namespace (`-s`) and
name (`-n`) arguments too. Version 1 requires a mac address (`-m`).
# User-Facing Changes
- Added support for uuid versions 1, 3, 5 and 7.
- For v3 and v5, the namespace and name arguments are required and hence
there will be an error if those are not passed. Similarly the mac
address for v1.
- Full backward compatibility by setting v4 as default.
# Tests + Formatting

Tests added:
in `nu-command::commands::random`
- generates_valid_uuid4_by_default
- generates_valid_uuid1
- generates_valid_uuid3_with_namespace_and_name
- generates_valid_uuid4
- generates_valid_uuid5_with_namespace_and_name
- generates_valid_uuid7
2025-03-06 14:21:52 +01:00
b1e591f84c Fix unterminated loop in parse_record (#15246)
Fixes #15243

# Description

As noted in #15243, a record with more characters after it (e.g.,
`{a:b}/`) will cause an OOM due to an infinite loop, introduced by
#15023. This happens because the entire string `{a:b}/` is lexed as one
token and passed to `parse_record`, where it repeatedly lexes until it
hits the closing `}`. This PR detects such extra characters and reports
an error.

# User-Facing Changes

`{a:b}/` and other such constructions will no longer cause infinite
loops. Before #15023, you would've seen an "Unclosed delimiter" error
message, but this PR changes that to "Invalid characters."

```
Error: nu::parser::extra_token_after_closing_delimiter

  × Invalid characters after closing delimiter
   ╭─[entry #5:1:7]
 1 │  {a:b}/
   ·       ┬
   ·       ╰── invalid characters
   ╰────
  help: Try removing them.
```

# Tests + Formatting

# After Submitting
2025-03-05 21:02:03 +01:00
122bcff356 fix $env.FILE_PWD and $env.CURRENT_FILE inside overlay use (#15126)
# Description
Fixes: #14540
The change is similar to #14101

User input can be a directory, in this case, we need to use the return
value of find_in_dirs_env carefully, so in case, I renamed
maybe_file_path to maybe_file_path_or_dir to emphasize it.

# User-Facing Changes
NaN

# Tests + Formatting
Added 2 test cases

# After Submitting
2025-03-05 21:13:44 +02:00
087fe484f6 Enhance polars plugin documentation (#15250)
This PR (based on #15249 and #15248 because it mentions them) adds extra
documentation to the main polars command outlining the main datatypes
that are used by the plugin. The lack of a description of the types
involved in `polars xxx` commands was quite confusing to me when I
started using the plugin and this is a first try improving it.

I didn't find a better place but please let me know what you think.
2025-03-05 08:22:21 -08:00
551fecd10d adds And and Or operators to polars plugin nu_expressions (#15248)
solution for #15242 

adds "And"

```
~/Projects/nushell> [[a, b]; [1., 2.], [3.,3.], [4., 6.]] | polars into-df | polars filter (((polars col a) > 2) and ((polars col b) < 5))
╭───┬──────┬──────╮
│ # │  a   │  b   │
├───┼──────┼──────┤
│ 0 │ 3.00 │ 3.00 │
╰───┴──────┴──────╯
```

adds "Or"

```
~/Projects/nushell> [[a, b]; [1., 2.], [3.,3.], [4., 6.]] | polars into-df | polars filter (((polars col a) > 7) or ((polars col b) > 5))
╭───┬──────┬──────╮
│ # │  a   │  b   │
├───┼──────┼──────┤
│ 0 │ 4.00 │ 6.00 │
╰───┴──────┴──────╯
```

but not (yet) xor because polars doesn't have a direct expression for
logical_xor

```
~/Projects/nushell> [[a, b]; [1., 2.], [3.,3.], [4., 6.]] | polars into-df | polars filter (((polars col a) > 7) xor ((polars col b) > 5))
Error: nu:🐚:operator_unsupported_type

  × The 'xor' operator does not work on values of type 'NuExpression'.
   ╭─[entry #5:1:94]
 1 │ [[a, b]; [1., 2.], [3.,3.], [4., 6.]] | polars into-df | polars filter (((polars col a) > 7) xor ((polars col b) > 5))
   ·                                                                                              ─┬─┬
   ·                                                                                               │ ╰── NuExpression
   ·                                                                                               ╰── does not support 'NuExpression'
   ╰────
```

Co-authored-by: Jack Wright <56345+ayax79@users.noreply.github.com>
2025-03-05 08:21:20 -08:00
88bbe4abaa Add Xor to polars plugin nu_expressions (#15249)
solution for #15242 ,  based on PR #15248 .

Allows doing this:

```
~/Projects/nushell> [[a, b]; [1., 2.], [3.,3.], [4., 6.]] | polars into-df | polars filter (((polars col a) < 2) xor ((polars col b) > 5))
╭───┬──────┬──────╮
│ # │  a   │  b   │
├───┼──────┼──────┤
│ 0 │ 1.00 │ 2.00 │
│ 1 │ 4.00 │ 6.00 │
╰───┴──────┴──────╯
```
2025-03-05 08:03:35 -08:00
49f92e9090 feat(lsp): completion items now respect the append_whitespace flag (#15247)
# Description

Append space if marked as required.
Aligned behavior as the REPL completion.

# User-Facing Changes

# Tests + Formatting

Adjusted

# After Submitting
2025-03-05 05:45:27 -06:00
4779d69de6 prevent panic when parsing incomplete multi-expr (|) matches (#15230)
Fixes #14971, fixes #15229

# User-Facing Changes

Fixes a panic when variable data is accessed after invalid usage of the
`|` separator, which made it impossible to type certain match arms:

```nushell
> match $in { 1 |
Error:   x Main thread panicked.
  |-> at crates/nu-protocol/src/engine/state_delta.rs💯14
  `-> internal error: missing required scope frame
```

# Description

Removes duplicative calls to `exit_scope` from an inner loop when `|`
parse errors are encountered. The outer loop creates and exits scopes
for each match arm.
2025-03-04 05:34:34 -06:00
de7b000505 fix(lsp): completion on command with following text (#15238)
# Description

Fixes a bug introduced by #15188 

# User-Facing Changes

Before:

<img width="216" alt="image"
src="https://github.com/user-attachments/assets/5846a844-d88e-4d9f-b9e2-e2478c7acb37"
/>

And will crash the lsp server.

After:

<img width="454" alt="image"
src="https://github.com/user-attachments/assets/85e727d6-fef5-426b-818c-e554d3c49c7d"
/>

# Tests + Formatting

adjusted

# After Submitting
2025-03-04 05:33:55 -06:00
9eaa8908d2 doc: clarify trailing line ending in 'to json -r' documentation (#15234)
# Description

Fixes issue  #15215

# User-Facing Changes

Change in help msg in "to json" command with -r flag

# Tests + Formatting
cargo fmt 🆗 

# After Submitting
Doc for that is generated from code I think, so 🆗
2025-03-03 16:49:29 -06:00
fc72aa6abe feat(lsp): signature help (manually triggered) (#15233)
# Description

To check for missing parameters

<img width="417" alt="image"
src="https://github.com/user-attachments/assets/5e2a8356-5fd9-4d15-8ae6-08321f9d6e0b"
/>

# User-Facing Changes

For other languages, the help request can be triggered by the `(`
character of the function call.
Editors like nvim refuse to set the trigger character to space, and
space is probably way too common for that.

So this kind of request has to be triggered manually for now.
example of nvim config:

```lua
vim.api.nvim_create_autocmd("FileType", {
  pattern = "nu",
  callback = function(event)
    vim.bo[event.buf].commentstring = "# %s"
    vim.api.nvim_buf_set_keymap(event.buf, "i", "<C-f>", "", {
      callback = function()
        vim.lsp.buf.signature_help()
      end,
    })
  end,
})
```

# Tests + Formatting

+2

# After Submitting
2025-03-03 06:54:42 -06:00
8e1385417e fix(lsp): completion label descriptions for cell_path and external values (#15226)
# Description

The type shown in the completion description is 1 level higher than the
actual entry.
Also cleans some TODOs for `SuggetionKind`.

# User-Facing Changes

## Before

<img width="409" alt="image"
src="https://github.com/user-attachments/assets/c7d7df02-aed9-4ea9-892a-0bca707352eb"
/>

<img width="491" alt="image"
src="https://github.com/user-attachments/assets/9b9394d4-62ee-4924-9840-402f00d88a8a"
/>

## After

<img width="425" alt="image"
src="https://github.com/user-attachments/assets/d8f41059-2c68-4902-9c32-d789f91b6d77"
/>

<img width="425" alt="image"
src="https://github.com/user-attachments/assets/ce03afb9-6c1f-4a65-a1cc-cbba4655abb3"
/>

# Tests + Formatting

Adjusted accordingly

# After Submitting
2025-03-02 16:17:12 -06:00
95f89a093a Add ansi codes to move cursor position (#15221)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

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

Add ansi codes to move cursor position: `ansi cursor_left`, `ansi
cursor_right`, `ansi cursor_up`, `ansi cursor_down`
Why I add these? I'm trying to add a spinner to the message end for a
long running task, just to find that I need to move the cursor left to
make it work as expected: `with-progress 'Waiting for the task to
finish' { sleep 10sec }`
```nu
def with-progress [
  message: string,         # Message to display
  action: closure,         # Action to perform
  --success: string,       # Success message
  --error: string          # Error message
] {
  print -n $'($message)   '
  # ASCII spinner frames
  let frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']

  # Start the spinner in the background
  let spinner_pid = job spawn {
    mut i = 0
    print -n (ansi cursor_off)
    loop {
      print -n (ansi cursor_left)
      print -n ($frames | get $i)
      sleep 100ms
      $i = ($i + 1) mod ($frames | length)
    }
  }

  # Run the action and capture result
  let result = try {
    do $action
    { success: true }
  } catch {
    { success: false }
  }

  # Stop the spinner
  job kill $spinner_pid
  print "\r                                                  \r"

  # Show appropriate message
  if $result.success {
    print ($success | default '✓ Done!')
  } else {
    print ($error | default '✗ Failed!')
    exit 1
  }
}
```

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

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-03-01 11:30:00 -06:00
e9b677a9e9 fix compact to handle empty list or record in column (#15213)
If a table contains an empty list or record in one column and both column
and -e flags are used, then skip that row.

`compact -e` now skips empty values in a column where as before they were
ignored. Example:

 ```nu
[["a", "b"]; ["c", "d"], ["h", []]] 
| compact -e b
```
before

```plain
 #   a         b
────────────────────────
 0   c   d
 1   h   [list 0 items]
```
after
```plain
 #   a   b
───────────
 0   c   d
```
2025-03-01 07:47:55 -05:00
7555743ccc fix(lsp): completion of commands defined after the cursor (#15188)
# Description

Completion feature in LSP can't deal with commands defined after the
cursor before this PR.
This PR adds an alternative completion route where text is not truncated
and no extra `a` appended.

This will also ease future implementation of [signature
help](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_signatureHelp).

# User-Facing Changes

# Tests + Formatting

+6

# After Submitting
2025-03-01 06:21:53 -06:00
93612974e0 fix(test-support): use CARGO_BUILD_TARGET_DIR env var (#15212)
# Description

cargo uses both CARGO_BUILD_TARGET_DIR and CARGO_TARGET_DIR, so check
for CARGO_BUILD_TARGET_DIR if CARGO_TARGET_DIR is not found

https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir
2025-02-28 20:08:44 +01:00
52a35827c7 fix(completion): edge cases of operator completions (#15169)
# Description

Improves the completeness of operator completions.
Check the new test cases for details.

# User-Facing Changes

# Tests + Formatting

+4

# After Submitting
2025-02-28 19:39:59 +01:00
c5a14bb8ff check signals in nu-glob and ls (#15140)
Fixes #10144

# User-Facing Changes

Long running glob expansions and `ls` runs (e.g. `ls /**/*`) can now be
interrupted with ctrl-c.
2025-02-28 19:36:39 +01:00
48bdcc71f4 update reedline editcommands in nushell (#15191)
# Description

This PR tries to update the EditCommands and ReedlineEvents by adding
missing items and ordering them to the same order that the reedline enum
has them listed.

@sholderbach When you have time, would you mind looking at this please.
I left some TODOs because I wasn't sure how to implement them. I also
guessed at some of the other implementations. I don't use vim much so
I'm not really sure how these are supposed to act. I was really just
trying to fill in the blanks.

# User-Facing Changes
Closes #15167

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

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

---------

Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
2025-02-28 17:41:27 +01:00
78c93e5ae0 Run-time pipeline input type checking performance optimizations (#15192)
# Description

Avoids cloning custom command signatures during run-time pipeline input
type checking

# User-Facing Changes

N/A

# Tests + Formatting

N/A
2025-02-27 14:29:25 +01:00
96af27fb4c fix: new clippy warnings from rust 1.85.0 (#15203)
# Description
Mainly some cleanup of `map_or`.
2025-02-27 14:11:47 +01:00
12b8b4580c build(deps): bump rust-embed from 8.5.0 to 8.6.0 (#15183)
Bumps [rust-embed](https://github.com/pyros2097/rust-embed) from 8.5.0
to 8.6.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyrossh/rust-embed/blob/master/changelog.md">rust-embed's
changelog</a>.</em></p>
<blockquote>
<h2>[8.6.0] - 2025-02-25</h2>
<ul>
<li>Update include-flate to 0.3 <a
href="https://redirect.github.com/pyrossh/rust-embed/pull/246">#246</a>.
Thanks to <a href="https://github.com/krant">krant</a></li>
<li>refactor: remove redundant reference and closure <a
href="https://redirect.github.com/pyrossh/rust-embed/pull/250">#250</a>.
Thanks to <a href="https://github.com/hamirmahal">hamirmahal</a></li>
<li>refactor: replace map().unwrap_or_else(). <a
href="https://redirect.github.com/pyrossh/rust-embed/pull/255">#250</a>.
Thanks to <a href="https://github.com/hamirmahal">hamirmahal</a></li>
<li>Compatible with Axum 0.7.9 <a
href="https://redirect.github.com/pyrossh/rust-embed/pull/253">#253</a>.
Thanks to <a href="https://github.com/wkmyws">wkmyws</a></li>
<li>Add allow_missing option to derive macro <a
href="https://redirect.github.com/pyrossh/rust-embed/pull/256">#256</a>.
Thanks to <a href="https://github.com/lirannl">lirannl</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/pyros2097/rust-embed/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rust-embed&package-manager=cargo&previous-version=8.5.0&new-version=8.6.0)](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 show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@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>
2025-02-26 23:57:10 +08:00
1616acd124 update query json help and examples (#15190)
# Description

This PR adds extra_description stating what syntax query json is with
links. It also adds some examples since query json was written before
examples existed for plugins.

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-02-26 09:15:14 -06:00
0cb4281fdb Bump reedline to latest commit (#15189)
Removes one `itertools` version duplication
2025-02-26 07:45:49 -06:00
6f6ad23072 Bump ratatui to 0.29.0 (#15187)
This is the most recent version

Deduplicates the `crossterm` dependency, brings `itertools` in line with
the majority of dependencies.

In the fight against compile times this sadly introduces a
proc-macro-crate for writing proc-macros (`darling`) as a transitive
dependency. So may not lead to a compile time improvement (or could make
it even slightly worse)

Observation: Cargo changed the `Cargo.lock` file version when running
this. (this should still be the specified toolchain, so don't expect a
risk of locking out the expected `cargo` versions)
2025-02-26 06:22:47 -06:00
1ab09256d7 build(deps): bump actions-rust-lang/setup-rust-toolchain from 1.10.1 to 1.11.0 (#15179)
Bumps
[actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain)
from 1.10.1 to 1.11.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions-rust-lang/setup-rust-toolchain/releases">actions-rust-lang/setup-rust-toolchain's
releases</a>.</em></p>
<blockquote>
<h2>v1.11.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat: add shared-cache-key to inputs by <a
href="https://github.com/skanehira"><code>@​skanehira</code></a> in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/52">actions-rust-lang/setup-rust-toolchain#52</a></li>
<li>fix: add cache-bin input with true as default value by <a
href="https://github.com/enkhjile"><code>@​enkhjile</code></a> in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/51">actions-rust-lang/setup-rust-toolchain#51</a></li>
<li>chore: prepare release 1.11.0 by <a
href="https://github.com/robjtede"><code>@​robjtede</code></a> in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/53">actions-rust-lang/setup-rust-toolchain#53</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/skanehira"><code>@​skanehira</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/52">actions-rust-lang/setup-rust-toolchain#52</a></li>
<li><a href="https://github.com/enkhjile"><code>@​enkhjile</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/51">actions-rust-lang/setup-rust-toolchain#51</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1...v1.11.0">https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1...v1.11.0</a></p>
</blockquote>
</details>
<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.11.0] - 2025-02-24</h2>
<ul>
<li>Add new parameter <code>cache-bin</code> that is propagated to
<code>Swatinem/rust-cache</code> as <code>cache-bin</code> (<a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/51">#51</a>
by <a
href="https://github.com/enkhjile"><code>@​enkhjile</code></a>)</li>
<li>Add new parameter <code>cache-shared-key</code> that is propagated
to <code>Swatinem/rust-cache</code> as <code>shared-key</code> (<a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/52">#52</a>
by <a
href="https://github.com/skanehira"><code>@​skanehira</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9399c7bb15"><code>9399c7b</code></a>
Merge pull request <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/53">#53</a>
from actions-rust-lang/rel-1110</li>
<li><a
href="3c7cfa82dc"><code>3c7cfa8</code></a>
Merge branch 'main' into rel-1110</li>
<li><a
href="b38f618be2"><code>b38f618</code></a>
Merge pull request <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/51">#51</a>
from enkhjile/main</li>
<li><a
href="6cbea1a794"><code>6cbea1a</code></a>
chore: prepare release 1.11.0</li>
<li><a
href="6f9a9da9f9"><code>6f9a9da</code></a>
Merge branch 'main' into main</li>
<li><a
href="2ad14f9ee2"><code>2ad14f9</code></a>
Merge pull request <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/52">#52</a>
from skanehira/main</li>
<li><a
href="30081c4da5"><code>30081c4</code></a>
fix: add cache-bin input with true as default value</li>
<li><a
href="f8efd60d2d"><code>f8efd60</code></a>
feat: add shared-cache-key to inputs</li>
<li><a
href="97db979bf8"><code>97db979</code></a>
Specify dependencies in README</li>
<li>See full diff in <a
href="https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.10.1...v1.11.0">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.10.1&new-version=1.11.0)](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 show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@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>
2025-02-26 09:20:31 +08:00
ee14811912 build(deps): bump crate-ci/typos from 1.29.5 to 1.29.10 (#15180)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.29.5 to
1.29.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/releases">crate-ci/typos's
releases</a>.</em></p>
<blockquote>
<h2>v1.29.10</h2>
<h2>[1.29.10] - 2025-02-25</h2>
<h3>Fixes</h3>
<ul>
<li>Also correct <code>contaminent</code> as
<code>contaminant</code></li>
</ul>
<h2>v1.29.9</h2>
<h2>[1.29.9] - 2025-02-20</h2>
<h3>Fixes</h3>
<ul>
<li><em>(action)</em> Correctly get binary for some aarch64 systems</li>
</ul>
<h2>v1.29.8</h2>
<h2>[1.29.8] - 2025-02-19</h2>
<h3>Features</h3>
<ul>
<li>Attempt to build Linux aarch64 binaries</li>
</ul>
<h2>v1.29.7</h2>
<h2>[1.29.7] - 2025-02-13</h2>
<h3>Fixes</h3>
<ul>
<li>Don't correct <code>implementors</code></li>
</ul>
<h2>v1.29.6</h2>
<h2>[1.29.6] - 2025-02-13</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1200">January
2025</a> changes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's
changelog</a>.</em></p>
<blockquote>
<h2>[1.29.10] - 2025-02-25</h2>
<h3>Fixes</h3>
<ul>
<li>Also correct <code>contaminent</code> as
<code>contaminant</code></li>
</ul>
<h2>[1.29.9] - 2025-02-20</h2>
<h3>Fixes</h3>
<ul>
<li><em>(action)</em> Correctly get binary for some aarch64 systems</li>
</ul>
<h2>[1.29.8] - 2025-02-19</h2>
<h3>Features</h3>
<ul>
<li>Attempt to build Linux aarch64 binaries</li>
</ul>
<h2>[1.29.7] - 2025-02-13</h2>
<h3>Fixes</h3>
<ul>
<li>Don't correct <code>implementors</code></li>
</ul>
<h2>[1.29.6] - 2025-02-13</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1200">January
2025</a> changes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="db35ee91e8"><code>db35ee9</code></a>
chore: Release</li>
<li><a
href="9f43c4dbd2"><code>9f43c4d</code></a>
docs: Update changelog</li>
<li><a
href="a1da2ce137"><code>a1da2ce</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1244">#1244</a>
from epage/containment</li>
<li><a
href="d74d5fd5ad"><code>d74d5fd</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1243">#1243</a>
from epage/dict</li>
<li><a
href="fa6122604f"><code>fa61226</code></a>
refactor(dict): Drop a dict</li>
<li><a
href="6276d585f7"><code>6276d58</code></a>
fix(dict): Correct contaminents to another spelling</li>
<li><a
href="07c9e1f6fa"><code>07c9e1f</code></a>
chore(deps): Update Rust Stable to v1.85 (<a
href="https://redirect.github.com/crate-ci/typos/issues/1241">#1241</a>)</li>
<li><a
href="71643b1191"><code>71643b1</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1240">#1240</a>
from szepeviktor/patch-1</li>
<li><a
href="931a5804a4"><code>931a580</code></a>
Fix typo in README</li>
<li><a
href="c5137fd6aa"><code>c5137fd</code></a>
refactor(action): Isolate unique parts</li>
<li>Additional commits viewable in <a
href="https://github.com/crate-ci/typos/compare/v1.29.5...v1.29.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=crate-ci/typos&package-manager=github_actions&previous-version=1.29.5&new-version=1.29.10)](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 show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@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>
2025-02-26 09:16:23 +08:00
7939fb05ea polars strip-chars: Allow any polars expression for pattern argument (#15178)
# Description 
Allow any polars expression for pattern argument for `polars
strip-chars`
2025-02-25 17:59:02 -06:00
53d30ee7ea add polars str strip chars (with --end / --start options) (#15118)
# Description

This PR adds `polars str-strip-chars-end`

# User-Facing Changes

New function that can be used as follows:

```
~/Projects/nushell> [[text]; [hello!!!] [world!!!]] | polars into-df | polars select (polars col text | polars str-strip-chars-end "!") | polars collect
╭───┬───────╮
│ # │ text  │
├───┼───────┤
│ 0 │ hello │
│ 1 │ world │
╰───┴───────╯
```

# Tests + Formatting

tests ran locally.
I ran the formatter.

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-02-25 15:37:52 -08:00
058ce0ed2d move to polars bigidx (#15177)
Fixes [#15157](https://github.com/nushell/nushell/issues/15157)

# Description
Utilizes the polar's bigidx feature to support massive datasets.
2025-02-25 17:29:56 -06:00
9bb7f0c7dc Jobs (#14883)
# Description

This is an attempt to improve the nushell situation with regard to issue
#247.

This PR implements:
- [X] spawning jobs: `job spawn { do_background_thing }`
Jobs will be implemented as threads and not forks, to maintain a
consistent behavior between unix and windows.

- [X] listing running jobs: `job list`
This should allow users to list what background tasks they currently
have running.

- [X] killing jobs: `job kill <id>`
- [X] interupting nushell code in the job's background thread
- [X] interrupting the job's currently-running process, if any.

Things that should be taken into consideration for implementation:
- [X] (unix-only) Handling `TSTP` signals while executing code and
turning the current program into a background job, and unfreezing them
in foreground `job unfreeze`.

- [X] Ensuring processes spawned by background jobs get distinct process
groups from the nushell shell itself

This PR originally aimed to implement some of the following, but it is
probably ideal to be left for another PR (scope creep)
- Disowning external process jobs (`job dispatch`)
- Inter job communication (`job send/recv`)

Roadblocks encountered so far:
- Nushell does some weird terminal sequence magics which make so that
when a background process or thread prints something to stderr and the
prompt is idle, the stderr output ends up showing up weirdly
2025-02-25 12:09:52 -05:00
9521b209d1 allow bench to handle larger numbers (#15162)
# Description

This PR allows `bench` to handle larger numbers by using `into float`
2025-02-25 15:02:42 +01:00
f51a79181a feat(lsp): semantic tokens for highlighting internal commands with spaces (#15173)
# Description

We decided to move that specific highlighting task from tree-sitter-nu
to lsp for various reasons.
https://github.com/nushell/tree-sitter-nu/pull/184

# User-Facing Changes

Before:
<img width="404" alt="image"
src="https://github.com/user-attachments/assets/79fad167-e424-4411-8aa2-334f08ecc4ab"
/>

After:
<img width="404" alt="image"
src="https://github.com/user-attachments/assets/8eec7c6c-2f63-4a7d-9e98-9e0c397be6bf"
/>


# Tests + Formatting
+1
# After Submitting
2025-02-25 07:14:48 -06:00
938fa6ee55 fix(completion): prefix_str should be trimmed to element_expression (#15171)
# Description
Hot fix of  a newly introduced bug by #15086.
Forgot to trim the line str according to the expression span, which will
disable external command completions in many cases.

Also adds the suggestion kind to external commands, for lsp
visualization.

# User-Facing Changes

Before:
<img width="246" alt="image"
src="https://github.com/user-attachments/assets/c62904f6-0dd7-4368-8f0b-aacd6fe590f0"
/>

After:
<img width="291" alt="image"
src="https://github.com/user-attachments/assets/76316649-956f-4828-94cb-41f79d5f94f7"
/>

I find it better to visually distinguish externals from internals, so
`function` for internals and `interface` for externals.
But it's arguably not the best option.

# Tests + Formatting

test case adjusted

# After Submitting
2025-02-25 11:47:10 +01:00
1d0d91d5e5 Improve documentation for each command (#15172)
# Description

It is a rework of https://github.com/nushell/nushell.github.io/pull/1819

So, I was wasting time looking for equivalent of `filter_map` in Nu,
unaware that `each` already has it. This PR is to make it clear in the
documentation, saving other user's time.

# User-Facing Changes

No

# Tests + Formatting

No

# After Submitting

No
2025-02-25 11:01:09 +01:00
252155bdb9 Add insert benchmarks (#15166)
# Description

Adds some benchmarks for inserting into records and tables as part of
#12624.
2025-02-24 17:37:25 -08:00
be508cbd7f refactor(completion): flatten_shape -> expression for internal/external/operator (#15086)
# Description

Fixes #14852

As the completion rules are somehow intertwined between internals and
externals,
this PR is relatively messy, and has larger probability to break things,
@fdncred @ysthakur @sholderbach
But I strongly believe this is a better direction to go. Edge cases
should be easier to fix in the dedicated branches.

There're no flattened expression based completion rules left.

# User-Facing Changes

# Tests + Formatting
+7
# After Submitting

---------

Co-authored-by: Yash Thakur <45539777+ysthakur@users.noreply.github.com>
2025-02-23 13:47:49 -05:00
fcd1d59abd split list: add streaming, closure argument, and splitting before/after a separator (#15161)
- this PR addresses most of the points in #13153

# Description

- make `split list` support streaming
- **[BREAKING CHANGE]** if the input is split on consecutive items, the
empty lists between those items are preserved.
  e.g. `[1 1 0 0 3 3 0 4 4] | split list 0` == `[[1 1] [] [2 2] [3 3]]`
- accept a closure as argument, the closure is called for each item, and
if it returns `true` the list is split on that item
- added `--split` flag, which allows keeping the separator items.
`--split=after` splits the list *after* the separator and
`--split=before` splits the list *before* the separator.
  `--split=on` is the default behavior where the separator is lost

# User-Facing Changes

`split list`:
- keeps empty sublists
- allows using a closure to determine items to split on
- allows keeping the separator items with `--split=after` and
`--split=before`

# Tests + Formatting

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

# After Submitting
N/A

---------

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
2025-02-23 07:53:38 -06:00
083c534948 Fix insert/upsert creation for nested lists (#15131) (#15133)
# Description
This PR fixes #15131 by allowing the `insert` and `upsert` commands to
create lists where they may be expected based on the cell path provided.
For example, the below would have previously thrown an error, but now
creates lists and list elements where necessary
<img width="173" alt="Screenshot 2025-02-17 at 2 46 12 AM"
src="https://github.com/user-attachments/assets/6d680e7e-6268-42ed-a037-a0795014a7e0"
/>
<img width="200" alt="Screenshot 2025-02-17 at 2 46 16 AM"
src="https://github.com/user-attachments/assets/50d0e8eb-aabb-49fe-b961-5f7489fdc993"
/>
<img width="284" alt="Screenshot 2025-02-17 at 2 45 43 AM"
src="https://github.com/user-attachments/assets/242a2ec6-7e8f-4a51-92ce-9d5ec10f867f"
/>

# User-Facing Changes
This change removes errors that were previously raised by
`insert_data_at_cell_path` and `upsert_data_at_cell_path`. If one of
these commands encountered an unknown cell path in cases such as these,
it would either raise a "Not a list value" as the list index is used on
a record:

<img width="326" alt="Screenshot 2025-02-17 at 2 46 43 AM"
src="https://github.com/user-attachments/assets/39b9b006-388b-49b3-82a0-8cc9b739feaa"
/>


Or a "Row number too large" when required to create a new list element
along the way:
<img width="475" alt="Screenshot 2025-02-17 at 2 46 51 AM"
src="https://github.com/user-attachments/assets/007d1268-7d26-42aa-9bf5-d54c0abf4058"
/>


But both now succeed, which seems to be the intention as it is in parity
with record behavior. Any consumers depending on this specific behavior
will see these errors subside.

This change also includes the static method
`Value::with_data_at_cell_path` that creates a value with a given nested
value at a given cell path, creating records or lists based on the path
member type. 

# Tests + Formatting
In addition to unit tests for the altered behavior, both affected
user-facing commands (`insert` and `upsert`) gained a new command
example to both explain and test this change at the user level.
<img width="382" alt="Screenshot 2025-02-17 at 2 29 26 AM"
src="https://github.com/user-attachments/assets/e6973640-3ce6-4ea7-9ba5-d256fe5cb38b"
/>

Note: A single test did fail locally, due to my config directory
differing from expected, but works where this variable is unset
(`with-env { XDG_CONFIG_HOME: null } {cargo test}`):
```
---- repl::test_config_path::test_default_config_path stdout ----
thread 'repl::test_config_path::test_default_config_path' panicked at tests/repl/test_config_path.rs:101:5:
assertion failed: `(left == right)`

Diff < left / right > :
<[home_dir]/Library/Application Support/nushell
>[home_dir]/.config/nushell
```
2025-02-22 21:53:25 -08:00
bda3245725 More precise ErrorKind::NotFound errors (#15149)
In this PR, the two new variants for `ErrorKind`, `FileNotFound`
and `DirectoryNotFound` with a nice `not_found_as` method for the
`ErrorKind` to easily specify the `NotFound` errors. I also updated some
places where I could of think of with these new variants and the message
for `NotFound` is no longer "Entity not found" but "Not found" to be
less strange.

closes #15142
closes #15055
2025-02-22 11:42:44 -05:00
1d44843970 Remove inheritance for PROMPT variables created in default_env.nu (#15130)
This PR always sets a fresh `PROMPT_COMMAND` and `PROMPT_COMMAND_RIGHT`
during startup in `default_env.nu`. This is a more "sensible default",
and can then be overridden with user config later in the startup.
2025-02-21 10:08:10 -05:00
d16946c6e8 Transpose now rejects streams with non-record values (#15151)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

# Description

Closes #13765

Transpose now checks if the input consists entirely of records before
doing its things, which is fine since it already `.collects()` all of
its input already.

<!--
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

Adds `rejects_non_table_stream_input` test to cover regressions.
<!--
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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-02-20 23:34:26 -05:00
2f6b4c5e9b bump the rust toolchain to 1.83.0 (#15148)
# Description

This PR bumps the rust toolchain to 1.83.0 and fixes a clippy lint. We
do this because Rust 1.85.0 was released today, and we try and stay 2
versions behind.

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-02-20 16:34:09 -06:00
4a967d19a9 Remove BACKTRACE message for non-panic errors (#15143)
# Description

Resolves #15070 by removing the `BACKTRACE` message from all Nushell
(non-panic) errors. This was added in #14945 and is useful for
debugging, but not all that helpful to the typical shell user,
especially since most shell errors won't have a backtrace anyway.

At some point it would be nice to display this message only when there
*is* a backtrace available.

# User-Facing Changes

Error messages will be more concise.

# Tests + Formatting

Updated tests.

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

# After Submitting

We should include information in the *"Custom Commands"* chapter of the
documentation on how to enable this for debugging.
2025-02-20 15:59:11 +08:00
3d58c3f70e Expose flag to not maintain order on polars concat (#15145) 2025-02-19 19:50:57 -08:00
c504c93a1d Polars: Minor code cleanup (#15144)
# Description
Removing todos and deadcode from a previous refactor
2025-02-19 09:47:21 -08:00
8b46ba8b6b Feature+: Bracoxide Zero Padding for Numeric Ranges (#15125)
adds feature spécified in bracoxide#6

```
$ echo {01..10} 
01 02 03 04 05 06 07 08 09 10
$ echo {1..010} 
001 002 003 004 005 006 007 008 009 010
```

I'm going to update the examples, but I'm currently on mobile. Will land
in a couple of days.
2025-02-19 07:35:10 -06:00
f8ac9db15b update to the latest reedline (#15139)
# Description

This PR updates nushell to the latest reedline commit
[4ca1ed9](4ca1ed960f)

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-02-19 07:34:13 -06:00
7636963732 add attr category @category to custom command attributes (#15137)
# Description

This PR adds the `@category` attribute to nushell for use with custom
commands.

### Example Code
```nushell
# Some example with category
@category "math"
@search-terms "addition"
@example "add two numbers together" {
    blah 5 6
} --result 11
def blah [
  a: int # First number to add
  b: int # Second number to add
  ] {
    $a + $b
}
```
#### Source & Help
```nushell
❯ source blah.nu
❯ help blah
Some example with category

Search terms: addition

Usage:
  > blah <a> <b>

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

Parameters:
  a <int>: First number to add
  b <int>: Second number to add

Input/output types:
  ╭─#─┬─input─┬─output─╮
  │ 0 │ any   │ any    │
  ╰───┴───────┴────────╯

Examples:
  add two numbers together
  > blah 5 6
  11
```
#### Show the category
```nushell
❯ help commands | where name == blah
╭─#─┬─name─┬─category─┬─command_type─┬────────description─────────┬─────params─────┬──input_output──┬─search_terms─┬─is_const─╮
│ 0 │ blah │ math     │ custom       │ Some example with category │ [table 3 rows] │ [list 0 items] │ addition     │ false    │
╰───┴──────┴──────────┴──────────────┴────────────────────────────┴────────────────┴────────────────┴──────────────┴──────────╯
```

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

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

/cc @Bahex
2025-02-18 15:35:52 -06:00
5d1e2b1df1 Replace "function" with "command" in several user-facing doc (#15129) 2025-02-17 14:10:38 -05:00
273226d666 Provide more directories autocomplete for "overlay use" (#15057)
## Description

- Fixes #12891
- An escape for #12835

Currently, Nushell is not very friendly to Python workflow, because
Python developers very often need to activate a virtual environment, and
in some workflow, the _activate.nu_ script is not near to "current
working directory" (especially ones who use
[virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)
and [Poetry](https://python-poetry.org/)), and hence, is not
auto-completed for `overlay use`.
Though Nu v0.102.0 has improved auto-complete for `overlay use`, it
doesn't work if user starts the file path with `~` or `/`, like:

```nu
> overlay use /h
```
```nu
> overlay use ~/W
```

### Before:


![image](https://github.com/user-attachments/assets/8b668c21-0f3a-4d6f-9cd2-8cc92460525c)

### After:


![image](https://github.com/user-attachments/assets/ca491e64-774a-48d4-8f4f-58d647e011df)


![image](https://github.com/user-attachments/assets/4e097008-b5e1-4f63-af80-c1697025d4ad)



## User-Facing Changes

No

## Tests + Formatting

Passed

---------

Co-authored-by: blindfs <blindfs19@gmail.com>
2025-02-17 12:52:07 -05:00
2b8fb4fe00 Fix failing test when using man version 2.13.0 (#15123)
The test added in #15115 fails on systems using later versions of `man`
(2.13.0 triggers the issue, at least). This updates the test to ignore
formatting characters.

Thanks to @fdncred and @blindFS for the debugging assistance.
2025-02-15 18:55:33 -05:00
2cb059146b Add buffer_editor example with arguments in config nu --doc (#15122)
Counterpart to https://github.com/nushell/nushell.github.io/pull/1810 -
Adds an example to the `config nu --doc` for using a `buffer_editor`
with arguments.

Closes https://github.com/nushell/nushell.github.io/issues/1660 and
resolves the main issue in #14893, but we'll leave it open based on
[this
suggestion](https://github.com/nushell/nushell/issues/14893#issuecomment-2607670442)
2025-02-14 18:51:54 -05:00
fb7b0a8c11 feat(lsp): hover on external command shows manpage (#15115)
# Description

<img width="642" alt="image"
src="https://github.com/user-attachments/assets/a97e4f33-df12-4240-a221-d4b97a171de0"
/>

Not particularly useful, but better than showing nothing I guess. #14464

Also fixed a markdown syntax issue for mutable variable hovering

# User-Facing Changes

# Tests + Formatting

+1

# After Submitting

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2025-02-14 10:59:46 -06:00
d4aeadbb44 Enable theming the Welcome Banner (#15095)
The banner will now use three new `$env.config.color_config` settings:
- `banner_foreground`: The primary color of the banner text
- `banner_highlight1`: Used for the first set of highlights, e.g.,
`Nushell`, `nu`, `GitHub`, et. al
- `banner_highlight2`: Used for the second set of highlights, e.g.
`Discord`, `Documentation`, et. al.

If the settings above are not defined, `banner` continues to use the
default green/purple/foreground. However, two more lines use the
purple/highlight2 in order to give more separation and consistency to
the colorization.
2025-02-14 10:19:16 -05:00
2a8f92b709 docs(chunks): make chunks easier to discover for binary data (#15117)
# Description
There has been multiple instances of users being unable to discover that
`chunks` can be used with binary data.
This should make it easier for users to discover that (using `help -f`).

# User-Facing Changes
Help text of `chunks` updated as mentioned above.

# Tests + Formatting

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

# After Submitting

Should we consider mentioning commands that can work with binary input
(first, take, chunks, etc) in the help text for `bytes`?

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
2025-02-14 06:29:45 -06:00
453e294883 Refactor kv commands: replace inline params in SQL queries (#15108)
# Description

Update some comments and fix potential security issue:

SQL Injection in DELETE statements: The code constructs SQL queries by
interpolating the $key variable directly into the string. If a key
contains malicious input could lead to SQL injection. Need to use
parameterized queries or escaping.
2025-02-13 23:23:59 -05:00
e1c5ae3cd5 fix(test stdlib): scanning tests shouldn't be affected by user config (#15113)
# Description

# User-Facing Changes

# Tests + Formatting

# After Submitting

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
2025-02-13 20:23:14 +01:00
a8a0c78a32 Bump Ubuntu runners to 22.04 LTS for tests (#15109)
The release workflow is already on 22.04 for a while. But our tests
where still with 20.04 which is close to end of life

Closes #15052
2025-02-13 16:51:21 +01:00
879258039c Revert / vi binding due to priority bug (#15111)
Manually added bindings take priority to the vi-mode state machine in
reedline thus this addition blocked the use of `f/`/`t/` etc.

Partial revert of #14908

Addresses #15096 with a temporary fix. The full solution of that should
resolve it on the reedline side so you can have both the search option
and the availability of `/` in normal mode bindings
2025-02-13 16:29:08 +01:00
4ac4f71a37 feat(overlay): expose constants with overlay use (#15081)
# Description

`overlay use` now imports constants exported from modules, just like
`use`.

```nushell
# foo.nu
export const a = 1
export const b = 2
```

- `overlay use foo.nu` being equivalent to `use foo.nu *` and exposing
constants `$a = 1` and `$b = 2`
- `overlay use foo.nu -p` being equivalent to `use foo.nu` and exposing
the constant `$foo = {a: 1, b: 2}`

# User-Facing Changes

`overlay use` now imports constants just like `use`.

# Tests + Formatting

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

# After Submitting
N/A
2025-02-13 18:55:03 +08:00
62e56d3581 Rework operator type errors (#14429)
# Description

This PR adds two new `ParseError` and `ShellError` cases for type errors
relating to operators.
- `OperatorUnsupportedType` is used when a type is not supported by an
operator in any way, shape, or form. E.g., `+` does not support `bool`.
- `OperatorIncompatibleTypes` is used when a operator is used with types
it supports, but the combination of types provided cannot be used
together. E.g., `filesize + duration` is not a valid combination.

The other preexisting error cases related to operators have been removed
and replaced with the new ones above. Namely:

- `ShellError::OperatorMismatch`
- `ShellError::UnsupportedOperator`
- `ParseError::UnsupportedOperationLHS`
- `ParseError::UnsupportedOperationRHS`
- `ParseError::UnsupportedOperationTernary`

# User-Facing Changes

- `help operators` now lists the precedence of `not` as 55 instead of 0
(above the other boolean operators). Fixes #13675.
- `math median` and `math mode` now ignore NaN values so that `[NaN NaN]
| math median` and `[NaN NaN] | math mode` no longer trigger a type
error. Instead, it's now an empty input error. Fixing this in earnest
can be left for a future PR.
- Comparisons with `nan` now return false instead of causing an error.
E.g., `1 == nan` is now `false`.
- All the operator type errors have been standardized and reworked. In
particular, they can now have a help message, which is currently used
for types errors relating to `++`.

```nu
[1] ++ 2
```
```
Error: nu::parser::operator_unsupported_type

  × The '++' operator does not work on values of type 'int'.
   ╭─[entry #1:1:5]
 1 │ [1] ++ 2
   ·     ─┬ ┬
   ·      │ ╰── int
   ·      ╰── does not support 'int'
   ╰────
  help: if you meant to append a value to a list or a record to a table, use the `append` command or wrap the value in a list. For example: `$list ++ $value` should be
        `$list ++ [$value]` or `$list | append $value`.
```
2025-02-12 20:03:40 -08:00
2e1b6acc0e feat(const): implement run_const for const (#15082)
- fixes #14559

# Description
Allow using `const` keyword in const contexts. The `run_const` body is
basically empty as the actual logic is already handled by the parser.

# User-Facing Changes
`const` keyword can now be used in `const` contexts.

# Tests + Formatting

I'll add some tests before marking this ready. 

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

# After Submitting
N/A

---------

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
2025-02-12 16:59:51 +01:00
3eae657121 Update std-rfc tests for to use @test attributes (#15098)
After #14906, the test runner was updated to use attributes, along with
the existing `std` modules. However, since that PR was started before
`std-rfc` was in main, it didn't include updates to those tests. Once
#14906 was merged, the `std-rfc` tests no longer ran in CI. This PR
updates the tests accordingly.
2025-02-12 06:48:41 -05:00
e74ce72f09 build(deps): bump data-encoding from 2.7.0 to 2.8.0 (#15101)
Bumps [data-encoding](https://github.com/ia0/data-encoding) from 2.7.0
to 2.8.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="284f84626a"><code>284f846</code></a>
Release 2.8.0 (<a
href="https://redirect.github.com/ia0/data-encoding/issues/134">#134</a>)</li>
<li><a
href="b6f9f3b9d6"><code>b6f9f3b</code></a>
Remove MSRV for unpublished crates (<a
href="https://redirect.github.com/ia0/data-encoding/issues/133">#133</a>)</li>
<li><a
href="c060e6873c"><code>c060e68</code></a>
Delete outdated cargo cache to force save (<a
href="https://redirect.github.com/ia0/data-encoding/issues/132">#132</a>)</li>
<li><a
href="d62d722222"><code>d62d722</code></a>
Remove top-level Makefile (<a
href="https://redirect.github.com/ia0/data-encoding/issues/131">#131</a>)</li>
<li><a
href="5e86676a34"><code>5e86676</code></a>
Improve CI workflow (<a
href="https://redirect.github.com/ia0/data-encoding/issues/130">#130</a>)</li>
<li><a
href="8a9537cf64"><code>8a9537c</code></a>
Improve fuzzing (<a
href="https://redirect.github.com/ia0/data-encoding/issues/129">#129</a>)</li>
<li><a
href="27a68f43cd"><code>27a68f4</code></a>
Add missing safety documentation and assertions for testing and fuzzing
(<a
href="https://redirect.github.com/ia0/data-encoding/issues/128">#128</a>)</li>
<li><a
href="06b0d89b11"><code>06b0d89</code></a>
Add BASE32_NOPAD_NOCASE and BASE32_NOPAD_VISUAL (<a
href="https://redirect.github.com/ia0/data-encoding/issues/127">#127</a>)</li>
<li>See full diff in <a
href="https://github.com/ia0/data-encoding/compare/v2.7.0...v2.8.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=data-encoding&package-manager=cargo&previous-version=2.7.0&new-version=2.8.0)](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 show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@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>
2025-02-12 12:45:27 +08:00
d577074da9 feat(std/dirs): retain state in subshells or with exec-restarts (#15080)
Persist `std/dirs`'s state in subshells and also when restarting the
shell with `exec $nu.current-exe`
2025-02-11 19:51:43 -05:00
f7d5162582 docs(std-rfc): use actual examples rather than doc comments (#15097)
# Description
Update examples with attributes.

# User-Facing Changes
`std-rfc` commands now have examples.

# Tests + Formatting
N/A

# After Submitting
N/A
2025-02-11 16:33:27 -06:00
0430167f1c Use proc-macro-error2 instead of proc-macro-error (#15093)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

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

This PR replaces the usage of `proc-macro-error` with
`proc-macro-error2`. At the time of writing `nu-derive-value` this
wasn't an option, at least it wasn't clear that it is the direction to
go. This shouldn't change any of the usage of `nu-derive-value` in any
way but removes one security warning.

`proc-macro-error` depends on `syn 1`, that's why I initially had the
default features for `proc-macro-error` disabled. `proc-macro-error2`
uses `syn 2` as mostly everything. So we can use that.

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

Same interface, no 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

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

The tests for `nu-derive-value` do not test spans, so maybe something
changed now but probably not.

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

We still have `quickcheck` which depends on `syn 1` but it seems we need
that for `nu-cmd-lang`. Would be great if, in the future, we can get rid
of `syn 1` as that should improve build times a bit.
2025-02-11 15:13:34 -05:00
1128fa137f Fix spread operator lexing in records (#15023)
# Description

Zyphys found that when parsing `{...{}, ...{}, a: 1}`, the `a:` would be
considered one token, leading to a parse error ([Discord
message](https://discord.com/channels/601130461678272522/614593951969574961/1336762075535511573)).
This PR fixes that.

What would happen is that while getting tokens, the following would
happen in a loop:
1. Get the next two tokens while treating `:` as a special character (so
we get the next field key and a colon token)
2. Get the next token while not treating `:` as a special character (so
we get the next value)

I didn't update this when I added the spread operator. With `{...{},
...{}, a: 1}`, the first two tokens would be `...{}` and `...{}`, and
the next token would be `a:`. This PR changes this loop to first get a
single token, check if it's spreading a record, and move on if so.

Alternatives considered:
- Treat `:` as a special character when getting the value too. This
would simplify the loop greatly, but would mean you can't use colons in
values.
- Merge the loop for getting tokens and the loop for parsing those
tokens. I tried this, but it complicates things if you run into a syntax
error and want to create a garbage span going to the end of the record.

# User-Facing Changes

Nothing new
2025-02-11 09:51:34 -05:00
81243c48f0 make plugin compatible with nightly nushell version (#15084)
# Description
Close: #15083

This pr will set `pre` field of version to `Prerelease::EMPTY`, as
nushell does not require this level of checking currently.

# User-Facing Changes
NaN

# Tests + Formatting
Added 1 test

# After Submitting
NaN
2025-02-11 06:40:15 -06:00
442df9e39c Custom command attributes (#14906)
# Description
Add custom command attributes.

- Attributes are placed before a command definition and start with a `@`
character.
- Attribute invocations consist of const command call. The command's
name must start with "attr ", but this prefix is not used in the
invocation.
- A command named `attr example` is invoked as an attribute as
`@example`
-   Several built-in attribute commands are provided as part of this PR
    -   `attr example`: Attaches an example to the commands help text
        ```nushell
        # Double numbers
        @example "double an int"  { 5 | double }   --result 10
        @example "double a float" { 0.5 | double } --result 1.0
        def double []: [number -> number] {
            $in * 2
        }
        ```
    -   `attr search-terms`: Adds search terms to a command
    -   ~`attr env`: Equivalent to using `def --env`~
- ~`attr wrapped`: Equivalent to using `def --wrapped`~ shelved for
later discussion
    -   several testing related attributes in `std/testing`
- If an attribute has no internal/special purpose, it's stored as
command metadata that can be obtained with `scope commands`.
- This allows having attributes like `@test` which can be used by test
runners.
-   Used the `@example` attribute for `std` examples.
-   Updated the std tests and test runner to use `@test` attributes
-   Added completions for attributes

# User-Facing Changes
Users can add examples to their own command definitions, and add other
arbitrary attributes.

# Tests + Formatting

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

# After Submitting
- Add documentation about the attribute syntax and built-in attributes
- `help attributes`

---------

Co-authored-by: 132ikl <132@ikl.sh>
2025-02-11 06:34:51 -06:00
a58d9b0b3a Refactor/fix tests affecting the whole command set (#15073)
# Description
Pre-cratification of `nu-command` we added tests that covered the whole
command set to ensure consistent documentation style choices and that
the search terms which are added are not uselessly redundant. These
tests are now moved into the suite of the main binary to truly cover all
commands.

- **Move parser quickcheck "fuzz" to `nu-cmd-lang`**
- **Factor out creation of full engine state for tests**
- **Move all-command tests to main context creation**
- **Fix all descriptions**
- **Fix search term duplicate**

# User-Facing Changes
As a result I had to fix a few command argument descriptions. (Doesn't
mean I fully stand behind this choice, but) positionals
(rest/required/optional) and top level descriptions should start with a
capital letter and end with a period. This is not enforced for flags.

# Tests + Formatting
Furthermore I moved our poor-peoples-fuzzer that runs in CI with
`quicktest` over the parser to `nu-cmd-lang` reducing its command set to
just the keywords (similar to
https://github.com/nushell/nushell/pull/15036). Thus this should also
run slightly faster (maybe a slight parallel build cost due to earlier
dependency on quicktest)
2025-02-11 11:36:36 +01:00
2a3d5a9d42 Bump bytesize to fix into filesize (#15088)
# Description
Closes https://github.com/nushell/nushell/issues/14866

Incorporates https://github.com/bytesize-rs/bytesize/pull/59 with
bytesize version 1.3.1

# User-Facing Changes
Now rejected strings
```
"1.3 1.3 kB" | into filesize
"1 420 kB" | into filesize
```
# Tests + Formatting
Added test with invalid input that was silently ignored before
2025-02-11 11:33:48 +01:00
a5d7d6dd46 Bump yanked dependencies (#15090)
Seen in the `cargo install --locked` step of the stdlib ci
- `scc`
- `sdd` [according to changelog potential use after
free](https://github.com/wvwwvwwv/scalable-delayed-dealloc/blob/main/CHANGELOG.md)

Should only be part of the `dev-dependencies` due to `serial_test`
2025-02-11 11:33:29 +01:00
18e3a5d40b Fix match blocks in std-rfc/kv implementation (#15089)
Fixes failure surfaced by merging
https://github.com/nushell/nushell/pull/15032
2025-02-11 11:16:57 +01:00
553c951a60 Fix match running closures as block (#15032)
# Description
This PR makes `match` no longer run closures as if they were blocks.
This also allows returning closures from `match` without needing to wrap
in an outer subexpression or block.

Before PR:
```nushell
match 1 { _ => {|| print hi} }
# => hi
```

After PR:
```nushell
match 1 { _ => {|| print hi} }
# => closure_1090
```

# User-Facing Changes

* `match` no longer runs closures as if they were blocks

# Tests + Formatting

N/A

# After Submitting

N/A
2025-02-11 10:35:23 +01:00
781c4bd1d7 use 0-indexing in explore (#15079)
# Description
The index in `explore --index` starting with 1 is inconsistent with rest
of nushell. Also it tripped me up a few times when I wanted to select a
row with `:nu get n`

# User-Facing Changes
Index in `explore --index` now starts with 0.

# Tests + Formatting

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

# After Submitting
N/A
2025-02-10 15:26:42 -06:00
a2e335dcd7 fix block spans for the module keyword (#15078)
# Description
I noticed that the following code snippet wasn't being highlighted
correctly. Spans used for parsing contents of the block was also
incorrectly used for the expression.

# User-Facing Changes
The block following the module keyword is now highlighted correctly.

| Before | After |
|--------|--------|
|
![image](https://github.com/user-attachments/assets/8d1040f5-5002-4880-bb71-47549a67a804)
|
![image](https://github.com/user-attachments/assets/0dc28e25-2da3-4411-82ae-3e4e129fd42f)
|

# Tests + Formatting

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

# After Submitting
N/A
2025-02-10 15:26:02 -06:00
c6fc6bd5a7 fix(lsp): inlay hints span issue with user config scripts (#15071)
# Description

Fixes this:

![image](https://github.com/user-attachments/assets/98b523dd-df30-4e85-b069-20aaad0d9bf5)

# User-Facing Changes

# Tests + Formatting

I can't figure out how to test this atm.
Happy to do it if someone show me some hints how.

# After Submitting
2025-02-10 16:15:03 +01:00
a7830ac1fd Update README.md
Fix link
2025-02-10 09:06:09 -05:00
00713c9339 Update README.md
Fix Drawing-Board link/description
2025-02-10 09:05:05 -05:00
7d7dbd8b2c Fix missing required overlay error (#15058)
# Description
Fixes: #15049

The error occurs when using an alias with a module prefix, it can
initially pass through alias checking, but if the alias leads to
commands which have side effects, it doesn't call these functions to
apply side effects.
This pr ensure that in such cases, nushell still calls
`parse_overlay_xxx` functions to apply the side effects.

I want to make my test easier to write, so this pr depends on
https://github.com/nushell/nushell/pull/15054.

# User-Facing Changes
The following code will no longer raise an error:
```
module inner {}
module spam { export alias b = overlay use inner }
use spam
spam b
```

# Tests + Formatting
Added 2 tests.

# After Submitting
NaN
2025-02-10 16:27:50 +08:00
d4675d9138 allow export alias in repl (#15054)
# Description
Fixes: #15048
The issue is happened while `parse_export_in_block`, it makes a call to
`parse_internal_call`, which may be an error.
But in reality, these errors are not useful, all useful errors will be
generated by `parse_xxx` at the end of the function.

# User-Facing Changes
The following code should no longer raise error:
```
export alias a = overlay use
```

# Tests + Formatting
Added 1 test.

# After Submitting
NaN
2025-02-10 15:32:05 +08:00
6e88b3f8d6 refactor(completion): expression based variable/cell_path completion (#15033)
# Description

fixes #14643 , as well as some nested cell path cases:

```nushell
let foo = {a: [1 {a: 1}]}

$foo.a.1.#<tab>

const bar = {a: 1, b: 2}
$bar.#<tab>
```

So my plan of the refactoring process is that:
1. gradually move those rules of flattened shapes into expression match
branches, until they are gone
2. keep each PR focused, easier to review and track. 

# User-Facing Changes

# Tests + Formatting

+2

# After Submitting
2025-02-09 22:26:41 -05:00
720813339f Add std-rfc README (#15066)
Copied the old README from `nu_scripts/stdlib-candidate/std-rfc` over to `nu-std/std-rfc` and
updated it with the latest info.
2025-02-09 11:21:56 -05:00
5b4dd775d4 Move std-rfc into Nushell (#15042)
Move `std-rfc` into Nushell.  `use std-rfc/<submodule>` now works "out-of-the-box"
2025-02-09 09:03:37 -05:00
bfe398ca36 Fix char lsep assignment (#15065)
Fix `char eol` issue where there was still a hardcoded `\n` taking
effect on Windows.
2025-02-09 07:19:11 -05:00
31e1f49cb6 fix ranges over zero-length input (#15062)
Fixes #15061

# User-Facing Changes

Fixes panics when slicing empty input with inclusive ranges:

```nushell
> random binary 0 | bytes at 0..0
Error:   x Main thread panicked.
  |-> at crates/nu-protocol/src/value/range.rs:118:42
  `-> attempt to subtract with overflow
```
2025-02-08 19:57:28 -05:00
26897b287c Adds platform agnostic EoL separator to char command (#15059)
Adds `char eol`, `char line_sep`, and `char lsep` (synonyms) to represent
the platform specific line ending character(s).
2025-02-08 16:23:51 -05:00
5a7707cb52 Remove --no-default-features for std-lib-and-python-virtualenv CI (#15045)
# Description

Current CI tests `std-lib-and-python-virtualenv` using Nushell installed
with:

```
cargo install --path . --locked --no-default-features --force
```

However, this disables certain features that may be utilized in `std` or
(now) `std-rfc`; namely `stor` and `into sqlite`.

This PR simply removes the `--no-default-features` flag, which *should*
allow #15042 CI to complete successfully.

Historically, I believe that this was set up to mirror
[`pypa/virtualenv`
CI](https://github.com/pypa/virtualenv/pulls?q=is%3Apr+is%3Amerged+nushell).
However, with all Nushell binary builds now including these features, it
seems to me that a more accurate CI will test with default features. Let
me know if my understanding is off here, and we can look for
alternatives.

# User-Facing Changes

None

# Tests + Formatting

CI Update

# After Submitting

N/A
2025-02-08 21:02:15 +02:00
4b0b4ddce1 Replaced IoError::new_with_additional_context calls that still had Span::unknown() (#15056)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

# 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.
-->
In #14968 I grepped the code for `IoError::new` calls with unknown
spans, but I forgot to also grep for
`IoError::new_with_additional_context`, so I missed some. Hopefullly
this is the last P.S. to #14968.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

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

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

N/A
2025-02-08 09:23:28 -06:00
9fa2f43d06 fix(lsp): exit on null root_dir (#15051)
# Description

This PR fixes one reported bug of recent lsp changes.

It exit unexpectedly with empty `root_dir` settings in neovim.

# User-Facing Changes

# Tests + Formatting

+1 test case

# After Submitting
2025-02-08 06:49:38 -06:00
2891867de9 Trigger tests for patch release branch pushes (#15037)
Make sure that when creating a cherry-picked or otherwise diverging
patch release branch the final product still gets checked via CI before
a release is cut.

To trigger this patch release branches MUST follow the pattern:
`patch-release-*` (e.g. `patch-release-0.102.1`)
2025-02-07 23:51:01 +01:00
55c7246830 Fuzz more realistically with keyword const eval (#15036)
# Description
The parsing logic for several of our keywords is conditional on the
particular commands for those keywords being in scope:


942030199d/crates/nu-parser/src/parse_keywords.rs (L272-L279)

Thus the following involved parsing logic was not fuzzed by the existing
`parse` fuzz target so far.

This adds an additional fuzz target `parse_with_keywords` that loads the
commands from `nu-cmd-lang`. Those are primarily the keyword
implementations, thus the relevant code paths in the parser that depend
on those `DeclId`s and the potential const eval of `if` etc. get
unlocked.

The existing `parse` target is preserved if you have concerns about the
fuzzing breaking containment in some form due to those commands.

# Tests + Formatting
Found https://github.com/nushell/nushell/issues/14972 with this target
2025-02-07 23:50:47 +01:00
17246db38b Fix usages of fmt to format number (#15041)
Those slipped through the cracks with
https://github.com/nushell/nushell/pull/14875

Avoids deprecation warning and failure after
https://github.com/nushell/nushell/pull/15040
2025-02-07 23:50:33 +01:00
e60dac8957 remove nu-check examples with the --all flag (#15047)
# Description

Deletes example usage of `nu-check`'s `--all` flag, which was removed in
5e937ca1af.
2025-02-07 14:31:59 -06:00
d007b10fbf Use build_target information in startup banner (#15046)
# Description
The `(version).build_os` variable inherits from `shadow_rs` `BUILD_OS`
which points to the OS on which the binary was built but does not
reflect the target if it was cross-compiled. We cross-compile several of
the targets for our binary releases. Thus the info in the banner was
misleading.

# User-Facing Changes
By changing to `build_target` the target triple is shown instead.
This is slightly more verbose but should also allow disambiguation
between the `musl` and `glibc` builds.


![grafik](https://github.com/user-attachments/assets/24dd43d7-9717-463b-809b-b81b44f9ab52)

# Tests + Formatting
(-)
2025-02-07 12:10:13 -06:00
942030199d check signals while printing values (#14980)
Fixes #14960

# User-Facing Changes

- The output of non-streaming values can now be interrupted with ctrl-c:

```nushell
~> use std repeat; random chars --length 100kb | repeat 2000 | str join ' ' | collect
<data omitted>^C
Error:
  × Operation interrupted
   ╭─[entry #1:1:61]
 1 │ use std repeat; random chars --length 100kb | repeat 2000 | str join ' ' | collect
   ·                                                             ────┬───
   ·                                                                 ╰── This operation was interrupted
   ╰────
```

- When IO errors occur while printing data, nushell no longer panics:

```diff
 $ nu -c "true | print" | -

-Error:
-  x Main thread panicked.
-  |-> at crates/nu-protocol/src/errors/shell_error/io.rs:198:13
-  `-> for unknown spans with paths, use `new_internal_with_path`
+Error: nu:🐚:io::broken_pipe
+
+  x I/O error
+  `->   x Broken pipe
+
+   ,-[source:1:1]
+ 1 | true | print
+   : ^^|^
+   :   `-| Writing to stdout failed
+   :     | Broken pipe
+   `----
```
2025-02-07 06:56:07 -05:00
fb8ac4198b fix: clippy warnings with --all-features (#15035)
# Description

Some more `cargo clippy --all-features` warnings from rust toolchain
1.84.1 that I forgot to fix in #14984
2025-02-07 12:30:25 +01:00
2ce5de58e6 Fix an integer overflow bug in into duration (#15031)
Fixes #15028

# Description

The current implementation of `into duration` uses bare pointer
arithmetic instead of wrapping one. This works fine on 64-bit platforms,
since the pointers don't take up all of the 64 bits, but fails on 32 bit
ones.


# Tests + Formatting

All of the affected tests pass on my end, but it's `x86_84`, so they
were also passing before that.
2025-02-06 21:32:42 +01:00
2f18b9c856 Enable nushell error with backtrace (#14945)
# Description
After this pr, nushell is able to raise errors with a backtrace, which
should make users easier to debug. To enable the feature, users need to
set env variable via `$env.NU_BACKTRACE = 1`. But yeah it might not work
perfectly, there are some corner cases which might not be handled.

I think it should close #13379 in another way.

### About the change

The implementation mostly contained with 2 parts:
1. introduce a new `ChainedError` struct as well as a new
`ShellError::ChainedError` variant. If `eval_instruction` returned an
error, it converts the error to `ShellError::ChainedError`.
`ChainedError` struct is responsable to display errors properly. It
needs to handle the following 2 cases:
- if we run a function which runs `error make` internally, it needs to
display the error itself along with caller span.
- if we run a `error make` directly, or some commands directly returns
an error, we just want nushell raise an error about `error make`.

2. Attach caller spans to `ListStream` and `ByteStream`, because they
are lazy streams, and *only* contains the span that runs it
directly(like `^false`, for example), so nushell needs to add all caller
spans to the stream.
For example: in `def a [] { ^false }; def b [] { a; 33 }; b`, when we
run `b`, which runs `a`, which runs `^false`, the `ByteStream` only
contains the span of `^false`, we need to make it contains the span of
`a`, so nushell is able to get all spans if something bad happened.
This behavior is happened after running `Instruction::Call`, if it
returns a `ByteStream` and `ListStream`, it will call `push_caller_span`
method to attach call spans.

# User-Facing Changes
It's better to demostrate how it works by examples, given the following
definition:
```nushell
> $env.NU_BACKTRACE = 1
> def a [x] { if $x == 3 { error make {msg: 'a custom error'}}}
> def a_2 [x] { if $x == 3 { ^false } else { $x } }
> def a_3 [x] { if $x == 3 { [1 2 3] | each {error make {msg: 'a custom error inside list stream'} } } }
> def b [--list-stream --external] {
    if $external == true {
        # error with non-zero exit code, which is generated from external command.
        a_2 1; a_2 3; a_2 2
    } else if $list_stream == true {
        # error generated by list-stream
        a_3 1; a_3 3; a_3 2
    } else {
        # error generated by command directly
        a 1; a 2; a 3
    }
}
```

Run `b` directly shows the following error:

<details>

```nushell
Error: chained_error

  × oops
   ╭─[entry #27:1:1]
 1 │ b
   · ┬
   · ╰── error happened when running this
   ╰────

Error: chained_error

  × oops
    ╭─[entry #26:10:19]
  9 │         # error generated by command directly
 10 │         a 1; a 2; a 3
    ·                   ┬
    ·                   ╰── error happened when running this
 11 │     }
    ╰────

Error:
  × a custom error
   ╭─[entry #6:1:26]
 1 │ def a [x] { if $x == 3 { error make {msg: 'a custom error'}}}
   ·                          ─────┬────
   ·                               ╰── originates from here
   ╰────
```

</details>

Run `b --list-stream` shows the following error

<details>

```nushell
Error: chained_error

  × oops
   ╭─[entry #28:1:1]
 1 │ b --list-stream
   · ┬
   · ╰── error happened when running this
   ╰────

Error: nu:🐚:eval_block_with_input

  × Eval block failed with pipeline input
   ╭─[entry #26:7:16]
 6 │         # error generated by list-stream
 7 │         a_3 1; a_3 3; a_3 2
   ·                ─┬─
   ·                 ╰── source value
 8 │     } else {
   ╰────

Error: nu:🐚:eval_block_with_input

  × Eval block failed with pipeline input
   ╭─[entry #23:1:29]
 1 │ def a_3 [x] { if $x == 3 { [1 2 3] | each {error make {msg: 'a custom error inside list stream'} } } }
   ·                             ┬
   ·                             ╰── source value
   ╰────

Error:
  × a custom error inside list stream
   ╭─[entry #23:1:44]
 1 │ def a_3 [x] { if $x == 3 { [1 2 3] | each {error make {msg: 'a custom error inside list stream'} } } }
   ·                                            ─────┬────
   ·                                                 ╰── originates from here
   ╰────
```

</details>

Run `b --external` shows the following error:

<details>

```nushell
Error: chained_error

  × oops
   ╭─[entry #29:1:1]
 1 │ b --external
   · ┬
   · ╰── error happened when running this
   ╰────

Error: nu:🐚:eval_block_with_input

  × Eval block failed with pipeline input
   ╭─[entry #26:4:16]
 3 │         # error with non-zero exit code, which is generated from external command.
 4 │         a_2 1; a_2 3; a_2 2
   ·                ─┬─
   ·                 ╰── source value
 5 │     } else if $list_stream == true {
   ╰────

Error: nu:🐚:non_zero_exit_code

  × External command had a non-zero exit code
   ╭─[entry #7:1:29]
 1 │ def a_2 [x] { if $x == 3 { ^false } else { $x } }
   ·                             ──┬──
   ·                               ╰── exited with code 1
   ╰────
```

</details>

It also added a message to guide the usage of NU_BACKTRACE, see the last
line in the following example:
```shell
 ls asdfasd
Error: nu:🐚:io::not_found

  × I/O error
  ╰─▶   × Entity not found

   ╭─[entry #17:1:4]
 1 │ ls asdfasd
   ·    ───┬───
   ·       ╰── Entity not found
   ╰────
  help: The error occurred at '/home/windsoilder/projects/nushell/asdfasd'

set the `NU_BACKTRACE=1` environment variable to display a backtrace.
```
# Tests + Formatting
Added some tests for the behavior.

# After Submitting
2025-02-06 22:05:58 +08:00
bdc767bf23 fix polars save example typo (#15008)
# Description
 fix polars save example dfr -> polars 

I'm wondering why the commands `polars open` and `polars save` don't
have the same flags?
2025-02-06 07:01:09 -06:00
3770a5eed1 remove duplicate code in math/log.rs (#15022)
# Description
I have investigated all const commands and found that math log contains
some duplicate code, which can be eliminated by introducing a new helper
function. So this pr is going to do this


# User-Facing Changes
NaN

# Tests + Formatting
NaN

# After Submitting
NaN
2025-02-06 07:00:25 -06:00
0705fb9cd1 Added S3 support for polars save (#15005)
# Description
Parquet, CSV, NDJSON, and Arrow files can be written to AWS S3 via
`polars save`. This mirrors the s3 functionality provided by `polars
open`.

```nushell
ls | polars into-df | polars save s3://my-bucket/test.parquet
```

# User-Facing Changes
- S3 urls are now supported by `polars save`
2025-02-06 06:59:39 -06:00
1a1a960836 feat(explore): Allow expanding selected cell with 'e' (#15000)
Closes #14993
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

# 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
New keybinding has been added to `explore`
<!-- 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-02-06 06:56:53 -06:00
5be818b5ee make echo const (#14997)
# Description

Make `echo` const.
- It's a very simple command, there is no reason for it to not be const.
- It's return type `any` is utilized in tests to type erase values, this
might be useful for testing const evaluation too.
- The upcoming custom command attribute feature can make use of it as a
stopgap replacement for `const def` commands.

# User-Facing Changes

`echo` can be used in const contexts.

# Tests + Formatting

# After Submitting
N/A
2025-02-06 06:56:30 -06:00
c7d3014849 feat(cli): add vi solidus / keybinding (#14908)
# Description

- Add keybinding for `/` when in vi normal mode which activates the
history menu.
- Make keybinding `mode` (`edit_mode`) case-insensitive.

This keybinding exists both in vim and GNU Readline (e.g. bash) when in
vi normal mode. The reason this keybinding is getting added here (and
not in `reedline`) is because it triggers the history menu, and should
only be defined when the history menu exists. Menus are defined
externally to `reedline`.

# User-Facing Changes

Added keybinding for `/` when in vi normal mode which activates the
history menu.

# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting

TODO: Update docs
2025-02-06 06:53:32 -06:00
164a089656 refactor(completion): AST traverse to find the inner-most expression to complete (#14973)
# Description

As discussed
[here](https://github.com/nushell/nushell/pull/14856#issuecomment-2623393017)
and [here](https://github.com/nushell/nushell/discussions/14868).

I feel this method is generally better. As for the new-parser, we can
simply modify the implementation in `traverse.rs` to accommodate.

Next, I'm gonna overhaul the `Completer` trait, so before it gets really
messy, I' think this is the step to put this open for review so we can
check if I'm on track.

This PR closes #13897 (the `|` part)

# User-Facing Changes

# After Submitting
2025-02-06 06:49:13 -06:00
0b2d1327d2 fix extern commands' extra description (#14996)
# Description

- Remove redundant fields from KnownExternal
- Command::extra_description and Command::search_terms using the
signature field

# User-Facing Changes

`extern` commands extra description is now shown in help text.

# Tests + Formatting

# After Submitting
2025-02-06 12:56:40 +01:00
5f6f18076c Remove Twitter from README (#15026)
# Description
Removes Twitter mentions from the README
2025-02-06 19:49:57 +08:00
81de8ecd70 build(deps): bump crate-ci/typos from 1.29.4 to 1.29.5 (#15006) 2025-02-06 11:01:07 +00:00
30ed63667b build(deps): bump bytes from 1.9.0 to 1.10.0 (#15010) 2025-02-06 11:00:19 +00:00
a56906ca6d update miette to 7.5 (#15014) 2025-02-06 11:59:19 +01:00
0f0e1e2068 Add search terms for hide and hide-env (#15017)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

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

Adds search terms for hide and hide-env.

Rel: #15013 

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

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

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
N/A
2025-02-05 23:33:49 -05:00
192ee59c75 Fix tests of docker image and Update Nu LICENSE (#15015)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

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

- Fix docker image tests failure for [nightly
build](https://github.com/nushell/nightly/actions/runs/13150680863) and
[release
build](https://github.com/nushell/nightly/actions/runs/13156381344). I
have test them locally to make sure it works
- Update Nushell LICENSE by the way
2025-02-05 06:27:17 -06:00
803a348f41 Bump to 0.102.1 dev version (#15012) 2025-02-05 00:19:48 -05:00
547 changed files with 16986 additions and 8181 deletions

52
.github/workflows/beta-test.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Test on Beta Toolchain
# This workflow is made to run our tests on the beta toolchain to validate that
# the beta toolchain works.
# We do not intend to test here that we are working correctly but rather that
# the beta toolchain works correctly.
# The ci.yml handles our actual testing with our guarantees.
on:
schedule:
# If this workflow fails, GitHub notifications will go to the last person
# who edited this line.
# See: https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/notifications-for-workflow-runs
- cron: '0 0 * * *' # Runs daily at midnight UTC
env:
NUSHELL_CARGO_PROFILE: ci
NU_LOG_LEVEL: DEBUG
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
build-and-test:
# this job is more for testing the beta toolchain and not our tests, so if
# this fails but the tests of the regular ci pass, then this is fine
continue-on-error: true
strategy:
fail-fast: true
matrix:
platform: [windows-latest, macos-latest, ubuntu-22.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- run: rustup update beta
- name: Tests
run: cargo +beta test --workspace --profile ci --exclude nu_plugin_*
- name: Check for clean repo
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi

View File

@ -3,6 +3,7 @@ on:
push: push:
branches: branches:
- main - main
- 'patch-release-*'
name: continuous-integration name: continuous-integration
@ -21,14 +22,14 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
# Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu # Pinning to Ubuntu 22.04 because building on newer Ubuntu versions causes linux-gnu
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider # 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) # revisiting this when 22.04 is closer to EOL (June 2027)
# #
# Using macOS 13 runner because 14 is based on the M1 and has half as much RAM (7 GB, # Using macOS 13 runner because 14 is based on the M1 and has half as much RAM (7 GB,
# instead of 14 GB) which is too little for us right now. Revisit when `dfr` commands are # instead of 14 GB) which is too little for us right now. Revisit when `dfr` commands are
# removed and we're only building the `polars` plugin instead # removed and we're only building the `polars` plugin instead
platform: [windows-latest, macos-13, ubuntu-20.04] platform: [windows-latest, macos-13, ubuntu-22.04]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
@ -36,7 +37,7 @@ jobs:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1 uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
- name: cargo fmt - name: cargo fmt
run: cargo fmt --all -- --check run: cargo fmt --all -- --check
@ -56,7 +57,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [windows-latest, macos-latest, ubuntu-20.04] platform: [windows-latest, macos-latest, ubuntu-22.04]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
@ -64,7 +65,7 @@ jobs:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1 uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
- name: Tests - name: Tests
run: cargo test --workspace --profile ci --exclude nu_plugin_* run: cargo test --workspace --profile ci --exclude nu_plugin_*
@ -83,7 +84,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [ubuntu-20.04, macos-latest, windows-latest] platform: [ubuntu-22.04, macos-latest, windows-latest]
py: py:
- py - py
@ -93,10 +94,10 @@ jobs:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1 uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
- name: Install Nushell - name: Install Nushell
run: cargo install --path . --locked --no-default-features --force run: cargo install --path . --locked --force
- name: Standard library tests - name: Standard library tests
run: nu -c 'use crates/nu-std/testing.nu; testing run-tests --path crates/nu-std' run: nu -c 'use crates/nu-std/testing.nu; testing run-tests --path crates/nu-std'
@ -136,7 +137,7 @@ jobs:
# instead of 14 GB) which is too little for us right now. # instead of 14 GB) which is too little for us right now.
# #
# Failure occurring with clippy for rust 1.77.2 # Failure occurring with clippy for rust 1.77.2
platform: [windows-latest, macos-13, ubuntu-20.04] platform: [windows-latest, macos-13, ubuntu-22.04]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
@ -144,7 +145,7 @@ jobs:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1 uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
- name: Clippy - name: Clippy
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
@ -185,7 +186,7 @@ jobs:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1 uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
- name: Add wasm32-unknown-unknown target - name: Add wasm32-unknown-unknown target
run: rustup target add wasm32-unknown-unknown run: rustup target add wasm32-unknown-unknown

View File

@ -131,7 +131,7 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1 uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135` # WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with: with:
rustflags: '' rustflags: ''

View File

@ -80,7 +80,7 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain - name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1 uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135` # WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with: with:
cache: false cache: false

View File

@ -10,4 +10,4 @@ jobs:
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Check spelling - name: Check spelling
uses: crate-ci/typos@v1.29.4 uses: crate-ci/typos@v1.29.10

369
Cargo.lock generated
View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "addr2line" name = "addr2line"
@ -293,6 +293,15 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4790f9e8961209112beb783d85449b508673cf4a6a419c8449b210743ac4dbe9" checksum = "4790f9e8961209112beb783d85449b508673cf4a6a419c8449b210743ac4dbe9"
[[package]]
name = "atomic"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -752,9 +761,9 @@ dependencies = [
[[package]] [[package]]
name = "bracoxide" name = "bracoxide"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc0bcb5424e8e1f29c21a00f2d222df5e8e9779732ff03f840315d8fbac708e" checksum = "7f52991c481aa9d7518254cfb6ce5726d24ff8c5d383d6422cd3793729b0962a"
[[package]] [[package]]
name = "brotli" name = "brotli"
@ -828,9 +837,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.9.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -847,9 +856,9 @@ dependencies = [
[[package]] [[package]]
name = "bytesize" name = "bytesize"
version = "1.3.0" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" checksum = "2d2c12f985c78475a6b8d629afd0c360260ef34cfef52efccdcfd31972f81c2e"
[[package]] [[package]]
name = "calamine" name = "calamine"
@ -884,9 +893,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.3" version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@ -1088,25 +1097,12 @@ version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9"
dependencies = [ dependencies = [
"crossterm 0.28.1", "crossterm",
"strum", "strum",
"strum_macros", "strum_macros",
"unicode-width 0.2.0", "unicode-width 0.2.0",
] ]
[[package]]
name = "compact_str"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"ryu",
"static_assertions",
]
[[package]] [[package]]
name = "compact_str" name = "compact_str"
version = "0.8.0" version = "0.8.0"
@ -1279,25 +1275,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.20" version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crossterm"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
"bitflags 2.6.0",
"crossterm_winapi",
"libc",
"mio 0.8.11",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]] [[package]]
name = "crossterm" name = "crossterm"
@ -1426,10 +1406,45 @@ dependencies = [
] ]
[[package]] [[package]]
name = "data-encoding" name = "darling"
version = "2.7.0" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.90",
]
[[package]]
name = "darling_macro"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn 2.0.90",
]
[[package]]
name = "data-encoding"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
[[package]] [[package]]
name = "deranged" name = "deranged"
@ -2639,6 +2654,12 @@ dependencies = [
"syn 2.0.90", "syn 2.0.90",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "1.0.3" version = "1.0.3"
@ -2684,6 +2705,12 @@ dependencies = [
"web-time", "web-time",
] ]
[[package]]
name = "indoc"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]] [[package]]
name = "inotify" name = "inotify"
version = "0.9.6" version = "0.9.6"
@ -2704,6 +2731,19 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "instability"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d"
dependencies = [
"darling",
"indoc",
"proc-macro2",
"quote",
"syn 2.0.90",
]
[[package]] [[package]]
name = "interprocess" name = "interprocess"
version = "2.2.2" version = "2.2.2"
@ -2784,15 +2824,6 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.13.0" version = "0.13.0"
@ -2920,16 +2951,6 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "libmimalloc-sys"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "libproc" name = "libproc"
version = "0.14.10" version = "0.14.10"
@ -3074,9 +3095,9 @@ dependencies = [
[[package]] [[package]]
name = "lsp-textdocument" name = "lsp-textdocument"
version = "0.4.1" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a17dcde15cae78fb2e54166da22cd6c53f48033a0391cc392b22f2437805792" checksum = "2d564d595f4e3dcd3c071bf472dbd2cac53bc3665ae7222d2abfecd18feaed2c"
dependencies = [ dependencies = [
"lsp-types", "lsp-types",
"serde_json", "serde_json",
@ -3196,9 +3217,9 @@ dependencies = [
[[package]] [[package]]
name = "miette" name = "miette"
version = "7.4.0" version = "7.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317f146e2eb7021892722af37cf1b971f0a70c8406f487e24952667616192c64" checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"backtrace-ext", "backtrace-ext",
@ -3216,24 +3237,15 @@ dependencies = [
[[package]] [[package]]
name = "miette-derive" name = "miette-derive"
version = "7.4.0" version = "7.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.90", "syn 2.0.90",
] ]
[[package]]
name = "mimalloc"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
dependencies = [
"libmimalloc-sys",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@ -3435,16 +3447,15 @@ dependencies = [
[[package]] [[package]]
name = "nu" name = "nu"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"assert_cmd", "assert_cmd",
"crossterm 0.28.1", "crossterm",
"ctrlc", "ctrlc",
"dirs", "dirs",
"fancy-regex", "fancy-regex",
"log", "log",
"miette", "miette",
"mimalloc",
"multipart-rs", "multipart-rs",
"nix 0.29.0", "nix 0.29.0",
"nu-cli", "nu-cli",
@ -3490,10 +3501,10 @@ dependencies = [
[[package]] [[package]]
name = "nu-cli" name = "nu-cli"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"crossterm 0.28.1", "crossterm",
"fancy-regex", "fancy-regex",
"is_executable", "is_executable",
"log", "log",
@ -3516,6 +3527,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"reedline", "reedline",
"rstest", "rstest",
"strum",
"sysinfo", "sysinfo",
"tempfile", "tempfile",
"unicode-segmentation", "unicode-segmentation",
@ -3525,7 +3537,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-base" name = "nu-cmd-base"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"miette", "miette",
@ -3537,7 +3549,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-extra" name = "nu-cmd-extra"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"heck", "heck",
@ -3563,19 +3575,21 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-lang" name = "nu-cmd-lang"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"itertools 0.13.0", "itertools 0.13.0",
"nu-engine", "nu-engine",
"nu-parser", "nu-parser",
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
"quickcheck",
"quickcheck_macros",
"shadow-rs", "shadow-rs",
] ]
[[package]] [[package]]
name = "nu-cmd-plugin" name = "nu-cmd-plugin"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"itertools 0.13.0", "itertools 0.13.0",
"nu-engine", "nu-engine",
@ -3586,7 +3600,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-color-config" name = "nu-color-config"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"nu-engine", "nu-engine",
@ -3598,7 +3612,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-command" name = "nu-command"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"alphanumeric-sort", "alphanumeric-sort",
"base64 0.22.1", "base64 0.22.1",
@ -3611,7 +3625,7 @@ dependencies = [
"chrono", "chrono",
"chrono-humanize", "chrono-humanize",
"chrono-tz", "chrono-tz",
"crossterm 0.28.1", "crossterm",
"csv", "csv",
"data-encoding", "data-encoding",
"devicons", "devicons",
@ -3666,8 +3680,6 @@ dependencies = [
"print-positions", "print-positions",
"procfs", "procfs",
"quick-xml 0.37.1", "quick-xml 0.37.1",
"quickcheck",
"quickcheck_macros",
"rand", "rand",
"rand_chacha", "rand_chacha",
"rayon", "rayon",
@ -3713,10 +3725,10 @@ dependencies = [
[[package]] [[package]]
name = "nu-derive-value" name = "nu-derive-value"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.90", "syn 2.0.90",
@ -3724,7 +3736,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-engine" name = "nu-engine"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"log", "log",
"nu-glob", "nu-glob",
@ -3735,11 +3747,11 @@ dependencies = [
[[package]] [[package]]
name = "nu-explore" name = "nu-explore"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"ansi-str", "ansi-str",
"anyhow", "anyhow",
"crossterm 0.28.1", "crossterm",
"log", "log",
"lscolors", "lscolors",
"nu-ansi-term", "nu-ansi-term",
@ -3759,14 +3771,15 @@ dependencies = [
[[package]] [[package]]
name = "nu-glob" name = "nu-glob"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"doc-comment", "doc-comment",
"nu-protocol",
] ]
[[package]] [[package]]
name = "nu-json" name = "nu-json"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"linked-hash-map", "linked-hash-map",
@ -3779,7 +3792,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-lsp" name = "nu-lsp"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"assert-json-diff", "assert-json-diff",
"crossbeam-channel", "crossbeam-channel",
@ -3790,10 +3803,12 @@ dependencies = [
"nu-cli", "nu-cli",
"nu-cmd-lang", "nu-cmd-lang",
"nu-command", "nu-command",
"nu-engine",
"nu-glob", "nu-glob",
"nu-parser", "nu-parser",
"nu-protocol", "nu-protocol",
"nu-test-support", "nu-test-support",
"nu-utils",
"nucleo-matcher", "nucleo-matcher",
"serde", "serde",
"serde_json", "serde_json",
@ -3802,7 +3817,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-parser" name = "nu-parser"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"bytesize", "bytesize",
"chrono", "chrono",
@ -3819,7 +3834,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-path" name = "nu-path"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"dirs", "dirs",
"omnipath", "omnipath",
@ -3829,7 +3844,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin" name = "nu-plugin"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"log", "log",
"nix 0.29.0", "nix 0.29.0",
@ -3845,7 +3860,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-core" name = "nu-plugin-core"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"log", "log",
@ -3859,7 +3874,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-engine" name = "nu-plugin-engine"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"log", "log",
"nu-engine", "nu-engine",
@ -3875,7 +3890,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-protocol" name = "nu-plugin-protocol"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
@ -3887,7 +3902,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-test-support" name = "nu-plugin-test-support"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"nu-cmd-lang", "nu-cmd-lang",
@ -3905,7 +3920,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-pretty-hex" name = "nu-pretty-hex"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"heapless", "heapless",
"nu-ansi-term", "nu-ansi-term",
@ -3914,7 +3929,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-protocol" name = "nu-protocol"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"brotli", "brotli",
"bytes", "bytes",
@ -3953,7 +3968,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-std" name = "nu-std"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"log", "log",
"miette", "miette",
@ -3964,7 +3979,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-system" name = "nu-system"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"itertools 0.13.0", "itertools 0.13.0",
@ -3982,7 +3997,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-table" name = "nu-table"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"nu-ansi-term", "nu-ansi-term",
@ -3995,7 +4010,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-term-grid" name = "nu-term-grid"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"nu-utils", "nu-utils",
"unicode-width 0.2.0", "unicode-width 0.2.0",
@ -4003,7 +4018,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-test-support" name = "nu-test-support"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"nu-glob", "nu-glob",
"nu-path", "nu-path",
@ -4015,9 +4030,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-utils" name = "nu-utils"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"crossterm 0.28.1", "crossterm",
"crossterm_winapi", "crossterm_winapi",
"fancy-regex", "fancy-regex",
"log", "log",
@ -4044,7 +4059,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_example" name = "nu_plugin_example"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"nu-cmd-lang", "nu-cmd-lang",
"nu-plugin", "nu-plugin",
@ -4054,7 +4069,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_formats" name = "nu_plugin_formats"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"eml-parser", "eml-parser",
@ -4069,7 +4084,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_gstat" name = "nu_plugin_gstat"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"git2", "git2",
"nu-plugin", "nu-plugin",
@ -4078,7 +4093,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_inc" name = "nu_plugin_inc"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"nu-plugin", "nu-plugin",
"nu-protocol", "nu-protocol",
@ -4087,7 +4102,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_polars" name = "nu_plugin_polars"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"aws-config", "aws-config",
"aws-credential-types", "aws-credential-types",
@ -4098,7 +4113,6 @@ dependencies = [
"hashbrown 0.15.2", "hashbrown 0.15.2",
"indexmap", "indexmap",
"log", "log",
"mimalloc",
"nu-cmd-lang", "nu-cmd-lang",
"nu-command", "nu-command",
"nu-engine", "nu-engine",
@ -4127,7 +4141,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_query" name = "nu_plugin_query"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"gjson", "gjson",
"nu-plugin", "nu-plugin",
@ -4142,7 +4156,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_stress_internals" name = "nu_plugin_stress_internals"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"serde", "serde",
@ -4266,7 +4280,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]] [[package]]
name = "nuon" name = "nuon"
version = "0.102.0" version = "0.103.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"nu-engine", "nu-engine",
@ -5350,7 +5364,7 @@ dependencies = [
"bincode", "bincode",
"bytemuck", "bytemuck",
"bytes", "bytes",
"compact_str 0.8.0", "compact_str",
"flate2", "flate2",
"hashbrown 0.15.2", "hashbrown 0.15.2",
"indexmap", "indexmap",
@ -5452,26 +5466,25 @@ dependencies = [
] ]
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error-attr2"
version = "1.0.4" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [ dependencies = [
"proc-macro-error-attr",
"proc-macro2", "proc-macro2",
"quote", "quote",
"version_check",
] ]
[[package]] [[package]]
name = "proc-macro-error-attr" name = "proc-macro-error2"
version = "1.0.4" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [ dependencies = [
"proc-macro-error-attr2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"version_check", "syn 2.0.90",
] ]
[[package]] [[package]]
@ -5714,22 +5727,23 @@ dependencies = [
[[package]] [[package]]
name = "ratatui" name = "ratatui"
version = "0.26.3" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"cassowary", "cassowary",
"compact_str 0.7.1", "compact_str",
"crossterm 0.27.0", "crossterm",
"itertools 0.12.1", "indoc",
"instability",
"itertools 0.13.0",
"lru", "lru",
"paste", "paste",
"stability",
"strum", "strum",
"unicode-segmentation", "unicode-segmentation",
"unicode-truncate", "unicode-truncate",
"unicode-width 0.1.11", "unicode-width 0.2.0",
] ]
[[package]] [[package]]
@ -5809,15 +5823,15 @@ dependencies = [
[[package]] [[package]]
name = "reedline" name = "reedline"
version = "0.38.0" version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bfa8cb0ad84c396c936d8abb814703d7042a433d2da75a0c7060cbdc89109f3" checksum = "dd4728ee71d2aa3a364ee64470d1aa64b3f0467b2d28b73df15259d005dec64a"
dependencies = [ dependencies = [
"arboard", "arboard",
"chrono", "chrono",
"crossterm 0.28.1", "crossterm",
"fd-lock", "fd-lock",
"itertools 0.12.1", "itertools 0.13.0",
"nu-ansi-term", "nu-ansi-term",
"rusqlite", "rusqlite",
"serde", "serde",
@ -5956,15 +5970,14 @@ dependencies = [
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.8" version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
"getrandom", "getrandom",
"libc", "libc",
"spin",
"untrusted", "untrusted",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -6058,9 +6071,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed" name = "rust-embed"
version = "8.5.0" version = "8.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" checksum = "0b3aba5104622db5c9fc61098de54708feb732e7763d7faa2fa625899f00bf6f"
dependencies = [ dependencies = [
"rust-embed-impl", "rust-embed-impl",
"rust-embed-utils", "rust-embed-utils",
@ -6069,9 +6082,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed-impl" name = "rust-embed-impl"
version = "8.5.0" version = "8.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" checksum = "1f198c73be048d2c5aa8e12f7960ad08443e56fd39cc26336719fdb4ea0ebaae"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -6082,9 +6095,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed-utils" name = "rust-embed-utils"
version = "8.5.0" version = "8.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" checksum = "5a2fcdc9f40c8dc2922842ca9add611ad19f332227fc651d015881ad1552bd9a"
dependencies = [ dependencies = [
"sha2", "sha2",
"walkdir", "walkdir",
@ -6272,9 +6285,9 @@ dependencies = [
[[package]] [[package]]
name = "scc" name = "scc"
version = "2.2.5" version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed" checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1"
dependencies = [ dependencies = [
"sdd", "sdd",
] ]
@ -6302,9 +6315,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "scraper" name = "scraper"
version = "0.22.0" version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc3d051b884f40e309de6c149734eab57aa8cc1347992710dc80bcc1c2194c15" checksum = "527e65d9d888567588db4c12da1087598d0f6f8b346cc2c5abc91f05fc2dffe2"
dependencies = [ dependencies = [
"cssparser", "cssparser",
"ego-tree", "ego-tree",
@ -6346,9 +6359,9 @@ dependencies = [
[[package]] [[package]]
name = "sdd" name = "sdd"
version = "3.0.4" version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" checksum = "b07779b9b918cc05650cb30f404d4d7835d26df37c235eded8a6832e2fb82cca"
[[package]] [[package]]
name = "security-framework" name = "security-framework"
@ -6523,6 +6536,12 @@ dependencies = [
"stable_deref_trait", "stable_deref_trait",
] ]
[[package]]
name = "sha1_smol"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.8" version = "0.10.8"
@ -6574,7 +6593,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [ dependencies = [
"libc", "libc",
"mio 0.8.11",
"mio 1.0.3", "mio 1.0.3",
"signal-hook", "signal-hook",
] ]
@ -6701,12 +6719,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "sqlparser" name = "sqlparser"
version = "0.53.0" version = "0.53.0"
@ -6716,16 +6728,6 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "stability"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
dependencies = [
"quote",
"syn 2.0.90",
]
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.0" version = "1.2.0"
@ -7159,9 +7161,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "titlecase" name = "titlecase"
version = "3.3.0" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0e20e744fbec1913fa168f3ffbef64324bbcb152c6cda8394baa79fa5ec9142" checksum = "ef6b5cbe1316986025d8f662ff6945a0c85f2ca8ca13f04b5e0829ddb0d047f2"
dependencies = [ dependencies = [
"regex", "regex",
] ]
@ -7698,8 +7700,11 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
dependencies = [ dependencies = [
"atomic",
"getrandom", "getrandom",
"md-5",
"serde", "serde",
"sha1_smol",
] ]
[[package]] [[package]]
@ -8567,9 +8572,9 @@ dependencies = [
[[package]] [[package]]
name = "zip" name = "zip"
version = "2.2.1" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" checksum = "938cc23ac49778ac8340e366ddc422b2227ea176edb447e23fc0627608dddadd"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"crc32fast", "crc32fast",

View File

@ -10,8 +10,8 @@ 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.82.0" rust-version = "1.83.0"
version = "0.102.0" version = "0.103.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
@ -66,11 +66,11 @@ alphanumeric-sort = "1.5"
ansi-str = "0.8" ansi-str = "0.8"
anyhow = "1.0.82" anyhow = "1.0.82"
base64 = "0.22.1" base64 = "0.22.1"
bracoxide = "0.1.4" bracoxide = "0.1.5"
brotli = "7.0" brotli = "7.0"
byteorder = "1.5" byteorder = "1.5"
bytes = "1" bytes = "1"
bytesize = "1.3" bytesize = "1.3.1"
calamine = "0.26.1" calamine = "0.26.1"
chardetng = "0.1.17" chardetng = "0.1.17"
chrono = { default-features = false, version = "0.4.34" } chrono = { default-features = false, version = "0.4.34" }
@ -104,10 +104,10 @@ lru = "0.12"
lscolors = { version = "0.17", default-features = false } lscolors = { version = "0.17", default-features = false }
lsp-server = "0.7.8" lsp-server = "0.7.8"
lsp-types = { version = "0.97.0", features = ["proposed"] } lsp-types = { version = "0.97.0", features = ["proposed"] }
lsp-textdocument = "0.4.1" lsp-textdocument = "0.4.2"
mach2 = "0.4" mach2 = "0.4"
md5 = { version = "0.10", package = "md-5" } md5 = { version = "0.10", package = "md-5" }
miette = "7.3" miette = "7.5"
mime = "0.3.17" mime = "0.3.17"
mime_guess = "2.0" mime_guess = "2.0"
mockito = { version = "1.6", default-features = false } mockito = { version = "1.6", default-features = false }
@ -127,7 +127,7 @@ pathdiff = "0.2"
percent-encoding = "2" percent-encoding = "2"
pretty_assertions = "1.4" pretty_assertions = "1.4"
print-positions = "0.6" print-positions = "0.6"
proc-macro-error = { version = "1.0", default-features = false } proc-macro-error2 = "2.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"
procfs = "0.17.0" procfs = "0.17.0"
pwd = "1.3" pwd = "1.3"
@ -138,16 +138,16 @@ quote = "1.0"
rand = "0.8" rand = "0.8"
getrandom = "0.2" # pick same version that rand requires getrandom = "0.2" # pick same version that rand requires
rand_chacha = "0.3.1" rand_chacha = "0.3.1"
ratatui = "0.26" ratatui = "0.29"
rayon = "1.10" rayon = "1.10"
reedline = "0.38.0" reedline = "0.39.0"
rmp = "0.8" rmp = "0.8"
rmp-serde = "1.3" rmp-serde = "1.3"
roxmltree = "0.20" roxmltree = "0.20"
rstest = { version = "0.23", default-features = false } rstest = { version = "0.23", default-features = false }
rstest_reuse = "0.7" rstest_reuse = "0.7"
rusqlite = "0.31" rusqlite = "0.31"
rust-embed = "8.5.0" rust-embed = "8.6.0"
scopeguard = { version = "1.2.0" } scopeguard = { version = "1.2.0" }
serde = { version = "1.0" } serde = { version = "1.0" }
serde_json = "1.0" serde_json = "1.0"
@ -155,11 +155,13 @@ serde_urlencoded = "0.7.1"
serde_yaml = "0.9.33" serde_yaml = "0.9.33"
sha2 = "0.10" sha2 = "0.10"
strip-ansi-escapes = "0.2.0" strip-ansi-escapes = "0.2.0"
strum = "0.26"
strum_macros = "0.26"
syn = "2.0" syn = "2.0"
sysinfo = "0.33" sysinfo = "0.33"
tabled = { version = "0.17.0", default-features = false } tabled = { version = "0.17.0", default-features = false }
tempfile = "3.15" tempfile = "3.15"
titlecase = "3.0" titlecase = "3.4"
toml = "0.8" toml = "0.8"
trash = "5.2" trash = "5.2"
update-informer = { version = "1.2.0", default-features = false, features = ["github", "native-tls", "ureq"] } update-informer = { version = "1.2.0", default-features = false, features = ["github", "native-tls", "ureq"] }
@ -195,22 +197,22 @@ unchecked_duration_subtraction = "warn"
workspace = true workspace = true
[dependencies] [dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.102.0" } nu-cli = { path = "./crates/nu-cli", version = "0.103.0" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.102.0" } nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.103.0" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.102.0" } nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.103.0" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.102.0", optional = true } nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.103.0", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.102.0" } nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.103.0" }
nu-command = { path = "./crates/nu-command", version = "0.102.0" } nu-command = { path = "./crates/nu-command", version = "0.103.0" }
nu-engine = { path = "./crates/nu-engine", version = "0.102.0" } nu-engine = { path = "./crates/nu-engine", version = "0.103.0" }
nu-explore = { path = "./crates/nu-explore", version = "0.102.0" } nu-explore = { path = "./crates/nu-explore", version = "0.103.0" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.102.0" } nu-lsp = { path = "./crates/nu-lsp/", version = "0.103.0" }
nu-parser = { path = "./crates/nu-parser", version = "0.102.0" } nu-parser = { path = "./crates/nu-parser", version = "0.103.0" }
nu-path = { path = "./crates/nu-path", version = "0.102.0" } nu-path = { path = "./crates/nu-path", version = "0.103.0" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.102.0" } nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.103.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.102.0" } nu-protocol = { path = "./crates/nu-protocol", version = "0.103.0" }
nu-std = { path = "./crates/nu-std", version = "0.102.0" } nu-std = { path = "./crates/nu-std", version = "0.103.0" }
nu-system = { path = "./crates/nu-system", version = "0.102.0" } nu-system = { path = "./crates/nu-system", version = "0.103.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.102.0" } nu-utils = { path = "./crates/nu-utils", version = "0.103.0" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] } reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true } crossterm = { workspace = true }
@ -218,7 +220,6 @@ ctrlc = { workspace = true }
dirs = { workspace = true } dirs = { workspace = true }
log = { workspace = true } log = { workspace = true }
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] } miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
mimalloc = { version = "0.1.42", default-features = false, optional = true }
multipart-rs = { workspace = true } multipart-rs = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
simplelog = "0.12" simplelog = "0.12"
@ -240,9 +241,9 @@ nix = { workspace = true, default-features = false, features = [
] } ] }
[dev-dependencies] [dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.102.0" } nu-test-support = { path = "./crates/nu-test-support", version = "0.103.0" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.102.0" } nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.103.0" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.102.0" } nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.103.0" }
assert_cmd = "2.0" assert_cmd = "2.0"
dirs = { workspace = true } dirs = { workspace = true }
tango-bench = "0.6" tango-bench = "0.6"
@ -272,7 +273,6 @@ default = [
"plugin", "plugin",
"trash-support", "trash-support",
"sqlite", "sqlite",
"mimalloc",
] ]
stable = ["default"] stable = ["default"]
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command # NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
@ -281,7 +281,6 @@ stable = ["default"]
# otherwise the system version will be used. Not enabled by default because it takes a while to build # otherwise the system version will be used. Not enabled by default because it takes a while to build
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"] static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
mimalloc = ["nu-cmd-lang/mimalloc", "dep:mimalloc"]
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems. # Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
# Missing X server/ Wayland can cause issues # Missing X server/ Wayland can cause issues
system-clipboard = [ system-clipboard = [
@ -294,7 +293,7 @@ system-clipboard = [
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"] trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
# SQLite commands for nushell # SQLite commands for nushell
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite"] sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite", "nu-std/sqlite"]
[profile.release] [profile.release]
opt-level = "s" # Optimize for size opt-level = "s" # Optimize for size

View File

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

View File

@ -4,7 +4,6 @@
[![Nightly Build](https://github.com/nushell/nushell/actions/workflows/nightly-build.yml/badge.svg)](https://github.com/nushell/nushell/actions/workflows/nightly-build.yml) [![Nightly Build](https://github.com/nushell/nushell/actions/workflows/nightly-build.yml/badge.svg)](https://github.com/nushell/nushell/actions/workflows/nightly-build.yml)
[![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)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/nushell/nushell)](https://github.com/nushell/nushell/graphs/commit-activity) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/nushell/nushell)](https://github.com/nushell/nushell/graphs/commit-activity)
[![GitHub contributors](https://img.shields.io/github/contributors/nushell/nushell)](https://github.com/nushell/nushell/graphs/contributors) [![GitHub contributors](https://img.shields.io/github/contributors/nushell/nushell)](https://github.com/nushell/nushell/graphs/contributors)
@ -35,7 +34,7 @@ This project has reached a minimum-viable-product level of quality. Many people
The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/commands/), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/). The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/commands/), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/).
We're also active on [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell); come and chat with us! We're also active on [Discord](https://discord.gg/NtAbbGn); come and chat with us!
## Installation ## Installation

View File

@ -1,7 +1,6 @@
use nu_cli::{eval_source, evaluate_commands}; use nu_cli::{eval_source, evaluate_commands};
use nu_plugin_core::{Encoder, EncodingType}; use nu_plugin_core::{Encoder, EncodingType};
use nu_plugin_protocol::{PluginCallResponse, PluginOutput}; use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
PipelineData, Signals, Span, Spanned, Value, PipelineData, Signals, Span, Spanned, Value,
@ -9,12 +8,11 @@ use nu_protocol::{
use nu_std::load_standard_library; use nu_std::load_standard_library;
use nu_utils::{get_default_config, get_default_env}; use nu_utils::{get_default_config, get_default_env};
use std::{ use std::{
fmt::Write,
hint::black_box,
rc::Rc, rc::Rc,
sync::{atomic::AtomicBool, Arc}, sync::{atomic::AtomicBool, Arc},
}; };
use std::hint::black_box;
use tango_bench::{benchmark_fn, tango_benchmarks, tango_main, IntoBenchmarks}; use tango_bench::{benchmark_fn, tango_benchmarks, tango_main, IntoBenchmarks};
fn load_bench_commands() -> EngineState { fn load_bench_commands() -> EngineState {
@ -141,19 +139,16 @@ fn bench_load_standard_lib() -> impl IntoBenchmarks {
})] })]
} }
fn create_flat_record_string(n: i32) -> String { fn create_flat_record_string(n: usize) -> String {
let mut s = String::from("let record = {"); let mut s = String::from("let record = { ");
for i in 0..n { for i in 0..n {
s.push_str(&format!("col_{}: {}", i, i)); write!(s, "col_{i}: {i}, ").unwrap();
if i < n - 1 {
s.push_str(", ");
}
} }
s.push('}'); s.push('}');
s s
} }
fn create_nested_record_string(depth: i32) -> String { fn create_nested_record_string(depth: usize) -> String {
let mut s = String::from("let record = {"); let mut s = String::from("let record = {");
for _ in 0..depth { for _ in 0..depth {
s.push_str("col: {"); s.push_str("col: {");
@ -166,7 +161,7 @@ fn create_nested_record_string(depth: i32) -> String {
s s
} }
fn create_example_table_nrows(n: i32) -> String { fn create_example_table_nrows(n: usize) -> String {
let mut s = String::from("let table = [[foo bar baz]; "); let mut s = String::from("let table = [[foo bar baz]; ");
for i in 0..n { for i in 0..n {
s.push_str(&format!("[0, 1, {i}]")); s.push_str(&format!("[0, 1, {i}]"));
@ -178,7 +173,7 @@ fn create_example_table_nrows(n: i32) -> String {
s s
} }
fn bench_record_create(n: i32) -> impl IntoBenchmarks { fn bench_record_create(n: usize) -> impl IntoBenchmarks {
bench_command( bench_command(
&format!("record_create_{n}"), &format!("record_create_{n}"),
&create_flat_record_string(n), &create_flat_record_string(n),
@ -187,7 +182,7 @@ fn bench_record_create(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_record_flat_access(n: i32) -> impl IntoBenchmarks { fn bench_record_flat_access(n: usize) -> impl IntoBenchmarks {
let setup_command = create_flat_record_string(n); let setup_command = create_flat_record_string(n);
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command); let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
bench_command( bench_command(
@ -198,10 +193,10 @@ fn bench_record_flat_access(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_record_nested_access(n: i32) -> impl IntoBenchmarks { fn bench_record_nested_access(n: usize) -> impl IntoBenchmarks {
let setup_command = create_nested_record_string(n); let setup_command = create_nested_record_string(n);
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command); let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
let nested_access = ".col".repeat(n as usize); let nested_access = ".col".repeat(n);
bench_command( bench_command(
&format!("record_nested_access_{n}"), &format!("record_nested_access_{n}"),
&format!("$record{} | ignore", nested_access), &format!("$record{} | ignore", nested_access),
@ -210,7 +205,18 @@ fn bench_record_nested_access(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_table_create(n: i32) -> impl IntoBenchmarks { fn bench_record_insert(n: usize, m: usize) -> impl IntoBenchmarks {
let setup_command = create_flat_record_string(n);
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
let mut insert = String::from("$record");
for i in n..(n + m) {
write!(insert, " | insert col_{i} {i}").unwrap();
}
insert.push_str(" | ignore");
bench_command(&format!("record_insert_{n}_{m}"), &insert, stack, engine)
}
fn bench_table_create(n: usize) -> impl IntoBenchmarks {
bench_command( bench_command(
&format!("table_create_{n}"), &format!("table_create_{n}"),
&create_example_table_nrows(n), &create_example_table_nrows(n),
@ -219,7 +225,7 @@ fn bench_table_create(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_table_get(n: i32) -> impl IntoBenchmarks { fn bench_table_get(n: usize) -> impl IntoBenchmarks {
let setup_command = create_example_table_nrows(n); let setup_command = create_example_table_nrows(n);
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command); let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
bench_command( bench_command(
@ -230,7 +236,7 @@ fn bench_table_get(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_table_select(n: i32) -> impl IntoBenchmarks { fn bench_table_select(n: usize) -> impl IntoBenchmarks {
let setup_command = create_example_table_nrows(n); let setup_command = create_example_table_nrows(n);
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command); let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
bench_command( bench_command(
@ -241,7 +247,29 @@ fn bench_table_select(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks { fn bench_table_insert_row(n: usize, m: usize) -> impl IntoBenchmarks {
let setup_command = create_example_table_nrows(n);
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
let mut insert = String::from("$table");
for i in n..(n + m) {
write!(insert, " | insert {i} {{ foo: 0, bar: 1, baz: {i} }}").unwrap();
}
insert.push_str(" | ignore");
bench_command(&format!("table_insert_row_{n}_{m}"), &insert, stack, engine)
}
fn bench_table_insert_col(n: usize, m: usize) -> impl IntoBenchmarks {
let setup_command = create_example_table_nrows(n);
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
let mut insert = String::from("$table");
for i in 0..m {
write!(insert, " | insert col_{i} {i}").unwrap();
}
insert.push_str(" | ignore");
bench_command(&format!("table_insert_col_{n}_{m}"), &insert, stack, engine)
}
fn bench_eval_interleave(n: usize) -> impl IntoBenchmarks {
let engine = setup_engine(); let engine = setup_engine();
let stack = Stack::new(); let stack = Stack::new();
bench_command( bench_command(
@ -252,7 +280,7 @@ fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks { fn bench_eval_interleave_with_interrupt(n: usize) -> impl IntoBenchmarks {
let mut engine = setup_engine(); let mut engine = setup_engine();
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false)))); engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
let stack = Stack::new(); let stack = Stack::new();
@ -264,7 +292,7 @@ fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_eval_for(n: i32) -> impl IntoBenchmarks { fn bench_eval_for(n: usize) -> impl IntoBenchmarks {
let engine = setup_engine(); let engine = setup_engine();
let stack = Stack::new(); let stack = Stack::new();
bench_command( bench_command(
@ -275,7 +303,7 @@ fn bench_eval_for(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_eval_each(n: i32) -> impl IntoBenchmarks { fn bench_eval_each(n: usize) -> impl IntoBenchmarks {
let engine = setup_engine(); let engine = setup_engine();
let stack = Stack::new(); let stack = Stack::new();
bench_command( bench_command(
@ -286,7 +314,7 @@ fn bench_eval_each(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_eval_par_each(n: i32) -> impl IntoBenchmarks { fn bench_eval_par_each(n: usize) -> impl IntoBenchmarks {
let engine = setup_engine(); let engine = setup_engine();
let stack = Stack::new(); let stack = Stack::new();
bench_command( bench_command(
@ -427,6 +455,14 @@ tango_benchmarks!(
bench_record_nested_access(32), bench_record_nested_access(32),
bench_record_nested_access(64), bench_record_nested_access(64),
bench_record_nested_access(128), bench_record_nested_access(128),
bench_record_insert(1, 1),
bench_record_insert(10, 1),
bench_record_insert(100, 1),
bench_record_insert(1000, 1),
bench_record_insert(1, 10),
bench_record_insert(10, 10),
bench_record_insert(100, 10),
bench_record_insert(1000, 10),
// Table // Table
bench_table_create(1), bench_table_create(1),
bench_table_create(10), bench_table_create(10),
@ -440,6 +476,22 @@ tango_benchmarks!(
bench_table_select(10), bench_table_select(10),
bench_table_select(100), bench_table_select(100),
bench_table_select(1_000), bench_table_select(1_000),
bench_table_insert_row(1, 1),
bench_table_insert_row(10, 1),
bench_table_insert_row(100, 1),
bench_table_insert_row(1000, 1),
bench_table_insert_row(1, 10),
bench_table_insert_row(10, 10),
bench_table_insert_row(100, 10),
bench_table_insert_row(1000, 10),
bench_table_insert_col(1, 1),
bench_table_insert_col(10, 1),
bench_table_insert_col(100, 1),
bench_table_insert_col(1000, 1),
bench_table_insert_col(1, 10),
bench_table_insert_col(10, 10),
bench_table_insert_col(100, 10),
bench_table_insert_col(1000, 10),
// Eval // Eval
// Interleave // Interleave
bench_eval_interleave(100), bench_eval_interleave(100),

View File

@ -5,28 +5,28 @@ 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.102.0" version = "0.103.0"
[lib] [lib]
bench = false bench = false
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.0" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.0" }
nu-command = { path = "../nu-command", version = "0.102.0" } nu-command = { path = "../nu-command", version = "0.103.0" }
nu-test-support = { path = "../nu-test-support", version = "0.102.0" } nu-test-support = { path = "../nu-test-support", version = "0.103.0" }
rstest = { workspace = true, default-features = false } rstest = { workspace = true, default-features = false }
tempfile = { workspace = true } tempfile = { workspace = true }
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.0" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.0" }
nu-engine = { path = "../nu-engine", version = "0.102.0", features = ["os"] } nu-engine = { path = "../nu-engine", version = "0.103.0", features = ["os"] }
nu-glob = { path = "../nu-glob", version = "0.102.0" } nu-glob = { path = "../nu-glob", version = "0.103.0" }
nu-path = { path = "../nu-path", version = "0.102.0" } nu-path = { path = "../nu-path", version = "0.103.0" }
nu-parser = { path = "../nu-parser", version = "0.102.0" } nu-parser = { path = "../nu-parser", version = "0.103.0" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.102.0", optional = true } nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.103.0", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.102.0", features = ["os"] } nu-protocol = { path = "../nu-protocol", version = "0.103.0", features = ["os"] }
nu-utils = { path = "../nu-utils", version = "0.102.0" } nu-utils = { path = "../nu-utils", version = "0.103.0" }
nu-color-config = { path = "../nu-color-config", version = "0.102.0" } nu-color-config = { path = "../nu-color-config", version = "0.103.0" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
reedline = { workspace = true, features = ["bashisms", "sqlite"] } reedline = { workspace = true, features = ["bashisms", "sqlite"] }
@ -40,6 +40,7 @@ miette = { workspace = true, features = ["fancy-no-backtrace"] }
nucleo-matcher = { workspace = true } nucleo-matcher = { workspace = true }
percent-encoding = { workspace = true } percent-encoding = { workspace = true }
sysinfo = { workspace = true } sysinfo = { workspace = true }
strum = { workspace = true }
unicode-segmentation = { workspace = true } unicode-segmentation = { workspace = true }
uuid = { workspace = true, features = ["v4"] } uuid = { workspace = true, features = ["v4"] }
which = { workspace = true } which = { workspace = true }
@ -49,4 +50,4 @@ plugin = ["nu-plugin-engine"]
system-clipboard = ["reedline/system_clipboard"] system-clipboard = ["reedline/system_clipboard"]
[lints] [lints]
workspace = true workspace = true

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct CommandlineEdit;
impl Command for SubCommand { impl Command for CommandlineEdit {
fn name(&self) -> &str { fn name(&self) -> &str {
"commandline edit" "commandline edit"
} }
@ -29,7 +29,7 @@ impl Command for SubCommand {
.required( .required(
"str", "str",
SyntaxShape::String, SyntaxShape::String,
"the string to perform the operation with", "The string to perform the operation with.",
) )
.category(Category::Core) .category(Category::Core)
} }

View File

@ -2,9 +2,9 @@ use nu_engine::command_prelude::*;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct CommandlineGetCursor;
impl Command for SubCommand { impl Command for CommandlineGetCursor {
fn name(&self) -> &str { fn name(&self) -> &str {
"commandline get-cursor" "commandline get-cursor"
} }

View File

@ -4,6 +4,6 @@ mod get_cursor;
mod set_cursor; mod set_cursor;
pub use commandline_::Commandline; pub use commandline_::Commandline;
pub use edit::SubCommand as CommandlineEdit; pub use edit::CommandlineEdit;
pub use get_cursor::SubCommand as CommandlineGetCursor; pub use get_cursor::CommandlineGetCursor;
pub use set_cursor::SubCommand as CommandlineSetCursor; pub use set_cursor::CommandlineSetCursor;

View File

@ -3,9 +3,9 @@ use nu_engine::command_prelude::*;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct CommandlineSetCursor;
impl Command for SubCommand { impl Command for CommandlineSetCursor {
fn name(&self) -> &str { fn name(&self) -> &str {
"commandline set-cursor" "commandline set-cursor"
} }
@ -18,7 +18,7 @@ impl Command for SubCommand {
"set the current cursor position to the end of the buffer", "set the current cursor position to the end of the buffer",
Some('e'), Some('e'),
) )
.optional("pos", SyntaxShape::Int, "Cursor position to be set") .optional("pos", SyntaxShape::Int, "Cursor position to be set.")
.category(Category::Core) .category(Category::Core)
} }

View File

@ -21,7 +21,7 @@ impl Command for HistoryImport {
} }
fn description(&self) -> &str { fn description(&self) -> &str {
"Import command line history" "Import command line history."
} }
fn extra_description(&self) -> &str { fn extra_description(&self) -> &str {

View File

@ -0,0 +1,87 @@
use super::{completion_options::NuMatcher, SemanticSuggestion};
use crate::{
completions::{Completer, CompletionOptions},
SuggestionKind,
};
use nu_protocol::{
engine::{Stack, StateWorkingSet},
Span,
};
use reedline::Suggestion;
pub struct AttributeCompletion;
pub struct AttributableCompletion;
impl Completer for AttributeCompletion {
fn fetch(
&mut self,
working_set: &StateWorkingSet,
_stack: &Stack,
prefix: impl AsRef<str>,
span: Span,
offset: usize,
options: &CompletionOptions,
) -> Vec<SemanticSuggestion> {
let mut matcher = NuMatcher::new(prefix, options);
let attr_commands =
working_set.find_commands_by_predicate(|s| s.starts_with(b"attr "), true);
for (name, desc, ty) in attr_commands {
let name = name.strip_prefix(b"attr ").unwrap_or(&name);
matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion {
value: String::from_utf8_lossy(name).into_owned(),
description: desc,
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
},
kind: Some(SuggestionKind::Command(ty)),
});
}
matcher.results()
}
}
impl Completer for AttributableCompletion {
fn fetch(
&mut self,
working_set: &StateWorkingSet,
_stack: &Stack,
prefix: impl AsRef<str>,
span: Span,
offset: usize,
options: &CompletionOptions,
) -> Vec<SemanticSuggestion> {
let mut matcher = NuMatcher::new(prefix, options);
for s in ["def", "extern", "export def", "export extern"] {
let decl_id = working_set
.find_decl(s.as_bytes())
.expect("internal error, builtin declaration not found");
let cmd = working_set.get_decl(decl_id);
matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion {
value: cmd.name().into(),
description: Some(cmd.description().into()),
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
},
kind: Some(SuggestionKind::Command(cmd.command_type())),
});
}
matcher.results()
}
}

View File

@ -12,10 +12,9 @@ pub trait Completer {
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
stack: &Stack, stack: &Stack,
prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion>; ) -> Vec<SemanticSuggestion>;
} }
@ -30,8 +29,14 @@ pub struct SemanticSuggestion {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum SuggestionKind { pub enum SuggestionKind {
Command(nu_protocol::engine::CommandType), Command(nu_protocol::engine::CommandType),
Type(nu_protocol::Type), Value(nu_protocol::Type),
CellPath,
Directory,
File,
Flag,
Module, Module,
Operator,
Variable,
} }
impl From<Suggestion> for SemanticSuggestion { impl From<Suggestion> for SemanticSuggestion {

View File

@ -0,0 +1,137 @@
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
use nu_engine::{column::get_columns, eval_variable};
use nu_protocol::{
ast::{Expr, Expression, FullCellPath, PathMember},
engine::{Stack, StateWorkingSet},
eval_const::eval_constant,
ShellError, Span, Value,
};
use reedline::Suggestion;
use super::completion_options::NuMatcher;
pub struct CellPathCompletion<'a> {
pub full_cell_path: &'a FullCellPath,
pub position: usize,
}
fn prefix_from_path_member(member: &PathMember, pos: usize) -> (String, Span) {
let (prefix_str, start) = match member {
PathMember::String { val, span, .. } => (val.clone(), span.start),
PathMember::Int { val, span, .. } => (val.to_string(), span.start),
};
let prefix_str = prefix_str
.get(..pos + 1 - start)
.map(str::to_string)
.unwrap_or(prefix_str);
(prefix_str, Span::new(start, pos + 1))
}
impl Completer for CellPathCompletion<'_> {
fn fetch(
&mut self,
working_set: &StateWorkingSet,
stack: &Stack,
_prefix: impl AsRef<str>,
_span: Span,
offset: usize,
options: &CompletionOptions,
) -> Vec<SemanticSuggestion> {
let mut prefix_str = String::new();
// position at dots, e.g. `$env.config.<TAB>`
let mut span = Span::new(self.position + 1, self.position + 1);
let mut path_member_num_before_pos = 0;
for member in self.full_cell_path.tail.iter() {
if member.span().end <= self.position {
path_member_num_before_pos += 1;
} else if member.span().contains(self.position) {
(prefix_str, span) = prefix_from_path_member(member, self.position);
break;
}
}
let current_span = reedline::Span {
start: span.start - offset,
end: span.end - offset,
};
let mut matcher = NuMatcher::new(prefix_str, options);
let path_members = self
.full_cell_path
.tail
.get(0..path_member_num_before_pos)
.unwrap_or_default();
let value = eval_cell_path(
working_set,
stack,
&self.full_cell_path.head,
path_members,
span,
)
.unwrap_or_default();
for suggestion in get_suggestions_by_value(&value, current_span) {
matcher.add_semantic_suggestion(suggestion);
}
matcher.results()
}
}
/// Follow cell path to get the value
/// NOTE: This is a relatively lightweight implementation,
/// so it may fail to get the exact value when the expression is complicated.
/// One failing example would be `[$foo].0`
pub(crate) fn eval_cell_path(
working_set: &StateWorkingSet,
stack: &Stack,
head: &Expression,
path_members: &[PathMember],
span: Span,
) -> Result<Value, ShellError> {
// evaluate the head expression to get its value
let head_value = if let Expr::Var(var_id) = head.expr {
working_set
.get_variable(var_id)
.const_val
.to_owned()
.map_or_else(
|| eval_variable(working_set.permanent_state, stack, var_id, span),
Ok,
)
} else {
eval_constant(working_set, head)
}?;
head_value.follow_cell_path(path_members, false)
}
fn get_suggestions_by_value(
value: &Value,
current_span: reedline::Span,
) -> Vec<SemanticSuggestion> {
let to_suggestion = |s: String, v: Option<&Value>| SemanticSuggestion {
suggestion: Suggestion {
value: s,
span: current_span,
description: v.map(|v| v.get_type().to_string()),
..Suggestion::default()
},
kind: Some(SuggestionKind::CellPath),
};
match value {
Value::Record { val, .. } => val
.columns()
.map(|s| to_suggestion(s.to_string(), val.get(s)))
.collect(),
Value::List { vals, .. } => get_columns(vals.as_slice())
.into_iter()
.map(|s| {
let sub_val = vals
.first()
.and_then(|v| v.as_record().ok())
.and_then(|rv| rv.get(&s));
to_suggestion(s, sub_val)
})
.collect(),
_ => vec![],
}
}

View File

@ -4,9 +4,8 @@ use crate::{
completions::{Completer, CompletionOptions}, completions::{Completer, CompletionOptions},
SuggestionKind, SuggestionKind,
}; };
use nu_parser::FlatShape;
use nu_protocol::{ use nu_protocol::{
engine::{CachedFile, Stack, StateWorkingSet}, engine::{CommandType, Stack, StateWorkingSet},
Span, Span,
}; };
use reedline::Suggestion; use reedline::Suggestion;
@ -14,24 +13,13 @@ use reedline::Suggestion;
use super::{completion_options::NuMatcher, SemanticSuggestion}; use super::{completion_options::NuMatcher, SemanticSuggestion};
pub struct CommandCompletion { pub struct CommandCompletion {
flattened: Vec<(Span, FlatShape)>, /// Whether to include internal commands
flat_shape: FlatShape, pub internals: bool,
force_completion_after_space: bool, /// Whether to include external commands
pub externals: bool,
} }
impl CommandCompletion { impl CommandCompletion {
pub fn new(
flattened: Vec<(Span, FlatShape)>,
flat_shape: FlatShape,
force_completion_after_space: bool,
) -> Self {
Self {
flattened,
flat_shape,
force_completion_after_space,
}
}
fn external_command_completion( fn external_command_completion(
&self, &self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
@ -71,6 +59,9 @@ impl CommandCompletion {
if suggs.contains_key(&value) { if suggs.contains_key(&value) {
continue; continue;
} }
// TODO: check name matching before a relative heavy IO involved
// `is_executable` for performance consideration, should avoid
// duplicated `match_aux` call for matched items in the future
if matcher.matches(&name) && is_executable::is_executable(item.path()) { if matcher.matches(&name) && is_executable::is_executable(item.path()) {
// If there's an internal command with the same name, adds ^cmd to the // If there's an internal command with the same name, adds ^cmd to the
// matcher so that both the internal and external command are included // matcher so that both the internal and external command are included
@ -84,8 +75,7 @@ impl CommandCompletion {
append_whitespace: true, append_whitespace: true,
..Default::default() ..Default::default()
}, },
// TODO: is there a way to create a test? kind: Some(SuggestionKind::Command(CommandType::External)),
kind: None,
}, },
); );
} }
@ -97,46 +87,50 @@ impl CommandCompletion {
suggs suggs
} }
}
fn complete_commands( impl Completer for CommandCompletion {
&self, fn fetch(
&mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
_stack: &Stack,
prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
find_externals: bool,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
let partial = working_set.get_span_contents(span); let mut matcher = NuMatcher::new(prefix, options);
let mut matcher = NuMatcher::new(String::from_utf8_lossy(partial), options.clone());
let sugg_span = reedline::Span::new(span.start - offset, span.end - offset); let sugg_span = reedline::Span::new(span.start - offset, span.end - offset);
let mut internal_suggs = HashMap::new(); let mut internal_suggs = HashMap::new();
let filtered_commands = working_set.find_commands_by_predicate( if self.internals {
|name| { let filtered_commands = working_set.find_commands_by_predicate(
let name = String::from_utf8_lossy(name); |name| {
matcher.add(&name, name.to_string()) let name = String::from_utf8_lossy(name);
}, matcher.add(&name, name.to_string())
true,
);
for (name, description, typ) in filtered_commands {
let name = String::from_utf8_lossy(&name);
internal_suggs.insert(
name.to_string(),
SemanticSuggestion {
suggestion: Suggestion {
value: name.to_string(),
description,
span: sugg_span,
append_whitespace: true,
..Suggestion::default()
},
kind: Some(SuggestionKind::Command(typ)),
}, },
true,
); );
for (name, description, typ) in filtered_commands {
let name = String::from_utf8_lossy(&name);
internal_suggs.insert(
name.to_string(),
SemanticSuggestion {
suggestion: Suggestion {
value: name.to_string(),
description,
span: sugg_span,
append_whitespace: true,
..Suggestion::default()
},
kind: Some(SuggestionKind::Command(typ)),
},
);
}
} }
let mut external_suggs = if find_externals { let mut external_suggs = if self.externals {
self.external_command_completion( self.external_command_completion(
working_set, working_set,
sugg_span, sugg_span,
@ -159,179 +153,3 @@ impl CommandCompletion {
res res
} }
} }
impl Completer for CommandCompletion {
fn fetch(
&mut self,
working_set: &StateWorkingSet,
_stack: &Stack,
_prefix: &[u8],
span: Span,
offset: usize,
pos: usize,
options: &CompletionOptions,
) -> Vec<SemanticSuggestion> {
let last = self
.flattened
.iter()
.rev()
.skip_while(|x| x.0.end > pos)
.take_while(|x| {
matches!(
x.1,
FlatShape::InternalCall(_)
| FlatShape::External
| FlatShape::ExternalArg
| FlatShape::Literal
| FlatShape::String
)
})
.last();
// The last item here would be the earliest shape that could possible by part of this subcommand
let subcommands = if let Some(last) = last {
self.complete_commands(
working_set,
Span::new(last.0.start, pos),
offset,
false,
options,
)
} else {
vec![]
};
if !subcommands.is_empty() {
return subcommands;
}
let config = working_set.get_config();
if matches!(self.flat_shape, nu_parser::FlatShape::External)
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall(_))
|| ((span.end - span.start) == 0)
|| is_passthrough_command(working_set.delta.get_file_contents())
{
// we're in a gap or at a command
if working_set.get_span_contents(span).is_empty() && !self.force_completion_after_space
{
return vec![];
}
self.complete_commands(
working_set,
span,
offset,
config.completions.external.enable,
options,
)
} else {
vec![]
}
}
}
pub fn find_non_whitespace_index(contents: &[u8], start: usize) -> usize {
match contents.get(start..) {
Some(contents) => {
contents
.iter()
.take_while(|x| x.is_ascii_whitespace())
.count()
+ start
}
None => start,
}
}
pub fn is_passthrough_command(working_set_file_contents: &[CachedFile]) -> bool {
for cached_file in working_set_file_contents {
let contents = &cached_file.content;
let last_pipe_pos_rev = contents.iter().rev().position(|x| x == &b'|');
let last_pipe_pos = last_pipe_pos_rev.map(|x| contents.len() - x).unwrap_or(0);
let cur_pos = find_non_whitespace_index(contents, last_pipe_pos);
let result = match contents.get(cur_pos..) {
Some(contents) => contents.starts_with(b"sudo ") || contents.starts_with(b"doas "),
None => false,
};
if result {
return true;
}
}
false
}
#[cfg(test)]
mod command_completions_tests {
use super::*;
use nu_protocol::engine::EngineState;
use std::sync::Arc;
#[test]
fn test_find_non_whitespace_index() {
let commands = [
(" hello", 4),
("sudo ", 0),
(" sudo ", 2),
(" sudo ", 2),
(" hello ", 1),
(" hello ", 3),
(" hello | sudo ", 4),
(" sudo|sudo", 5),
("sudo | sudo ", 0),
(" hello sud", 1),
];
for (idx, ele) in commands.iter().enumerate() {
let index = find_non_whitespace_index(ele.0.as_bytes(), 0);
assert_eq!(index, ele.1, "Failed on index {}", idx);
}
}
#[test]
fn test_is_last_command_passthrough() {
let commands = [
(" hello", false),
(" sudo ", true),
("sudo ", true),
(" hello", false),
(" sudo", false),
(" sudo ", true),
(" sudo ", true),
(" sudo ", true),
(" hello ", false),
(" hello | sudo ", true),
(" sudo|sudo", false),
("sudo | sudo ", true),
(" hello sud", false),
(" sudo | sud ", false),
(" sudo|sudo ", true),
(" sudo | sudo ls | sudo ", true),
];
for (idx, ele) in commands.iter().enumerate() {
let input = ele.0.as_bytes();
let mut engine_state = EngineState::new();
engine_state.add_file("test.nu".into(), Arc::new([]));
let delta = {
let mut working_set = StateWorkingSet::new(&engine_state);
let _ = working_set.add_file("child.nu".into(), input);
working_set.render()
};
let result = engine_state.merge_delta(delta);
assert!(
result.is_ok(),
"Merge delta has failed: {}",
result.err().unwrap()
);
let is_passthrough_command = is_passthrough_command(engine_state.get_file_contents());
assert_eq!(
is_passthrough_command, ele.1,
"index for '{}': {}",
ele.0, idx
);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@ fn complete_rec(
} }
let prefix = partial.first().unwrap_or(&""); let prefix = partial.first().unwrap_or(&"");
let mut matcher = NuMatcher::new(prefix, options.clone()); let mut matcher = NuMatcher::new(prefix, options);
for built in built_paths { for built in built_paths {
let mut path = built.cwd.clone(); let mut path = built.cwd.clone();
@ -65,10 +65,11 @@ fn complete_rec(
for entry in result.filter_map(|e| e.ok()) { for entry in result.filter_map(|e| e.ok()) {
let entry_name = entry.file_name().to_string_lossy().into_owned(); let entry_name = entry.file_name().to_string_lossy().into_owned();
let entry_isdir = entry.path().is_dir() && !entry.path().is_symlink(); let entry_isdir = entry.path().is_dir();
let mut built = built.clone(); let mut built = built.clone();
built.parts.push(entry_name.clone()); built.parts.push(entry_name.clone());
built.isdir = entry_isdir; // Symlinks to directories shouldn't have a trailing slash (#13275)
built.isdir = entry_isdir && !entry.path().is_symlink();
if !want_directory || entry_isdir { if !want_directory || entry_isdir {
matcher.add(entry_name.clone(), (entry_name, built)); matcher.add(entry_name.clone(), (entry_name, built));
@ -157,6 +158,7 @@ pub struct FileSuggestion {
pub span: nu_protocol::Span, pub span: nu_protocol::Span,
pub path: String, pub path: String,
pub style: Option<Style>, pub style: Option<Style>,
pub is_dir: bool,
} }
/// # Parameters /// # Parameters
@ -260,6 +262,7 @@ pub fn complete_item(
if should_collapse_dots { if should_collapse_dots {
p = collapse_ndots(p); p = collapse_ndots(p);
} }
let is_dir = p.isdir;
let path = original_cwd.apply(p, path_separator); let path = original_cwd.apply(p, path_separator);
let style = ls_colors.as_ref().map(|lsc| { let style = ls_colors.as_ref().map(|lsc| {
lsc.style_for_path_with_metadata( lsc.style_for_path_with_metadata(
@ -273,38 +276,39 @@ pub fn complete_item(
}); });
FileSuggestion { FileSuggestion {
span, span,
path: escape_path(path, want_directory), path: escape_path(path),
style, style,
is_dir,
} }
}) })
.collect() .collect()
} }
// Fix files or folders with quotes or hashes // Fix files or folders with quotes or hashes
pub fn escape_path(path: String, dir: bool) -> String { pub fn escape_path(path: String) -> String {
// make glob pattern have the highest priority. // make glob pattern have the highest priority.
if nu_glob::is_glob(path.as_str()) { if nu_glob::is_glob(path.as_str()) || path.contains('`') {
// expand home `~` for https://github.com/nushell/nushell/issues/13905
let pathbuf = nu_path::expand_tilde(path); let pathbuf = nu_path::expand_tilde(path);
let path = pathbuf.to_string_lossy(); let path = pathbuf.to_string_lossy();
return if path.contains('\'') { if path.contains('\'') {
// decide to use double quote, also need to escape `"` in path // decide to use double quotes
// or else users can't do anything with completed path either. // Path as Debug will do the escaping for `"`, `\`
format!("\"{}\"", path.replace('"', r#"\""#)) format!("{:?}", path)
} else { } else {
format!("'{path}'") format!("'{path}'")
}; }
}
let filename_contaminated = !dir && path.contains(['\'', '"', ' ', '#', '(', ')']);
let dirname_contaminated = dir && path.contains(['\'', '"', ' ', '#']);
let maybe_flag = path.starts_with('-');
let maybe_variable = path.starts_with('$');
let maybe_number = path.parse::<f64>().is_ok();
if filename_contaminated || dirname_contaminated || maybe_flag || maybe_variable || maybe_number
{
format!("`{path}`")
} else { } else {
path let contaminated =
path.contains(['\'', '"', ' ', '#', '(', ')', '{', '}', '[', ']', '|', ';']);
let maybe_flag = path.starts_with('-');
let maybe_variable = path.starts_with('$');
let maybe_number = path.parse::<f64>().is_ok();
if contaminated || maybe_flag || maybe_variable || maybe_number {
format!("`{path}`")
} else {
path
}
} }
} }
@ -315,12 +319,12 @@ pub struct AdjustView {
} }
pub fn adjust_if_intermediate( pub fn adjust_if_intermediate(
prefix: &[u8], prefix: &str,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
mut span: nu_protocol::Span, mut span: nu_protocol::Span,
) -> AdjustView { ) -> AdjustView {
let span_contents = String::from_utf8_lossy(working_set.get_span_contents(span)).to_string(); let span_contents = String::from_utf8_lossy(working_set.get_span_contents(span)).to_string();
let mut prefix = String::from_utf8_lossy(prefix).to_string(); let mut prefix = prefix.to_string();
// A difference of 1 because of the cursor's unicode code point in between. // A difference of 1 because of the cursor's unicode code point in between.
// Using .chars().count() because unicode and Windows. // Using .chars().count() because unicode and Windows.

View File

@ -25,8 +25,8 @@ pub enum MatchAlgorithm {
Fuzzy, Fuzzy,
} }
pub struct NuMatcher<T> { pub struct NuMatcher<'a, T> {
options: CompletionOptions, options: &'a CompletionOptions,
needle: String, needle: String,
state: State<T>, state: State<T>,
} }
@ -45,11 +45,11 @@ enum State<T> {
} }
/// Filters and sorts suggestions /// Filters and sorts suggestions
impl<T> NuMatcher<T> { impl<T> NuMatcher<'_, T> {
/// # Arguments /// # Arguments
/// ///
/// * `needle` - The text to search for /// * `needle` - The text to search for
pub fn new(needle: impl AsRef<str>, options: CompletionOptions) -> NuMatcher<T> { pub fn new(needle: impl AsRef<str>, options: &CompletionOptions) -> NuMatcher<T> {
let needle = trim_quotes_str(needle.as_ref()); let needle = trim_quotes_str(needle.as_ref());
match options.match_algorithm { match options.match_algorithm {
MatchAlgorithm::Prefix => { MatchAlgorithm::Prefix => {
@ -184,7 +184,7 @@ impl<T> NuMatcher<T> {
} }
} }
impl NuMatcher<SemanticSuggestion> { impl NuMatcher<'_, SemanticSuggestion> {
pub fn add_semantic_suggestion(&mut self, sugg: SemanticSuggestion) -> bool { pub fn add_semantic_suggestion(&mut self, sugg: SemanticSuggestion) -> bool {
let value = sugg.suggestion.value.to_string(); let value = sugg.suggestion.value.to_string();
self.add(value, sugg) self.add(value, sugg)
@ -271,7 +271,7 @@ mod test {
match_algorithm, match_algorithm,
..Default::default() ..Default::default()
}; };
let mut matcher = NuMatcher::new(needle, options); let mut matcher = NuMatcher::new(needle, &options);
matcher.add(haystack, haystack); matcher.add(haystack, haystack);
if should_match { if should_match {
assert_eq!(vec![haystack], matcher.results()); assert_eq!(vec![haystack], matcher.results());
@ -286,7 +286,7 @@ mod test {
match_algorithm: MatchAlgorithm::Fuzzy, match_algorithm: MatchAlgorithm::Fuzzy,
..Default::default() ..Default::default()
}; };
let mut matcher = NuMatcher::new("fob", options); let mut matcher = NuMatcher::new("fob", &options);
for item in ["foo/bar", "fob", "foo bar"] { for item in ["foo/bar", "fob", "foo bar"] {
matcher.add(item, item); matcher.add(item, item);
} }
@ -300,7 +300,7 @@ mod test {
match_algorithm: MatchAlgorithm::Fuzzy, match_algorithm: MatchAlgorithm::Fuzzy,
..Default::default() ..Default::default()
}; };
let mut matcher = NuMatcher::new("'love spaces' ", options); let mut matcher = NuMatcher::new("'love spaces' ", &options);
for item in [ for item in [
"'i love spaces'", "'i love spaces'",
"'i love spaces' so much", "'i love spaces' so much",

View File

@ -13,18 +13,18 @@ use std::collections::HashMap;
use super::completion_options::NuMatcher; use super::completion_options::NuMatcher;
pub struct CustomCompletion<T: Completer> { pub struct CustomCompletion<T: Completer> {
stack: Stack,
decl_id: DeclId, decl_id: DeclId,
line: String, line: String,
line_pos: usize,
fallback: T, fallback: T,
} }
impl<T: Completer> CustomCompletion<T> { impl<T: Completer> CustomCompletion<T> {
pub fn new(stack: Stack, decl_id: DeclId, line: String, fallback: T) -> Self { pub fn new(decl_id: DeclId, line: String, line_pos: usize, fallback: T) -> Self {
Self { Self {
stack,
decl_id, decl_id,
line, line,
line_pos,
fallback, fallback,
} }
} }
@ -35,19 +35,16 @@ impl<T: Completer> Completer for CustomCompletion<T> {
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
stack: &Stack, stack: &Stack,
prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
pos: usize,
orig_options: &CompletionOptions, orig_options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
// Line position
let line_pos = pos - offset;
// Call custom declaration // Call custom declaration
let mut stack_mut = stack.clone();
let result = eval_call::<WithoutDebug>( let result = eval_call::<WithoutDebug>(
working_set.permanent_state, working_set.permanent_state,
&mut self.stack, &mut stack_mut,
&Call { &Call {
decl_id: self.decl_id, decl_id: self.decl_id,
head: span, head: span,
@ -58,7 +55,7 @@ impl<T: Completer> Completer for CustomCompletion<T> {
Type::String, Type::String,
)), )),
Argument::Positional(Expression::new_unknown( Argument::Positional(Expression::new_unknown(
Expr::Int(line_pos as i64), Expr::Int(self.line_pos as i64),
Span::unknown(), Span::unknown(),
Type::Int, Type::Int,
)), )),
@ -120,7 +117,6 @@ impl<T: Completer> Completer for CustomCompletion<T> {
prefix, prefix,
span, span,
offset, offset,
pos,
orig_options, orig_options,
); );
} }
@ -138,7 +134,7 @@ impl<T: Completer> Completer for CustomCompletion<T> {
} }
}; };
let mut matcher = NuMatcher::new(String::from_utf8_lossy(prefix), completion_options); let mut matcher = NuMatcher::new(prefix, &completion_options);
if should_sort { if should_sort {
for sugg in suggestions { for sugg in suggestions {

View File

@ -9,29 +9,22 @@ use nu_protocol::{
use reedline::Suggestion; use reedline::Suggestion;
use std::path::Path; use std::path::Path;
use super::{completion_common::FileSuggestion, SemanticSuggestion}; use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
#[derive(Clone, Default)] pub struct DirectoryCompletion;
pub struct DirectoryCompletion {}
impl DirectoryCompletion {
pub fn new() -> Self {
Self::default()
}
}
impl Completer for DirectoryCompletion { impl Completer for DirectoryCompletion {
fn fetch( fn fetch(
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
stack: &Stack, stack: &Stack,
prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
_pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
let AdjustView { prefix, span, .. } = adjust_if_intermediate(prefix, working_set, span); let AdjustView { prefix, span, .. } =
adjust_if_intermediate(prefix.as_ref(), working_set, span);
// Filter only the folders // Filter only the folders
#[allow(deprecated)] #[allow(deprecated)]
@ -54,8 +47,7 @@ impl Completer for DirectoryCompletion {
}, },
..Suggestion::default() ..Suggestion::default()
}, },
// TODO???? kind: Some(SuggestionKind::Directory),
kind: None,
}) })
.collect(); .collect();

View File

@ -5,75 +5,99 @@ use nu_protocol::{
Span, Span,
}; };
use reedline::Suggestion; use reedline::Suggestion;
use std::path::{is_separator, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR}; use std::{
collections::HashSet,
path::{is_separator, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
};
use super::{SemanticSuggestion, SuggestionKind}; use super::{SemanticSuggestion, SuggestionKind};
#[derive(Clone, Default)] pub struct DotNuCompletion;
pub struct DotNuCompletion {}
impl DotNuCompletion {
pub fn new() -> Self {
Self::default()
}
}
impl Completer for DotNuCompletion { impl Completer for DotNuCompletion {
fn fetch( fn fetch(
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
stack: &Stack, stack: &Stack,
prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
_pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(prefix); let prefix_str = prefix.as_ref();
let start_with_backquote = prefix_str.starts_with('`'); let start_with_backquote = prefix_str.starts_with('`');
let end_with_backquote = prefix_str.ends_with('`'); let end_with_backquote = prefix_str.ends_with('`');
let prefix_str = prefix_str.replace('`', ""); let prefix_str = prefix_str.replace('`', "");
// e.g. `./`, `..\`, `/`
let not_lib_dirs = prefix_str
.chars()
.find(|c| *c != '.')
.is_some_and(is_separator);
let mut search_dirs: Vec<PathBuf> = vec![]; let mut search_dirs: Vec<PathBuf> = vec![];
// If prefix_str is only a word we want to search in the current dir let (base, partial) = if let Some((parent, remain)) = prefix_str.rsplit_once(is_separator) {
let (base, partial) = prefix_str // If prefix_str is only a word we want to search in the current dir.
.rsplit_once(is_separator) // "/xx" should be split to "/" and "xx".
.unwrap_or((".", &prefix_str)); if parent.is_empty() {
(MAIN_SEPARATOR_STR, remain)
} else {
(parent, remain)
}
} else {
(".", prefix_str.as_str())
};
let base_dir = base.replace(is_separator, MAIN_SEPARATOR_STR); let base_dir = base.replace(is_separator, MAIN_SEPARATOR_STR);
// Fetch the lib dirs // Fetch the lib dirs
let lib_dirs: Vec<PathBuf> = working_set // NOTE: 2 ways to setup `NU_LIB_DIRS`
// 1. `const NU_LIB_DIRS = [paths]`, equal to `nu -I paths`
// 2. `$env.NU_LIB_DIRS = [paths]`
let const_lib_dirs = working_set
.find_variable(b"$NU_LIB_DIRS") .find_variable(b"$NU_LIB_DIRS")
.and_then(|vid| working_set.get_variable(vid).const_val.as_ref()) .and_then(|vid| working_set.get_variable(vid).const_val.as_ref());
.or(working_set.get_env_var("NU_LIB_DIRS")) let env_lib_dirs = working_set.get_env_var("NU_LIB_DIRS");
.map(|lib_dirs| { let lib_dirs: HashSet<PathBuf> = [const_lib_dirs, env_lib_dirs]
.into_iter()
.flatten()
.flat_map(|lib_dirs| {
lib_dirs lib_dirs
.as_list() .as_list()
.into_iter() .into_iter()
.flat_map(|it| it.iter().filter_map(|x| x.to_path().ok())) .flat_map(|it| it.iter().filter_map(|x| x.to_path().ok()))
.map(expand_tilde) .map(expand_tilde)
.collect()
}) })
.unwrap_or_default(); .collect();
// Check if the base_dir is a folder // Check if the base_dir is a folder
// rsplit_once removes the separator
let cwd = working_set.permanent_state.cwd(None); let cwd = working_set.permanent_state.cwd(None);
if base_dir != "." { if base_dir != "." {
// Search in base_dir as well as lib_dirs let expanded_base_dir = expand_tilde(&base_dir);
let is_base_dir_relative = expanded_base_dir.is_relative();
// Search in base_dir as well as lib_dirs.
// After expanded, base_dir can be a relative path or absolute path.
// If relative, we join "current working dir" with it to get subdirectory and add to search_dirs.
// If absolute, we add it to search_dirs.
if let Ok(mut cwd) = cwd { if let Ok(mut cwd) = cwd {
cwd.push(&base_dir); if is_base_dir_relative {
search_dirs.push(cwd.into_std_path_buf()); cwd.push(&base_dir);
search_dirs.push(cwd.into_std_path_buf());
} else {
search_dirs.push(expanded_base_dir);
}
}
if !not_lib_dirs {
search_dirs.extend(lib_dirs.into_iter().map(|mut dir| {
dir.push(&base_dir);
dir
}));
} }
search_dirs.extend(lib_dirs.into_iter().map(|mut dir| {
dir.push(&base_dir);
dir
}));
} else { } else {
if let Ok(cwd) = cwd { if let Ok(cwd) = cwd {
search_dirs.push(cwd.into_std_path_buf()); search_dirs.push(cwd.into_std_path_buf());
} }
search_dirs.extend(lib_dirs); if !not_lib_dirs {
search_dirs.extend(lib_dirs);
}
} }
// Fetch the files filtering the ones that ends with .nu // Fetch the files filtering the ones that ends with .nu
@ -104,7 +128,9 @@ impl Completer for DotNuCompletion {
let mut span_offset = 0; let mut span_offset = 0;
let mut value = x.path.to_string(); let mut value = x.path.to_string();
// Complete only the last path component // Complete only the last path component
if base_dir != "." { if base_dir == MAIN_SEPARATOR_STR {
span_offset = base_dir.len()
} else if base_dir != "." {
span_offset = base_dir.len() + 1 span_offset = base_dir.len() + 1
} }
// Retain only one '`' // Retain only one '`'

View File

@ -9,33 +9,25 @@ use nu_protocol::{
use reedline::Suggestion; use reedline::Suggestion;
use std::path::Path; use std::path::Path;
use super::{completion_common::FileSuggestion, SemanticSuggestion}; use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
#[derive(Clone, Default)] pub struct FileCompletion;
pub struct FileCompletion {}
impl FileCompletion {
pub fn new() -> Self {
Self::default()
}
}
impl Completer for FileCompletion { impl Completer for FileCompletion {
fn fetch( fn fetch(
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
stack: &Stack, stack: &Stack,
prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
_pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
let AdjustView { let AdjustView {
prefix, prefix,
span, span,
readjusted, readjusted,
} = adjust_if_intermediate(prefix, working_set, span); } = adjust_if_intermediate(prefix.as_ref(), working_set, span);
#[allow(deprecated)] #[allow(deprecated)]
let items: Vec<_> = complete_item( let items: Vec<_> = complete_item(
@ -58,8 +50,11 @@ impl Completer for FileCompletion {
}, },
..Suggestion::default() ..Suggestion::default()
}, },
// TODO???? kind: Some(if x.is_dir {
kind: None, SuggestionKind::Directory
} else {
SuggestionKind::File
}),
}) })
.collect(); .collect();

View File

@ -1,22 +1,15 @@
use crate::completions::{completion_options::NuMatcher, Completer, CompletionOptions}; use crate::completions::{completion_options::NuMatcher, Completer, CompletionOptions};
use nu_protocol::{ use nu_protocol::{
ast::{Expr, Expression},
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
Span, DeclId, Span,
}; };
use reedline::Suggestion; use reedline::Suggestion;
use super::SemanticSuggestion; use super::{SemanticSuggestion, SuggestionKind};
#[derive(Clone)] #[derive(Clone)]
pub struct FlagCompletion { pub struct FlagCompletion {
expression: Expression, pub decl_id: DeclId,
}
impl FlagCompletion {
pub fn new(expression: Expression) -> Self {
Self { expression }
}
} }
impl Completer for FlagCompletion { impl Completer for FlagCompletion {
@ -24,69 +17,42 @@ impl Completer for FlagCompletion {
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
_stack: &Stack, _stack: &Stack,
prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
_pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
// Check if it's a flag let mut matcher = NuMatcher::new(prefix, options);
if let Expr::Call(call) = &self.expression.expr { let mut add_suggestion = |value: String, description: String| {
let decl = working_set.get_decl(call.decl_id); matcher.add_semantic_suggestion(SemanticSuggestion {
let sig = decl.signature(); suggestion: Suggestion {
value,
let mut matcher = NuMatcher::new(String::from_utf8_lossy(prefix), options.clone()); description: Some(description),
span: reedline::Span {
for named in &sig.named { start: span.start - offset,
let flag_desc = &named.desc; end: span.end - offset,
if let Some(short) = named.short {
let mut named = vec![0; short.len_utf8()];
short.encode_utf8(&mut named);
named.insert(0, b'-');
matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion {
value: String::from_utf8_lossy(&named).to_string(),
description: Some(flag_desc.to_string()),
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true,
..Suggestion::default()
},
// TODO????
kind: None,
});
}
if named.long.is_empty() {
continue;
}
let mut named = named.long.as_bytes().to_vec();
named.insert(0, b'-');
named.insert(0, b'-');
matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion {
value: String::from_utf8_lossy(&named).to_string(),
description: Some(flag_desc.to_string()),
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true,
..Suggestion::default()
}, },
// TODO???? append_whitespace: true,
kind: None, ..Suggestion::default()
}); },
kind: Some(SuggestionKind::Flag),
});
};
let decl = working_set.get_decl(self.decl_id);
let sig = decl.signature();
for named in &sig.named {
if let Some(short) = named.short {
let mut name = String::from("-");
name.push(short);
add_suggestion(name, named.desc.clone());
} }
return matcher.results(); if named.long.is_empty() {
continue;
}
add_suggestion(format!("--{}", named.long), named.desc.clone());
} }
matcher.results()
vec![]
} }
} }

View File

@ -1,4 +1,6 @@
mod attribute_completions;
mod base; mod base;
mod cell_path_completions;
mod command_completions; mod command_completions;
mod completer; mod completer;
mod completion_common; mod completion_common;
@ -11,7 +13,9 @@ mod flag_completions;
mod operator_completions; mod operator_completions;
mod variable_completions; mod variable_completions;
pub use attribute_completions::{AttributableCompletion, AttributeCompletion};
pub use base::{Completer, SemanticSuggestion, SuggestionKind}; pub use base::{Completer, SemanticSuggestion, SuggestionKind};
pub use cell_path_completions::CellPathCompletion;
pub use command_completions::CommandCompletion; pub use command_completions::CommandCompletion;
pub use completer::NuCompleter; pub use completer::NuCompleter;
pub use completion_options::{CompletionOptions, MatchAlgorithm}; pub use completion_options::{CompletionOptions, MatchAlgorithm};

View File

@ -2,169 +2,276 @@ use crate::completions::{
completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind, completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
}; };
use nu_protocol::{ use nu_protocol::{
ast::{Expr, Expression}, ast::{self, Comparison, Expr, Expression},
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
Span, Type, Span, Type, Value, ENV_VARIABLE_ID,
}; };
use reedline::Suggestion; use reedline::Suggestion;
use strum::{EnumMessage, IntoEnumIterator};
use super::cell_path_completions::eval_cell_path;
#[derive(Clone)] #[derive(Clone)]
pub struct OperatorCompletion { pub struct OperatorCompletion<'a> {
previous_expr: Expression, pub left_hand_side: &'a Expression,
} }
impl OperatorCompletion { struct OperatorItem {
pub fn new(previous_expr: Expression) -> Self { pub symbols: String,
OperatorCompletion { previous_expr } pub description: String,
}
fn operator_to_item<T: EnumMessage + AsRef<str>>(op: T) -> OperatorItem {
OperatorItem {
symbols: op.as_ref().into(),
description: op.get_message().unwrap_or_default().into(),
} }
} }
impl Completer for OperatorCompletion { fn common_comparison_ops() -> Vec<OperatorItem> {
vec![
operator_to_item(Comparison::In),
operator_to_item(Comparison::NotIn),
operator_to_item(Comparison::Equal),
operator_to_item(Comparison::NotEqual),
]
}
fn all_ops_for_immutable() -> Vec<OperatorItem> {
ast::Comparison::iter()
.map(operator_to_item)
.chain(ast::Math::iter().map(operator_to_item))
.chain(ast::Boolean::iter().map(operator_to_item))
.chain(ast::Bits::iter().map(operator_to_item))
.collect()
}
fn collection_comparison_ops() -> Vec<OperatorItem> {
let mut ops = common_comparison_ops();
ops.push(operator_to_item(Comparison::Has));
ops.push(operator_to_item(Comparison::NotHas));
ops
}
fn number_comparison_ops() -> Vec<OperatorItem> {
Comparison::iter()
.filter(|op| {
!matches!(
op,
Comparison::RegexMatch
| Comparison::NotRegexMatch
| Comparison::StartsWith
| Comparison::EndsWith
| Comparison::Has
| Comparison::NotHas
)
})
.map(operator_to_item)
.collect()
}
fn math_ops() -> Vec<OperatorItem> {
ast::Math::iter()
.filter(|op| !matches!(op, ast::Math::Concatenate | ast::Math::Pow))
.map(operator_to_item)
.collect()
}
fn bit_ops() -> Vec<OperatorItem> {
ast::Bits::iter().map(operator_to_item).collect()
}
fn all_assignment_ops() -> Vec<OperatorItem> {
ast::Assignment::iter().map(operator_to_item).collect()
}
fn numeric_assignment_ops() -> Vec<OperatorItem> {
ast::Assignment::iter()
.filter(|op| !matches!(op, ast::Assignment::ConcatenateAssign))
.map(operator_to_item)
.collect()
}
fn concat_assignment_ops() -> Vec<OperatorItem> {
vec![
operator_to_item(ast::Assignment::Assign),
operator_to_item(ast::Assignment::ConcatenateAssign),
]
}
fn valid_int_ops() -> Vec<OperatorItem> {
let mut ops = valid_float_ops();
ops.extend(bit_ops());
ops
}
fn valid_float_ops() -> Vec<OperatorItem> {
let mut ops = valid_value_with_unit_ops();
ops.push(operator_to_item(ast::Math::Pow));
ops
}
fn valid_string_ops() -> Vec<OperatorItem> {
let mut ops: Vec<OperatorItem> = Comparison::iter().map(operator_to_item).collect();
ops.push(operator_to_item(ast::Math::Concatenate));
ops.push(OperatorItem {
symbols: "like".into(),
description: Comparison::RegexMatch
.get_message()
.unwrap_or_default()
.into(),
});
ops.push(OperatorItem {
symbols: "not-like".into(),
description: Comparison::NotRegexMatch
.get_message()
.unwrap_or_default()
.into(),
});
ops
}
fn valid_list_ops() -> Vec<OperatorItem> {
let mut ops = collection_comparison_ops();
ops.push(operator_to_item(ast::Math::Concatenate));
ops
}
fn valid_binary_ops() -> Vec<OperatorItem> {
let mut ops = number_comparison_ops();
ops.extend(bit_ops());
ops.push(operator_to_item(ast::Math::Concatenate));
ops
}
fn valid_bool_ops() -> Vec<OperatorItem> {
let mut ops: Vec<OperatorItem> = ast::Boolean::iter().map(operator_to_item).collect();
ops.extend(common_comparison_ops());
ops
}
fn valid_value_with_unit_ops() -> Vec<OperatorItem> {
let mut ops = number_comparison_ops();
ops.extend(math_ops());
ops
}
fn ops_by_value(value: &Value, mutable: bool) -> Vec<OperatorItem> {
let mut ops = match value {
Value::Int { .. } => valid_int_ops(),
Value::Float { .. } => valid_float_ops(),
Value::String { .. } => valid_string_ops(),
Value::Binary { .. } => valid_binary_ops(),
Value::Bool { .. } => valid_bool_ops(),
Value::Date { .. } => number_comparison_ops(),
Value::Filesize { .. } | Value::Duration { .. } => valid_value_with_unit_ops(),
Value::Range { .. } | Value::Record { .. } => collection_comparison_ops(),
Value::List { .. } => valid_list_ops(),
_ => all_ops_for_immutable(),
};
if mutable {
ops.extend(match value {
Value::Int { .. }
| Value::Float { .. }
| Value::Filesize { .. }
| Value::Duration { .. } => numeric_assignment_ops(),
Value::String { .. } | Value::Binary { .. } | Value::List { .. } => {
concat_assignment_ops()
}
Value::Bool { .. }
| Value::Date { .. }
| Value::Range { .. }
| Value::Record { .. } => vec![operator_to_item(ast::Assignment::Assign)],
_ => all_assignment_ops(),
})
}
ops
}
fn is_expression_mutable(expr: &Expr, working_set: &StateWorkingSet) -> bool {
let Expr::FullCellPath(path) = expr else {
return false;
};
let Expr::Var(id) = path.head.expr else {
return false;
};
if id == ENV_VARIABLE_ID {
return true;
}
let var = working_set.get_variable(id);
var.mutable
}
impl Completer for OperatorCompletion<'_> {
fn fetch( fn fetch(
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
_stack: &Stack, stack: &Stack,
_prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
_pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
//Check if int, float, or string let mut needs_assignment_ops = true;
let partial = std::str::from_utf8(working_set.get_span_contents(span)).unwrap_or(""); // Complete according expression type
let op = match &self.previous_expr.expr { // TODO: type inference on self.left_hand_side to get more accurate completions
Expr::BinaryOp(x, _, _) => &x.expr, let mut possible_operations: Vec<OperatorItem> = match &self.left_hand_side.ty {
_ => { Type::Int | Type::Number => valid_int_ops(),
return vec![]; Type::Float => valid_float_ops(),
} Type::String => valid_string_ops(),
}; Type::Binary => valid_binary_ops(),
let possible_operations = match op { Type::Bool => valid_bool_ops(),
Expr::Int(_) => vec![ Type::Date => number_comparison_ops(),
("+", "Add (Plus)"), Type::Filesize | Type::Duration => valid_value_with_unit_ops(),
("-", "Subtract (Minus)"), Type::Record(_) | Type::Range => collection_comparison_ops(),
("*", "Multiply"), Type::List(_) | Type::Table(_) => valid_list_ops(),
("/", "Divide"), // Unknown type, resort to evaluated values
("==", "Equal to"), Type::Any => match &self.left_hand_side.expr {
("!=", "Not equal to"), Expr::FullCellPath(path) => {
("//", "Floor division"), // for `$ <tab>`
("<", "Less than"), if matches!(path.head.expr, Expr::Garbage) {
(">", "Greater than"), return vec![];
("<=", "Less than or equal to"), }
(">=", "Greater than or equal to"), let value =
("mod", "Floor division remainder (Modulo)"), eval_cell_path(working_set, stack, &path.head, &path.tail, path.head.span)
("**", "Power of"), .unwrap_or_default();
("bit-or", "Bitwise OR"), let mutable = is_expression_mutable(&self.left_hand_side.expr, working_set);
("bit-xor", "Bitwise exclusive OR"), // to avoid duplication
("bit-and", "Bitwise AND"), needs_assignment_ops = false;
("bit-shl", "Bitwise shift left"), ops_by_value(&value, mutable)
("bit-shr", "Bitwise shift right"), }
("in", "Is a member of (doesn't use regex)"), _ => all_ops_for_immutable(),
("not-in", "Is not a member of (doesn't use regex)"),
],
Expr::String(_) => vec![
("=~", "Contains regex match"),
("like", "Contains regex match"),
("!~", "Does not contain regex match"),
("not-like", "Does not contain regex match"),
(
"++",
"Concatenates two lists, two strings, or two binary values",
),
("in", "Is a member of (doesn't use regex)"),
("not-in", "Is not a member of (doesn't use regex)"),
("starts-with", "Starts with"),
("ends-with", "Ends with"),
],
Expr::Float(_) => vec![
("+", "Add (Plus)"),
("-", "Subtract (Minus)"),
("*", "Multiply"),
("/", "Divide"),
("==", "Equal to"),
("!=", "Not equal to"),
("//", "Floor division"),
("<", "Less than"),
(">", "Greater than"),
("<=", "Less than or equal to"),
(">=", "Greater than or equal to"),
("mod", "Floor division remainder (Modulo)"),
("**", "Power of"),
("in", "Is a member of (doesn't use regex)"),
("not-in", "Is not a member of (doesn't use regex)"),
],
Expr::Bool(_) => vec![
(
"and",
"Both values are true (short-circuits when first value is false)",
),
(
"or",
"Either value is true (short-circuits when first value is true)",
),
("xor", "One value is true and the other is false"),
("not", "Negates a value or expression"),
("in", "Is a member of (doesn't use regex)"),
("not-in", "Is not a member of (doesn't use regex)"),
],
Expr::FullCellPath(path) => match path.head.expr {
Expr::List(_) => vec![
(
"++",
"Concatenates two lists, two strings, or two binary values",
),
("has", "Contains a value of (doesn't use regex)"),
("not-has", "Does not contain a value of (doesn't use regex)"),
],
Expr::Var(id) => get_variable_completions(id, working_set),
_ => vec![],
}, },
_ => vec![], _ => common_comparison_ops(),
}; };
// If the left hand side is a variable, add assignment operators if mutable
if needs_assignment_ops && is_expression_mutable(&self.left_hand_side.expr, working_set) {
possible_operations.extend(match &self.left_hand_side.ty {
Type::Int | Type::Float | Type::Number => numeric_assignment_ops(),
Type::Filesize | Type::Duration => numeric_assignment_ops(),
Type::String | Type::Binary | Type::List(_) => concat_assignment_ops(),
Type::Any => all_assignment_ops(),
_ => vec![operator_to_item(ast::Assignment::Assign)],
});
}
let mut matcher = NuMatcher::new(partial, options.clone()); let mut matcher = NuMatcher::new(prefix, options);
for (symbol, desc) in possible_operations.into_iter() { for OperatorItem {
symbols,
description,
} in possible_operations
{
matcher.add_semantic_suggestion(SemanticSuggestion { matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: symbol.to_string(), value: symbols.to_owned(),
description: Some(desc.to_string()), description: Some(description.to_owned()),
span: reedline::Span::new(span.start - offset, span.end - offset), span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true, append_whitespace: true,
..Suggestion::default() ..Suggestion::default()
}, },
kind: Some(SuggestionKind::Command( kind: Some(SuggestionKind::Operator),
nu_protocol::engine::CommandType::Builtin,
)),
}); });
} }
matcher.results() matcher.results()
} }
} }
pub fn get_variable_completions<'a>(
id: nu_protocol::Id<nu_protocol::marker::Var>,
working_set: &StateWorkingSet,
) -> Vec<(&'a str, &'a str)> {
let var = working_set.get_variable(id);
if !var.mutable {
return vec![];
}
match var.ty {
Type::List(_) | Type::String | Type::Binary => vec![
(
"++=",
"Concatenates two lists, two strings, or two binary values",
),
("=", "Assigns a value to a variable."),
],
Type::Int | Type::Float => vec![
("=", "Assigns a value to a variable."),
("+=", "Adds a value to a variable."),
("-=", "Subtracts a value from a variable."),
("*=", "Multiplies a variable by a value"),
("/=", "Divides a variable by a value."),
],
_ => vec![],
}
}

View File

@ -1,157 +1,67 @@
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind}; use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
use nu_engine::{column::get_columns, eval_variable};
use nu_protocol::{ use nu_protocol::{
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
Span, Value, Span, VarId,
}; };
use reedline::Suggestion; use reedline::Suggestion;
use std::str;
use super::completion_options::NuMatcher; use super::completion_options::NuMatcher;
#[derive(Clone)] pub struct VariableCompletion;
pub struct VariableCompletion {
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
}
impl VariableCompletion {
pub fn new(var_context: (Vec<u8>, Vec<Vec<u8>>)) -> Self {
Self { var_context }
}
}
impl Completer for VariableCompletion { impl Completer for VariableCompletion {
fn fetch( fn fetch(
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
stack: &Stack, _stack: &Stack,
prefix: &[u8], prefix: impl AsRef<str>,
span: Span, span: Span,
offset: usize, offset: usize,
_pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
let builtins = ["$nu", "$in", "$env"]; let mut matcher = NuMatcher::new(prefix, options);
let var_str = std::str::from_utf8(&self.var_context.0).unwrap_or("");
let var_id = working_set.find_variable(&self.var_context.0);
let current_span = reedline::Span { let current_span = reedline::Span {
start: span.start - offset, start: span.start - offset,
end: span.end - offset, end: span.end - offset,
}; };
let sublevels_count = self.var_context.1.len();
let prefix_str = String::from_utf8_lossy(prefix);
let mut matcher = NuMatcher::new(prefix_str, options.clone());
// Completions for the given variable
if !var_str.is_empty() {
// Completion for $env.<tab>
if var_str == "$env" {
let env_vars = stack.get_env_vars(working_set.permanent_state);
// Return nested values
if sublevels_count > 0 {
// Extract the target var ($env.<target-var>)
let target_var = self.var_context.1[0].clone();
let target_var_str =
str::from_utf8(&target_var).unwrap_or_default().to_string();
// Everything after the target var is the nested level ($env.<target-var>.<nested_levels>...)
let nested_levels: Vec<Vec<u8>> =
self.var_context.1.clone().into_iter().skip(1).collect();
if let Some(val) = env_vars.get(&target_var_str) {
for suggestion in nested_suggestions(val, &nested_levels, current_span) {
matcher.add_semantic_suggestion(suggestion);
}
return matcher.results();
}
} else {
// No nesting provided, return all env vars
for env_var in env_vars {
matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion {
value: env_var.0,
span: current_span,
..Suggestion::default()
},
kind: Some(SuggestionKind::Type(env_var.1.get_type())),
});
}
return matcher.results();
}
}
// Completions for $nu.<tab>
if var_str == "$nu" {
// Eval nu var
if let Ok(nuval) = eval_variable(
working_set.permanent_state,
stack,
nu_protocol::NU_VARIABLE_ID,
nu_protocol::Span::new(current_span.start, current_span.end),
) {
for suggestion in nested_suggestions(&nuval, &self.var_context.1, current_span)
{
matcher.add_semantic_suggestion(suggestion);
}
return matcher.results();
}
}
// Completion other variable types
if let Some(var_id) = var_id {
// Extract the variable value from the stack
let var = stack.get_var(var_id, Span::new(span.start, span.end));
// If the value exists and it's of type Record
if let Ok(value) = var {
for suggestion in nested_suggestions(&value, &self.var_context.1, current_span)
{
matcher.add_semantic_suggestion(suggestion);
}
return matcher.results();
}
}
}
// Variable completion (e.g: $en<tab> to complete $env) // Variable completion (e.g: $en<tab> to complete $env)
let builtins = ["$nu", "$in", "$env"];
for builtin in builtins { for builtin in builtins {
matcher.add_semantic_suggestion(SemanticSuggestion { matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: builtin.to_string(), value: builtin.to_string(),
span: current_span, span: current_span,
description: Some("reserved".into()),
..Suggestion::default() ..Suggestion::default()
}, },
// TODO is there a way to get the VarId to get the type??? kind: Some(SuggestionKind::Variable),
kind: None,
}); });
} }
let mut add_candidate = |name, var_id: &VarId| {
matcher.add_semantic_suggestion(SemanticSuggestion {
suggestion: Suggestion {
value: String::from_utf8_lossy(name).to_string(),
span: current_span,
description: Some(working_set.get_variable(*var_id).ty.to_string()),
..Suggestion::default()
},
kind: Some(SuggestionKind::Variable),
})
};
// TODO: The following can be refactored (see find_commands_by_predicate() used in // TODO: The following can be refactored (see find_commands_by_predicate() used in
// command_completions). // command_completions).
let mut removed_overlays = vec![]; let mut removed_overlays = vec![];
// Working set scope vars // Working set scope vars
for scope_frame in working_set.delta.scope.iter().rev() { for scope_frame in working_set.delta.scope.iter().rev() {
for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() {
for v in &overlay_frame.vars { for (name, var_id) in &overlay_frame.vars {
matcher.add_semantic_suggestion(SemanticSuggestion { add_candidate(name, var_id);
suggestion: Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
span: current_span,
..Suggestion::default()
},
kind: Some(SuggestionKind::Type(
working_set.get_variable(*v.1).ty.clone(),
)),
});
} }
} }
} }
// Permanent state vars // Permanent state vars
// for scope in &self.engine_state.scope { // for scope in &self.engine_state.scope {
for overlay_frame in working_set for overlay_frame in working_set
@ -159,98 +69,11 @@ impl Completer for VariableCompletion {
.active_overlays(&removed_overlays) .active_overlays(&removed_overlays)
.rev() .rev()
{ {
for v in &overlay_frame.vars { for (name, var_id) in &overlay_frame.vars {
matcher.add_semantic_suggestion(SemanticSuggestion { add_candidate(name, var_id);
suggestion: Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
span: current_span,
..Suggestion::default()
},
kind: Some(SuggestionKind::Type(
working_set.get_variable(*v.1).ty.clone(),
)),
});
} }
} }
matcher.results() matcher.results()
} }
} }
// Find recursively the values for sublevels
// if no sublevels are set it returns the current value
fn nested_suggestions(
val: &Value,
sublevels: &[Vec<u8>],
current_span: reedline::Span,
) -> Vec<SemanticSuggestion> {
let mut output: Vec<SemanticSuggestion> = vec![];
let value = recursive_value(val, sublevels).unwrap_or_else(Value::nothing);
let kind = SuggestionKind::Type(value.get_type());
match value {
Value::Record { val, .. } => {
// Add all the columns as completion
for col in val.columns() {
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: col.clone(),
span: current_span,
..Suggestion::default()
},
kind: Some(kind.clone()),
});
}
output
}
Value::List { vals, .. } => {
for column_name in get_columns(vals.as_slice()) {
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: column_name,
span: current_span,
..Suggestion::default()
},
kind: Some(kind.clone()),
});
}
output
}
_ => output,
}
}
// Extracts the recursive value (e.g: $var.a.b.c)
fn recursive_value(val: &Value, sublevels: &[Vec<u8>]) -> Result<Value, Span> {
// Go to next sublevel
if let Some((sublevel, next_sublevels)) = sublevels.split_first() {
let span = val.span();
match val {
Value::Record { val, .. } => {
if let Some((_, value)) = val.iter().find(|(key, _)| key.as_bytes() == sublevel) {
// If matches try to fetch recursively the next
recursive_value(value, next_sublevels)
} else {
// Current sublevel value not found
Err(span)
}
}
Value::List { vals, .. } => {
for col in get_columns(vals.as_slice()) {
if col.as_bytes() == *sublevel {
let val = val.get_data_by_key(&col).ok_or(span)?;
return recursive_value(&val, next_sublevels);
}
}
// Current sublevel value not found
Err(span)
}
_ => Ok(val.clone()),
}
} else {
Ok(val.clone())
}
}

View File

@ -8,7 +8,7 @@ use nu_protocol::{
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
report_parse_error, report_parse_warning, report_parse_error, report_parse_warning,
shell_error::io::IoError, shell_error::io::*,
PipelineData, ShellError, Span, Value, PipelineData, ShellError, Span, Value,
}; };
use std::{path::PathBuf, sync::Arc}; use std::{path::PathBuf, sync::Arc};
@ -27,11 +27,11 @@ pub fn evaluate_file(
let cwd = engine_state.cwd_as_string(Some(stack))?; let cwd = engine_state.cwd_as_string(Some(stack))?;
let file_path = canonicalize_with(&path, cwd).map_err(|err| { let file_path = canonicalize_with(&path, cwd).map_err(|err| {
IoError::new_with_additional_context( IoError::new_internal_with_path(
err.kind(), err.kind().not_found_as(NotFound::File),
Span::unknown(),
PathBuf::from(&path),
"Could not access file", "Could not access file",
nu_protocol::location!(),
PathBuf::from(&path),
) )
})?; })?;
@ -46,21 +46,21 @@ pub fn evaluate_file(
})?; })?;
let file = std::fs::read(&file_path).map_err(|err| { let file = std::fs::read(&file_path).map_err(|err| {
IoError::new_with_additional_context( IoError::new_internal_with_path(
err.kind(), err.kind().not_found_as(NotFound::File),
Span::unknown(),
file_path.clone(),
"Could not read file", "Could not read file",
nu_protocol::location!(),
file_path.clone(),
) )
})?; })?;
engine_state.file = Some(file_path.clone()); engine_state.file = Some(file_path.clone());
let parent = file_path.parent().ok_or_else(|| { let parent = file_path.parent().ok_or_else(|| {
IoError::new_with_additional_context( IoError::new_internal_with_path(
std::io::ErrorKind::NotFound, ErrorKind::DirectoryNotFound,
Span::unknown(),
file_path.clone(),
"The file path does not have a parent", "The file path does not have a parent",
nu_protocol::location!(),
file_path.clone(),
) )
})?; })?;

View File

@ -740,9 +740,15 @@ fn add_keybinding(
let span = mode.span(); let span = mode.span();
match &mode { match &mode {
Value::String { val, .. } => match val.as_str() { Value::String { val, .. } => match val.as_str() {
"emacs" => add_parsed_keybinding(emacs_keybindings, keybinding, config), str if str.eq_ignore_ascii_case("emacs") => {
"vi_insert" => add_parsed_keybinding(insert_keybindings, keybinding, config), add_parsed_keybinding(emacs_keybindings, keybinding, config)
"vi_normal" => add_parsed_keybinding(normal_keybindings, keybinding, config), }
str if str.eq_ignore_ascii_case("vi_insert") => {
add_parsed_keybinding(insert_keybindings, keybinding, config)
}
str if str.eq_ignore_ascii_case("vi_normal") => {
add_parsed_keybinding(normal_keybindings, keybinding, config)
}
str => Err(ShellError::InvalidValue { str => Err(ShellError::InvalidValue {
valid: "'emacs', 'vi_insert', or 'vi_normal'".into(), valid: "'emacs', 'vi_insert', or 'vi_normal'".into(),
actual: format!("'{str}'"), actual: format!("'{str}'"),
@ -992,41 +998,54 @@ fn event_from_record(
) -> Result<ReedlineEvent, ShellError> { ) -> Result<ReedlineEvent, ShellError> {
let event = match name { let event = match name {
"none" => ReedlineEvent::None, "none" => ReedlineEvent::None,
"clearscreen" => ReedlineEvent::ClearScreen,
"clearscrollback" => ReedlineEvent::ClearScrollback,
"historyhintcomplete" => ReedlineEvent::HistoryHintComplete, "historyhintcomplete" => ReedlineEvent::HistoryHintComplete,
"historyhintwordcomplete" => ReedlineEvent::HistoryHintWordComplete, "historyhintwordcomplete" => ReedlineEvent::HistoryHintWordComplete,
"ctrld" => ReedlineEvent::CtrlD, "ctrld" => ReedlineEvent::CtrlD,
"ctrlc" => ReedlineEvent::CtrlC, "ctrlc" => ReedlineEvent::CtrlC,
"clearscreen" => ReedlineEvent::ClearScreen,
"clearscrollback" => ReedlineEvent::ClearScrollback,
"enter" => ReedlineEvent::Enter, "enter" => ReedlineEvent::Enter,
"submit" => ReedlineEvent::Submit, "submit" => ReedlineEvent::Submit,
"submitornewline" => ReedlineEvent::SubmitOrNewline, "submitornewline" => ReedlineEvent::SubmitOrNewline,
"esc" | "escape" => ReedlineEvent::Esc, "esc" | "escape" => ReedlineEvent::Esc,
// Non-sensical for user configuration:
//
// `ReedlineEvent::Mouse` - itself a no-op
// `ReedlineEvent::Resize` - requires size info specifically from the ANSI resize
// event
//
// Handled above in `parse_event`:
//
// `ReedlineEvent::Edit`
"repaint" => ReedlineEvent::Repaint,
"previoushistory" => ReedlineEvent::PreviousHistory,
"up" => ReedlineEvent::Up, "up" => ReedlineEvent::Up,
"down" => ReedlineEvent::Down, "down" => ReedlineEvent::Down,
"right" => ReedlineEvent::Right, "right" => ReedlineEvent::Right,
"left" => ReedlineEvent::Left, "left" => ReedlineEvent::Left,
"searchhistory" => ReedlineEvent::SearchHistory,
"nexthistory" => ReedlineEvent::NextHistory, "nexthistory" => ReedlineEvent::NextHistory,
"previoushistory" => ReedlineEvent::PreviousHistory, "searchhistory" => ReedlineEvent::SearchHistory,
"repaint" => ReedlineEvent::Repaint, // Handled above in `parse_event`:
"menudown" => ReedlineEvent::MenuDown, //
"menuup" => ReedlineEvent::MenuUp, // `ReedlineEvent::Multiple`
"menuleft" => ReedlineEvent::MenuLeft, // `ReedlineEvent::UntilFound`
"menuright" => ReedlineEvent::MenuRight,
"menunext" => ReedlineEvent::MenuNext,
"menuprevious" => ReedlineEvent::MenuPrevious,
"menupagenext" => ReedlineEvent::MenuPageNext,
"menupageprevious" => ReedlineEvent::MenuPagePrevious,
"openeditor" => ReedlineEvent::OpenEditor,
"menu" => { "menu" => {
let menu = extract_value("name", record, span)?; let menu = extract_value("name", record, span)?;
ReedlineEvent::Menu(menu.to_expanded_string("", config)) ReedlineEvent::Menu(menu.to_expanded_string("", config))
} }
"menunext" => ReedlineEvent::MenuNext,
"menuprevious" => ReedlineEvent::MenuPrevious,
"menuup" => ReedlineEvent::MenuUp,
"menudown" => ReedlineEvent::MenuDown,
"menuleft" => ReedlineEvent::MenuLeft,
"menuright" => ReedlineEvent::MenuRight,
"menupagenext" => ReedlineEvent::MenuPageNext,
"menupageprevious" => ReedlineEvent::MenuPagePrevious,
"executehostcommand" => { "executehostcommand" => {
let cmd = extract_value("cmd", record, span)?; let cmd = extract_value("cmd", record, span)?;
ReedlineEvent::ExecuteHostCommand(cmd.to_expanded_string("", config)) ReedlineEvent::ExecuteHostCommand(cmd.to_expanded_string("", config))
} }
"openeditor" => ReedlineEvent::OpenEditor,
str => { str => {
return Err(ShellError::InvalidValue { return Err(ShellError::InvalidValue {
valid: "a reedline event".into(), valid: "a reedline event".into(),
@ -1056,7 +1075,6 @@ fn edit_from_record(
.and_then(|value| value.as_bool()) .and_then(|value| value.as_bool())
.unwrap_or(false), .unwrap_or(false),
}, },
"movetoend" => EditCommand::MoveToEnd { "movetoend" => EditCommand::MoveToEnd {
select: extract_value("select", record, span) select: extract_value("select", record, span)
.and_then(|value| value.as_bool()) .and_then(|value| value.as_bool())
@ -1092,16 +1110,6 @@ fn edit_from_record(
.and_then(|value| value.as_bool()) .and_then(|value| value.as_bool())
.unwrap_or(false), .unwrap_or(false),
}, },
"movewordrightend" => EditCommand::MoveWordRightEnd {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movebigwordrightend" => EditCommand::MoveBigWordRightEnd {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movewordrightstart" => EditCommand::MoveWordRightStart { "movewordrightstart" => EditCommand::MoveWordRightStart {
select: extract_value("select", record, span) select: extract_value("select", record, span)
.and_then(|value| value.as_bool()) .and_then(|value| value.as_bool())
@ -1112,6 +1120,16 @@ fn edit_from_record(
.and_then(|value| value.as_bool()) .and_then(|value| value.as_bool())
.unwrap_or(false), .unwrap_or(false),
}, },
"movewordrightend" => EditCommand::MoveWordRightEnd {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movebigwordrightend" => EditCommand::MoveBigWordRightEnd {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movetoposition" => { "movetoposition" => {
let value = extract_value("value", record, span)?; let value = extract_value("value", record, span)?;
let select = extract_value("select", record, span) let select = extract_value("select", record, span)
@ -1133,6 +1151,13 @@ fn edit_from_record(
EditCommand::InsertString(value.to_expanded_string("", config)) EditCommand::InsertString(value.to_expanded_string("", config))
} }
"insertnewline" => EditCommand::InsertNewline, "insertnewline" => EditCommand::InsertNewline,
"replacechar" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value)?;
EditCommand::ReplaceChar(char)
}
// `EditCommand::ReplaceChars` - Internal hack not sanely implementable as a
// standalone binding
"backspace" => EditCommand::Backspace, "backspace" => EditCommand::Backspace,
"delete" => EditCommand::Delete, "delete" => EditCommand::Delete,
"cutchar" => EditCommand::CutChar, "cutchar" => EditCommand::CutChar,
@ -1140,6 +1165,7 @@ fn edit_from_record(
"deleteword" => EditCommand::DeleteWord, "deleteword" => EditCommand::DeleteWord,
"clear" => EditCommand::Clear, "clear" => EditCommand::Clear,
"cleartolineend" => EditCommand::ClearToLineEnd, "cleartolineend" => EditCommand::ClearToLineEnd,
"complete" => EditCommand::Complete,
"cutcurrentline" => EditCommand::CutCurrentLine, "cutcurrentline" => EditCommand::CutCurrentLine,
"cutfromstart" => EditCommand::CutFromStart, "cutfromstart" => EditCommand::CutFromStart,
"cutfromlinestart" => EditCommand::CutFromLineStart, "cutfromlinestart" => EditCommand::CutFromLineStart,
@ -1156,6 +1182,7 @@ fn edit_from_record(
"uppercaseword" => EditCommand::UppercaseWord, "uppercaseword" => EditCommand::UppercaseWord,
"lowercaseword" => EditCommand::LowercaseWord, "lowercaseword" => EditCommand::LowercaseWord,
"capitalizechar" => EditCommand::CapitalizeChar, "capitalizechar" => EditCommand::CapitalizeChar,
"switchcasechar" => EditCommand::SwitchcaseChar,
"swapwords" => EditCommand::SwapWords, "swapwords" => EditCommand::SwapWords,
"swapgraphemes" => EditCommand::SwapGraphemes, "swapgraphemes" => EditCommand::SwapGraphemes,
"undo" => EditCommand::Undo, "undo" => EditCommand::Undo,
@ -1212,17 +1239,64 @@ fn edit_from_record(
.unwrap_or(false); .unwrap_or(false);
EditCommand::MoveLeftBefore { c: char, select } EditCommand::MoveLeftBefore { c: char, select }
} }
"complete" => EditCommand::Complete, "selectall" => EditCommand::SelectAll,
"cutselection" => EditCommand::CutSelection, "cutselection" => EditCommand::CutSelection,
"copyselection" => EditCommand::CopySelection,
"paste" => EditCommand::Paste,
"copyfromstart" => EditCommand::CopyFromStart,
"copyfromlinestart" => EditCommand::CopyFromLineStart,
"copytoend" => EditCommand::CopyToEnd,
"copytolineend" => EditCommand::CopyToLineEnd,
"copycurrentline" => EditCommand::CopyCurrentLine,
"copywordleft" => EditCommand::CopyWordLeft,
"copybigwordleft" => EditCommand::CopyBigWordLeft,
"copywordright" => EditCommand::CopyWordRight,
"copybigwordright" => EditCommand::CopyBigWordRight,
"copywordrighttonext" => EditCommand::CopyWordRightToNext,
"copybigwordrighttonext" => EditCommand::CopyBigWordRightToNext,
"copyleft" => EditCommand::CopyLeft,
"copyright" => EditCommand::CopyRight,
"copyrightuntil" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value)?;
EditCommand::CopyRightUntil(char)
}
"copyrightbefore" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value)?;
EditCommand::CopyRightBefore(char)
}
"copyleftuntil" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value)?;
EditCommand::CopyLeftUntil(char)
}
"copyleftbefore" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value)?;
EditCommand::CopyLeftBefore(char)
}
"swapcursorandanchor" => EditCommand::SwapCursorAndAnchor,
#[cfg(feature = "system-clipboard")] #[cfg(feature = "system-clipboard")]
"cutselectionsystem" => EditCommand::CutSelectionSystem, "cutselectionsystem" => EditCommand::CutSelectionSystem,
"copyselection" => EditCommand::CopySelection,
#[cfg(feature = "system-clipboard")] #[cfg(feature = "system-clipboard")]
"copyselectionsystem" => EditCommand::CopySelectionSystem, "copyselectionsystem" => EditCommand::CopySelectionSystem,
"paste" => EditCommand::Paste,
#[cfg(feature = "system-clipboard")] #[cfg(feature = "system-clipboard")]
"pastesystem" => EditCommand::PasteSystem, "pastesystem" => EditCommand::PasteSystem,
"selectall" => EditCommand::SelectAll, "cutinside" => {
let value = extract_value("left", record, span)?;
let left = extract_char(value)?;
let value = extract_value("right", record, span)?;
let right = extract_char(value)?;
EditCommand::CutInside { left, right }
}
"yankinside" => {
let value = extract_value("left", record, span)?;
let left = extract_char(value)?;
let value = extract_value("right", record, span)?;
let right = extract_char(value)?;
EditCommand::YankInside { left, right }
}
str => { str => {
return Err(ShellError::InvalidValue { return Err(ShellError::InvalidValue {
valid: "a reedline EditCommand".into(), valid: "a reedline EditCommand".into(),

View File

@ -20,6 +20,7 @@ use nu_cmd_base::util::get_editor;
use nu_color_config::StyleComputer; use nu_color_config::StyleComputer;
#[allow(deprecated)] #[allow(deprecated)]
use nu_engine::env_to_strings; use nu_engine::env_to_strings;
use nu_engine::exit::cleanup_exit;
use nu_parser::{lex, parse, trim_quotes_str}; use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::shell_error::io::IoError; use nu_protocol::shell_error::io::IoError;
use nu_protocol::{ use nu_protocol::{
@ -36,6 +37,7 @@ use reedline::{
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory, CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
HistorySessionId, Reedline, SqliteBackedHistory, Vi, HistorySessionId, Reedline, SqliteBackedHistory, Vi,
}; };
use std::sync::atomic::Ordering;
use std::{ use std::{
collections::HashMap, collections::HashMap,
env::temp_dir, env::temp_dir,
@ -692,7 +694,11 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
); );
println!(); println!();
return (false, stack, line_editor);
cleanup_exit((), engine_state, 0);
// if cleanup_exit didn't exit, we should keep running
return (true, stack, line_editor);
} }
Err(err) => { Err(err) => {
let message = err.to_string(); let message = err.to_string();
@ -930,6 +936,9 @@ fn do_run_cmd(
trace!("eval source: {}", s); trace!("eval source: {}", s);
let mut cmds = s.split_whitespace(); let mut cmds = s.split_whitespace();
let had_warning_before = engine_state.exit_warning_given.load(Ordering::SeqCst);
if let Some("exit") = cmds.next() { if let Some("exit") = cmds.next() {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
let _ = parse(&mut working_set, None, s.as_bytes(), false); let _ = parse(&mut working_set, None, s.as_bytes(), false);
@ -938,13 +947,11 @@ fn do_run_cmd(
match cmds.next() { match cmds.next() {
Some(s) => { Some(s) => {
if let Ok(n) = s.parse::<i32>() { if let Ok(n) = s.parse::<i32>() {
drop(line_editor); return cleanup_exit(line_editor, engine_state, n);
std::process::exit(n);
} }
} }
None => { None => {
drop(line_editor); return cleanup_exit(line_editor, engine_state, 0);
std::process::exit(0);
} }
} }
} }
@ -963,6 +970,14 @@ fn do_run_cmd(
false, false,
); );
// if there was a warning before, and we got to this point, it means
// the possible call to cleanup_exit did not occur.
if had_warning_before && engine_state.is_interactive {
engine_state
.exit_warning_given
.store(false, Ordering::SeqCst);
}
line_editor line_editor
} }

View File

@ -309,6 +309,7 @@ fn find_matching_block_end_in_expr(
.unwrap_or(expression.span.start); .unwrap_or(expression.span.start);
return match &expression.expr { return match &expression.expr {
// TODO: Can't these be handled with an `_ => None` branch? Refactor
Expr::Bool(_) => None, Expr::Bool(_) => None,
Expr::Int(_) => None, Expr::Int(_) => None,
Expr::Float(_) => None, Expr::Float(_) => None,
@ -335,6 +336,28 @@ fn find_matching_block_end_in_expr(
Expr::Nothing => None, Expr::Nothing => None,
Expr::Garbage => None, Expr::Garbage => None,
Expr::AttributeBlock(ab) => ab
.attributes
.iter()
.find_map(|attr| {
find_matching_block_end_in_expr(
line,
working_set,
&attr.expr,
global_span_offset,
global_cursor_offset,
)
})
.or_else(|| {
find_matching_block_end_in_expr(
line,
working_set,
&ab.item,
global_span_offset,
global_cursor_offset,
)
}),
Expr::Table(table) => { Expr::Table(table) => {
if expr_last == global_cursor_offset { if expr_last == global_cursor_offset {
// cursor is at table end // cursor is at table end

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ fn create_default_context() -> EngineState {
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context()) nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
} }
// creates a new engine with the current path into the completions fixtures folder /// creates a new engine with the current path into the completions fixtures folder
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) { pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("completions"); let dir = fs::fixtures().join("completions");
@ -69,7 +69,26 @@ pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
// creates a new engine with the current path into the completions fixtures folder /// Adds pseudo PATH env for external completion tests
pub fn new_external_engine() -> EngineState {
let mut engine = create_default_context();
let dir = fs::fixtures().join("external_completions").join("path");
let dir_str = dir.to_string_lossy().to_string();
let internal_span = nu_protocol::Span::new(0, dir_str.len());
engine.add_env_var(
"PATH".to_string(),
Value::List {
vals: vec![Value::String {
val: dir_str,
internal_span,
}],
internal_span,
},
);
engine
}
/// creates a new engine with the current path into the completions fixtures folder
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) { pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("dotnu_completions"); let dir = fs::fixtures().join("dotnu_completions");
@ -86,6 +105,23 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Add $nu // Add $nu
engine_state.generate_nu_constant(); engine_state.generate_nu_constant();
// const $NU_LIB_DIRS
let mut working_set = StateWorkingSet::new(&engine_state);
let var_id = working_set.add_variable(
b"$NU_LIB_DIRS".into(),
Span::unknown(),
nu_protocol::Type::List(Box::new(nu_protocol::Type::String)),
false,
);
working_set.set_variable_const_val(
var_id,
Value::test_list(vec![
Value::string(file(dir.join("lib-dir1")), dir_span),
Value::string(file(dir.join("lib-dir3")), dir_span),
]),
);
let _ = engine_state.merge_delta(working_set.render());
// New stack // New stack
let mut stack = Stack::new(); let mut stack = Stack::new();
@ -95,17 +131,12 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
"TEST".to_string(), "TEST".to_string(),
Value::string("NUSHELL".to_string(), dir_span), Value::string("NUSHELL".to_string(), dir_span),
); );
stack.add_env_var( stack.add_env_var(
"NU_LIB_DIRS".to_string(), "NU_LIB_DIRS".into(),
Value::list( Value::test_list(vec![
vec![ Value::string(file(dir.join("lib-dir2")), dir_span),
Value::string(file(dir.join("lib-dir1")), dir_span), Value::string(file(dir.join("lib-dir3")), dir_span),
Value::string(file(dir.join("lib-dir2")), dir_span), ]),
Value::string(file(dir.join("lib-dir3")), dir_span),
],
dir_span,
),
); );
// Merge environment into the permanent state // Merge environment into the permanent state
@ -185,8 +216,8 @@ pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
// match a list of suggestions with the expected values /// match a list of suggestions with the expected values
pub fn match_suggestions(expected: &Vec<String>, suggestions: &Vec<Suggestion>) { pub fn match_suggestions(expected: &Vec<&str>, suggestions: &Vec<Suggestion>) {
let expected_len = expected.len(); let expected_len = expected.len();
let suggestions_len = suggestions.len(); let suggestions_len = suggestions.len();
if expected_len != suggestions_len { if expected_len != suggestions_len {
@ -197,28 +228,34 @@ pub fn match_suggestions(expected: &Vec<String>, suggestions: &Vec<Suggestion>)
) )
} }
let suggestoins_str = suggestions let suggestions_str = suggestions
.iter() .iter()
.map(|it| it.value.clone()) .map(|it| it.value.as_str())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(expected, &suggestoins_str); assert_eq!(expected, &suggestions_str);
} }
// append the separator to the converted path /// match a list of suggestions with the expected values
pub fn match_suggestions_by_string(expected: &[String], suggestions: &Vec<Suggestion>) {
let expected = expected.iter().map(|it| it.as_str()).collect::<Vec<_>>();
match_suggestions(&expected, suggestions);
}
/// append the separator to the converted path
pub fn folder(path: impl Into<PathBuf>) -> String { pub fn folder(path: impl Into<PathBuf>) -> String {
let mut converted_path = file(path); let mut converted_path = file(path);
converted_path.push(MAIN_SEPARATOR); converted_path.push(MAIN_SEPARATOR);
converted_path converted_path
} }
// convert a given path to string /// convert a given path to string
pub fn file(path: impl Into<PathBuf>) -> String { pub fn file(path: impl Into<PathBuf>) -> String {
path.into().into_os_string().into_string().unwrap() path.into().into_os_string().into_string().unwrap()
} }
// merge_input executes the given input into the engine /// merge_input executes the given input into the engine
// and merges the state /// and merges the state
pub fn merge_input( pub fn merge_input(
input: &[u8], input: &[u8],
engine_state: &mut EngineState, engine_state: &mut EngineState,

View File

@ -1,3 +1,5 @@
pub mod completions_helpers; pub mod completions_helpers;
pub use completions_helpers::{file, folder, match_suggestions, merge_input, new_engine}; pub use completions_helpers::{
file, folder, match_suggestions, match_suggestions_by_string, merge_input, new_engine,
};

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-base" name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base" repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.102.0" version = "0.103.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
@ -13,10 +13,10 @@ version = "0.102.0"
workspace = true workspace = true
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.102.0", default-features = false } nu-engine = { path = "../nu-engine", version = "0.103.0", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.102.0" } nu-parser = { path = "../nu-parser", version = "0.103.0" }
nu-path = { path = "../nu-path", version = "0.102.0" } nu-path = { path = "../nu-path", version = "0.103.0" }
nu-protocol = { path = "../nu-protocol", version = "0.102.0", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.0", default-features = false }
indexmap = { workspace = true } indexmap = { workspace = true }
miette = { workspace = true } miette = { workspace = true }

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-extra" name = "nu-cmd-extra"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra" repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
version = "0.102.0" version = "0.103.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
@ -16,13 +16,13 @@ bench = false
workspace = true workspace = true
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.0" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.0" }
nu-engine = { path = "../nu-engine", version = "0.102.0", default-features = false } nu-engine = { path = "../nu-engine", version = "0.103.0", default-features = false }
nu-json = { version = "0.102.0", path = "../nu-json" } nu-json = { version = "0.103.0", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.102.0" } nu-parser = { path = "../nu-parser", version = "0.103.0" }
nu-pretty-hex = { version = "0.102.0", path = "../nu-pretty-hex" } nu-pretty-hex = { version = "0.103.0", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.102.0", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.0", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.102.0", default-features = false } nu-utils = { path = "../nu-utils", version = "0.103.0", default-features = false }
# Potential dependencies for extras # Potential dependencies for extras
heck = { workspace = true } heck = { workspace = true }
@ -37,6 +37,6 @@ itertools = { workspace = true }
mime = { workspace = true } mime = { workspace = true }
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.0" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.0" }
nu-command = { path = "../nu-command", version = "0.102.0" } nu-command = { path = "../nu-command", version = "0.103.0" }
nu-test-support = { path = "../nu-test-support", version = "0.102.0" } nu-test-support = { path = "../nu-test-support", version = "0.103.0" }

View File

@ -26,7 +26,7 @@ impl Command for BitsAnd {
.required( .required(
"target", "target",
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]), SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
"right-hand side of the operation", "Right-hand side of the operation.",
) )
.named( .named(
"endian", "endian",

View File

@ -1,120 +0,0 @@
use nu_engine::command_prelude::*;
use nu_protocol::{report_parse_warning, ParseWarning};
#[derive(Clone)]
pub struct BitsInto;
impl Command for BitsInto {
fn name(&self) -> &str {
"into bits"
}
fn signature(&self) -> Signature {
Signature::build("into bits")
.input_output_types(vec![
(Type::Binary, Type::String),
(Type::Int, Type::String),
(Type::Filesize, Type::String),
(Type::Duration, Type::String),
(Type::String, Type::String),
(Type::Bool, Type::String),
(Type::table(), Type::table()),
(Type::record(), Type::record()),
])
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
.rest(
"rest",
SyntaxShape::CellPath,
"for a data structure input, convert data at the given cell paths",
)
.category(Category::Deprecated)
}
fn description(&self) -> &str {
"Convert value to a binary string."
}
fn search_terms(&self) -> Vec<&str> {
vec![]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
report_parse_warning(
&StateWorkingSet::new(engine_state),
&ParseWarning::DeprecatedWarning {
old_command: "into bits".into(),
new_suggestion: "use `format bits`".into(),
span: head,
url: "`help format bits`".into(),
},
);
crate::extra::strings::format::format_bits(engine_state, stack, call, input)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "convert a binary value into a string, padded to 8 places with 0s",
example: "0x[1] | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert an int into a string, padded to 8 places with 0s",
example: "1 | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a filesize value into a string, padded to 8 places with 0s",
example: "1b | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a duration value into a string, padded to 8 places with 0s",
example: "1ns | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a boolean value into a string, padded to 8 places with 0s",
example: "true | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a string into a raw binary string, padded with 0s to 8 places",
example: "'nushell.sh' | into bits",
result: Some(Value::string("01101110 01110101 01110011 01101000 01100101 01101100 01101100 00101110 01110011 01101000",
Span::test_data(),
)),
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(BitsInto {})
}
}

View File

@ -1,6 +1,5 @@
mod and; mod and;
mod bits_; mod bits_;
mod into;
mod not; mod not;
mod or; mod or;
mod rotate_left; mod rotate_left;
@ -11,7 +10,6 @@ mod xor;
pub use and::BitsAnd; pub use and::BitsAnd;
pub use bits_::Bits; pub use bits_::Bits;
pub use into::BitsInto;
pub use not::BitsNot; pub use not::BitsNot;
pub use or::BitsOr; pub use or::BitsOr;
pub use rotate_left::BitsRol; pub use rotate_left::BitsRol;

View File

@ -27,7 +27,7 @@ impl Command for BitsOr {
.required( .required(
"target", "target",
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]), SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
"right-hand side of the operation", "Right-hand side of the operation.",
) )
.named( .named(
"endian", "endian",

View File

@ -37,7 +37,7 @@ impl Command for BitsRol {
), ),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate left") .required("bits", SyntaxShape::Int, "Number of bits to rotate left.")
.switch( .switch(
"signed", "signed",
"always treat input number as a signed number", "always treat input number as a signed number",

View File

@ -37,7 +37,7 @@ impl Command for BitsRor {
), ),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate right") .required("bits", SyntaxShape::Int, "Number of bits to rotate right.")
.switch( .switch(
"signed", "signed",
"always treat input number as a signed number", "always treat input number as a signed number",

View File

@ -40,7 +40,7 @@ impl Command for BitsShl {
), ),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to shift left") .required("bits", SyntaxShape::Int, "Number of bits to shift left.")
.switch( .switch(
"signed", "signed",
"always treat input number as a signed number", "always treat input number as a signed number",

View File

@ -37,7 +37,7 @@ impl Command for BitsShr {
), ),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to shift right") .required("bits", SyntaxShape::Int, "Number of bits to shift right.")
.switch( .switch(
"signed", "signed",
"always treat input number as a signed number", "always treat input number as a signed number",

View File

@ -27,7 +27,7 @@ impl Command for BitsXor {
.required( .required(
"target", "target",
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]), SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
"right-hand side of the operation", "Right-hand side of the operation.",
) )
.named( .named(
"endian", "endian",

View File

@ -1,74 +0,0 @@
use nu_engine::command_prelude::*;
use nu_protocol::{report_parse_warning, ParseWarning};
#[derive(Clone)]
pub struct Fmt;
impl Command for Fmt {
fn name(&self) -> &str {
"fmt"
}
fn description(&self) -> &str {
"Format a number."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("fmt")
.input_output_types(vec![(Type::Number, Type::record())])
.category(Category::Deprecated)
}
fn search_terms(&self) -> Vec<&str> {
vec![]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get a record containing multiple formats for the number 42",
example: "42 | fmt",
result: Some(Value::test_record(record! {
"binary" => Value::test_string("0b101010"),
"debug" => Value::test_string("42"),
"display" => Value::test_string("42"),
"lowerexp" => Value::test_string("4.2e1"),
"lowerhex" => Value::test_string("0x2a"),
"octal" => Value::test_string("0o52"),
"upperexp" => Value::test_string("4.2E1"),
"upperhex" => Value::test_string("0x2A"),
})),
}]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
report_parse_warning(
&StateWorkingSet::new(engine_state),
&ParseWarning::DeprecatedWarning {
old_command: "fmt".into(),
new_suggestion: "use `format number`".into(),
span: head,
url: "`help format number`".into(),
},
);
crate::extra::strings::format::format_number(engine_state, stack, call, input)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Fmt {})
}
}

View File

@ -1,3 +0,0 @@
mod fmt;
pub(crate) use fmt::Fmt;

View File

@ -26,7 +26,7 @@ impl Command for EachWhile {
.required( .required(
"closure", "closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
"the closure to run", "The closure to run.",
) )
.category(Category::Filters) .category(Category::Filters)
} }

View File

@ -20,7 +20,7 @@ impl Command for Rotate {
.rest( .rest(
"rest", "rest",
SyntaxShape::String, SyntaxShape::String,
"the names to give columns once rotated", "The names to give columns once rotated.",
) )
.category(Category::Filters) .category(Category::Filters)
.allow_variants_without_examples(true) .allow_variants_without_examples(true)

View File

@ -16,7 +16,7 @@ impl Command for UpdateCells {
.required( .required(
"closure", "closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
"the closure to run an update for each cell", "The closure to run an update for each cell.",
) )
.named( .named(
"columns", "columns",

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathArcCos;
impl Command for SubCommand { impl Command for MathArcCos {
fn name(&self) -> &str { fn name(&self) -> &str {
"math arccos" "math arccos"
} }
@ -114,6 +114,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathArcCos {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathArcCosH;
impl Command for SubCommand { impl Command for MathArcCosH {
fn name(&self) -> &str { fn name(&self) -> &str {
"math arccosh" "math arccosh"
} }
@ -100,6 +100,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathArcCosH {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathArcSin;
impl Command for SubCommand { impl Command for MathArcSin {
fn name(&self) -> &str { fn name(&self) -> &str {
"math arcsin" "math arcsin"
} }
@ -115,6 +115,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathArcSin {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathArcSinH;
impl Command for SubCommand { impl Command for MathArcSinH {
fn name(&self) -> &str { fn name(&self) -> &str {
"math arcsinh" "math arcsinh"
} }
@ -88,6 +88,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathArcSinH {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathArcTan;
impl Command for SubCommand { impl Command for MathArcTan {
fn name(&self) -> &str { fn name(&self) -> &str {
"math arctan" "math arctan"
} }
@ -102,6 +102,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathArcTan {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathArcTanH;
impl Command for SubCommand { impl Command for MathArcTanH {
fn name(&self) -> &str { fn name(&self) -> &str {
"math arctanh" "math arctanh"
} }
@ -101,6 +101,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathArcTanH {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathCos;
impl Command for SubCommand { impl Command for MathCos {
fn name(&self) -> &str { fn name(&self) -> &str {
"math cos" "math cos"
} }
@ -108,6 +108,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathCos {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathCosH;
impl Command for SubCommand { impl Command for MathCosH {
fn name(&self) -> &str { fn name(&self) -> &str {
"math cosh" "math cosh"
} }
@ -88,6 +88,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathCosH {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathExp;
impl Command for SubCommand { impl Command for MathExp {
fn name(&self) -> &str { fn name(&self) -> &str {
"math exp" "math exp"
} }
@ -93,6 +93,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathExp {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathLn;
impl Command for SubCommand { impl Command for MathLn {
fn name(&self) -> &str { fn name(&self) -> &str {
"math ln" "math ln"
} }
@ -100,6 +100,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathLn {})
} }
} }

View File

@ -15,19 +15,19 @@ mod arcsinh;
mod arctan; mod arctan;
mod arctanh; mod arctanh;
pub use cos::SubCommand as MathCos; pub use cos::MathCos;
pub use cosh::SubCommand as MathCosH; pub use cosh::MathCosH;
pub use sin::SubCommand as MathSin; pub use sin::MathSin;
pub use sinh::SubCommand as MathSinH; pub use sinh::MathSinH;
pub use tan::SubCommand as MathTan; pub use tan::MathTan;
pub use tanh::SubCommand as MathTanH; pub use tanh::MathTanH;
pub use exp::SubCommand as MathExp; pub use exp::MathExp;
pub use ln::SubCommand as MathLn; pub use ln::MathLn;
pub use arccos::SubCommand as MathArcCos; pub use arccos::MathArcCos;
pub use arccosh::SubCommand as MathArcCosH; pub use arccosh::MathArcCosH;
pub use arcsin::SubCommand as MathArcSin; pub use arcsin::MathArcSin;
pub use arcsinh::SubCommand as MathArcSinH; pub use arcsinh::MathArcSinH;
pub use arctan::SubCommand as MathArcTan; pub use arctan::MathArcTan;
pub use arctanh::SubCommand as MathArcTanH; pub use arctanh::MathArcTanH;

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathSin;
impl Command for SubCommand { impl Command for MathSin {
fn name(&self) -> &str { fn name(&self) -> &str {
"math sin" "math sin"
} }
@ -108,6 +108,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathSin {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathSinH;
impl Command for SubCommand { impl Command for MathSinH {
fn name(&self) -> &str { fn name(&self) -> &str {
"math sinh" "math sinh"
} }
@ -87,6 +87,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathSinH {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathTan;
impl Command for SubCommand { impl Command for MathTan {
fn name(&self) -> &str { fn name(&self) -> &str {
"math tan" "math tan"
} }
@ -106,6 +106,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathTan {})
} }
} }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathTanH;
impl Command for SubCommand { impl Command for MathTanH {
fn name(&self) -> &str { fn name(&self) -> &str {
"math tanh" "math tanh"
} }
@ -86,6 +86,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathTanH {})
} }
} }

View File

@ -1,14 +1,11 @@
mod bits; mod bits;
mod conversions;
mod filters; mod filters;
mod formats; mod formats;
mod math; mod math;
mod platform; mod platform;
mod strings; mod strings;
pub use bits::{ pub use bits::{Bits, BitsAnd, BitsNot, BitsOr, BitsRol, BitsRor, BitsShl, BitsShr, BitsXor};
Bits, BitsAnd, BitsInto, BitsNot, BitsOr, BitsRol, BitsRor, BitsShl, BitsShr, BitsXor,
};
pub use formats::ToHtml; pub use formats::ToHtml;
pub use math::{MathArcCos, MathArcCosH, MathArcSin, MathArcSinH, MathArcTan, MathArcTanH}; pub use math::{MathArcCos, MathArcCosH, MathArcSin, MathArcSinH, MathArcTan, MathArcTanH};
pub use math::{MathCos, MathCosH, MathSin, MathSinH, MathTan, MathTanH}; pub use math::{MathCos, MathCosH, MathSin, MathSinH, MathTan, MathTanH};
@ -29,8 +26,6 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
}; };
} }
bind_command!(conversions::Fmt);
bind_command!( bind_command!(
filters::UpdateCells, filters::UpdateCells,
filters::EachWhile, filters::EachWhile,
@ -63,7 +58,6 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
bind_command! { bind_command! {
Bits, Bits,
BitsAnd, BitsAnd,
BitsInto,
BitsNot, BitsNot,
BitsOr, BitsOr,
BitsRol, BitsRol,

View File

@ -38,7 +38,7 @@ impl Command for SubCommand {
.rest( .rest(
"cell path", "cell path",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"for a data structure input, add a gradient to strings at the given cell paths", "For a data structure input, add a gradient to strings at the given cell paths.",
) )
.input_output_types(vec![ .input_output_types(vec![
(Type::String, Type::String), (Type::String, Type::String),

View File

@ -40,7 +40,7 @@ impl Command for FormatBits {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"for a data structure input, convert data at the given cell paths", "For a data structure input, convert data at the given cell paths.",
) )
.category(Category::Conversions) .category(Category::Conversions)
} }
@ -111,8 +111,7 @@ impl Command for FormatBits {
} }
} }
// TODO: crate public only during deprecation fn format_bits(
pub(crate) fn format_bits(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,

View File

@ -18,7 +18,7 @@ impl Command for FormatPattern {
.required( .required(
"pattern", "pattern",
SyntaxShape::String, SyntaxShape::String,
"the pattern to output. e.g.) \"{foo}: {bar}\"", "The pattern to output. e.g.) \"{foo}: {bar}\".",
) )
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.category(Category::Strings) .category(Category::Strings)

View File

@ -2,8 +2,6 @@ mod bits;
mod command; mod command;
mod number; mod number;
pub(crate) use bits::FormatBits;
pub(crate) use command::FormatPattern; pub(crate) use command::FormatPattern;
// TODO remove `format_bits` visibility after removal of into bits pub(crate) use number::FormatNumber;
pub(crate) use bits::{format_bits, FormatBits};
// TODO remove `format_number` visibility after removal of into bits
pub(crate) use number::{format_number, FormatNumber};

View File

@ -20,7 +20,7 @@ impl Command for FormatNumber {
} }
fn search_terms(&self) -> Vec<&str> { fn search_terms(&self) -> Vec<&str> {
vec!["display", "render", "format"] vec!["display", "render", "fmt"]
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -3,9 +3,9 @@ use heck::ToLowerCamelCase;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrCamelCase;
impl Command for SubCommand { impl Command for StrCamelCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str camel-case" "str camel-case"
} }
@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths", "For a data structure input, convert strings at the given cell paths.",
) )
.category(Category::Strings) .category(Category::Strings)
} }
@ -91,6 +91,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(StrCamelCase {})
} }
} }

View File

@ -3,9 +3,9 @@ use heck::ToKebabCase;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrKebabCase;
impl Command for SubCommand { impl Command for StrKebabCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str kebab-case" "str kebab-case"
} }
@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths", "For a data structure input, convert strings at the given cell paths.",
) )
.category(Category::Strings) .category(Category::Strings)
} }
@ -90,6 +90,6 @@ mod tests {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(StrKebabCase {})
} }
} }

View File

@ -6,13 +6,13 @@ mod snake_case;
mod str_; mod str_;
mod title_case; mod title_case;
pub use camel_case::SubCommand as StrCamelCase; pub use camel_case::StrCamelCase;
pub use kebab_case::SubCommand as StrKebabCase; pub use kebab_case::StrKebabCase;
pub use pascal_case::SubCommand as StrPascalCase; pub use pascal_case::StrPascalCase;
pub use screaming_snake_case::SubCommand as StrScreamingSnakeCase; pub use screaming_snake_case::StrScreamingSnakeCase;
pub use snake_case::SubCommand as StrSnakeCase; pub use snake_case::StrSnakeCase;
pub use str_::Str; pub use str_::Str;
pub use title_case::SubCommand as StrTitleCase; pub use title_case::StrTitleCase;
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument}; use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;

View File

@ -3,9 +3,9 @@ use heck::ToUpperCamelCase;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrPascalCase;
impl Command for SubCommand { impl Command for StrPascalCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str pascal-case" "str pascal-case"
} }
@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths", "For a data structure input, convert strings at the given cell paths.",
) )
.category(Category::Strings) .category(Category::Strings)
} }
@ -91,6 +91,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(StrPascalCase {})
} }
} }

View File

@ -3,9 +3,9 @@ use heck::ToShoutySnakeCase;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrScreamingSnakeCase;
impl Command for SubCommand { impl Command for StrScreamingSnakeCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str screaming-snake-case" "str screaming-snake-case"
} }
@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths", "For a data structure input, convert strings at the given cell paths.",
) )
.category(Category::Strings) .category(Category::Strings)
} }
@ -91,6 +91,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(StrScreamingSnakeCase {})
} }
} }

View File

@ -3,9 +3,9 @@ use heck::ToSnakeCase;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrSnakeCase;
impl Command for SubCommand { impl Command for StrSnakeCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str snake-case" "str snake-case"
} }
@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths", "For a data structure input, convert strings at the given cell paths.",
) )
.category(Category::Strings) .category(Category::Strings)
} }
@ -91,6 +91,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(StrSnakeCase {})
} }
} }

View File

@ -3,9 +3,9 @@ use heck::ToTitleCase;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrTitleCase;
impl Command for SubCommand { impl Command for StrTitleCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str title-case" "str title-case"
} }
@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths", "For a data structure input, convert strings at the given cell paths.",
) )
.category(Category::Strings) .category(Category::Strings)
} }
@ -86,6 +86,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(StrTitleCase {})
} }
} }

View File

@ -6,7 +6,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-lang" name = "nu-cmd-lang"
version = "0.102.0" version = "0.103.0"
[lib] [lib]
bench = false bench = false
@ -15,10 +15,10 @@ bench = false
workspace = true workspace = true
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.102.0", default-features = false } nu-engine = { path = "../nu-engine", version = "0.103.0", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.102.0" } nu-parser = { path = "../nu-parser", version = "0.103.0" }
nu-protocol = { path = "../nu-protocol", version = "0.102.0", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.0", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.102.0", default-features = false } nu-utils = { path = "../nu-utils", version = "0.103.0", default-features = false }
itertools = { workspace = true } itertools = { workspace = true }
shadow-rs = { version = "0.38", default-features = false } shadow-rs = { version = "0.38", default-features = false }
@ -26,6 +26,10 @@ shadow-rs = { version = "0.38", default-features = false }
[build-dependencies] [build-dependencies]
shadow-rs = { version = "0.38", default-features = false } shadow-rs = { version = "0.38", default-features = false }
[dev-dependencies]
quickcheck = { workspace = true }
quickcheck_macros = { workspace = true }
[features] [features]
default = ["os"] default = ["os"]
os = [ os = [
@ -38,8 +42,7 @@ plugin = [
"os", "os",
] ]
mimalloc = []
trash-support = [] trash-support = []
sqlite = [] sqlite = []
static-link-openssl = [] static-link-openssl = []
system-clipboard = [] system-clipboard = []

View File

@ -0,0 +1,61 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct AttrCategory;
impl Command for AttrCategory {
fn name(&self) -> &str {
"attr category"
}
fn signature(&self) -> Signature {
Signature::build("attr category")
.input_output_type(Type::Nothing, Type::list(Type::String))
.allow_variants_without_examples(true)
.required(
"category",
SyntaxShape::String,
"Category of the custom command.",
)
.category(Category::Core)
}
fn description(&self) -> &str {
"Attribute for adding a category to custom commands."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let arg: String = call.req(engine_state, stack, 0)?;
Ok(Value::string(arg, call.head).into_pipeline_data())
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let arg: String = call.req_const(working_set, 0)?;
Ok(Value::string(arg, call.head).into_pipeline_data())
}
fn is_const(&self) -> bool {
true
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Add a category to a custom command",
example: r###"# Double numbers
@category math
def double []: [number -> number] { $in * 2 }"###,
result: None,
}]
}
}

View File

@ -0,0 +1,159 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct AttrExample;
impl Command for AttrExample {
fn name(&self) -> &str {
"attr example"
}
// TODO: When const closure are available, switch to using them for the `example` argument
// rather than a block. That should remove the need for `requires_ast_for_arguments` to be true
fn signature(&self) -> Signature {
Signature::build("attr example")
.input_output_types(vec![(
Type::Nothing,
Type::Record(
[
("description".into(), Type::String),
("example".into(), Type::String),
]
.into(),
),
)])
.allow_variants_without_examples(true)
.required(
"description",
SyntaxShape::String,
"Description of the example.",
)
.required(
"example",
SyntaxShape::OneOf(vec![SyntaxShape::Block, SyntaxShape::String]),
"Example code snippet.",
)
.named(
"result",
SyntaxShape::Any,
"Expected output of example.",
None,
)
.category(Category::Core)
}
fn description(&self) -> &str {
"Attribute for adding examples to custom commands."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let description: Spanned<String> = call.req(engine_state, stack, 0)?;
let result: Option<Value> = call.get_flag(engine_state, stack, "result")?;
let example_string: Result<String, _> = call.req(engine_state, stack, 1);
let example_expr = call
.positional_nth(stack, 1)
.ok_or(ShellError::MissingParameter {
param_name: "example".into(),
span: call.head,
})?;
let working_set = StateWorkingSet::new(engine_state);
attr_example_impl(
example_expr,
example_string,
&working_set,
call,
description,
result,
)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let description: Spanned<String> = call.req_const(working_set, 0)?;
let result: Option<Value> = call.get_flag_const(working_set, "result")?;
let example_string: Result<String, _> = call.req_const(working_set, 1);
let example_expr =
call.assert_ast_call()?
.positional_nth(1)
.ok_or(ShellError::MissingParameter {
param_name: "example".into(),
span: call.head,
})?;
attr_example_impl(
example_expr,
example_string,
working_set,
call,
description,
result,
)
}
fn is_const(&self) -> bool {
true
}
fn requires_ast_for_arguments(&self) -> bool {
true
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Add examples to custom command",
example: r###"# Double numbers
@example "double an int" { 2 | double } --result 4
@example "double a float" { 0.25 | double } --result 0.5
def double []: [number -> number] { $in * 2 }"###,
result: None,
}]
}
}
fn attr_example_impl(
example_expr: &nu_protocol::ast::Expression,
example_string: Result<String, ShellError>,
working_set: &StateWorkingSet<'_>,
call: &Call<'_>,
description: Spanned<String>,
result: Option<Value>,
) -> Result<PipelineData, ShellError> {
let example_content = match example_expr.as_block() {
Some(block_id) => {
let block = working_set.get_block(block_id);
let contents =
working_set.get_span_contents(block.span.expect("a block must have a span"));
let contents = contents
.strip_prefix(b"{")
.and_then(|x| x.strip_suffix(b"}"))
.unwrap_or(contents)
.trim_ascii();
String::from_utf8_lossy(contents).into_owned()
}
None => example_string?,
};
let mut rec = record! {
"description" => Value::string(description.item, description.span),
"example" => Value::string(example_content, example_expr.span),
};
if let Some(result) = result {
rec.push("result", result);
}
Ok(Value::record(rec, call.head).into_pipeline_data())
}

View File

@ -0,0 +1,7 @@
mod category;
mod example;
mod search_terms;
pub use category::AttrCategory;
pub use example::AttrExample;
pub use search_terms::AttrSearchTerms;

View File

@ -0,0 +1,57 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct AttrSearchTerms;
impl Command for AttrSearchTerms {
fn name(&self) -> &str {
"attr search-terms"
}
fn signature(&self) -> Signature {
Signature::build("attr search-terms")
.input_output_type(Type::Nothing, Type::list(Type::String))
.allow_variants_without_examples(true)
.rest("terms", SyntaxShape::String, "Search terms.")
.category(Category::Core)
}
fn description(&self) -> &str {
"Attribute for adding search terms to custom commands."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let args = call.rest(engine_state, stack, 0)?;
Ok(Value::list(args, call.head).into_pipeline_data())
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let args = call.rest_const(working_set, 0)?;
Ok(Value::list(args, call.head).into_pipeline_data())
}
fn is_const(&self) -> bool {
true
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Add search terms to a custom command",
example: r###"# Double numbers
@search-terms multiply times
def double []: [number -> number] { $in * 2 }"###,
result: None,
}]
}
}

View File

@ -72,6 +72,19 @@ impl Command for Const {
} }
} }
fn run_const(
&self,
_working_set: &StateWorkingSet,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn is_const(&self) -> bool {
true
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -34,13 +34,22 @@ little reason to use this over just writing the values as-is."#
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let mut args = call.rest(engine_state, stack, 0)?; let args = call.rest(engine_state, stack, 0)?;
let value = match args.len() { echo_impl(args, call.head)
0 => Value::string("", call.head), }
1 => args.pop().expect("one element"),
_ => Value::list(args, call.head), fn run_const(
}; &self,
Ok(value.into_pipeline_data()) working_set: &StateWorkingSet,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let args = call.rest_const(working_set, 0)?;
echo_impl(args, call.head)
}
fn is_const(&self) -> bool {
true
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -63,6 +72,15 @@ little reason to use this over just writing the values as-is."#
} }
} }
fn echo_impl(mut args: Vec<Value>, head: Span) -> Result<PipelineData, ShellError> {
let value = match args.len() {
0 => Value::string("", head),
1 => args.pop().expect("one element"),
_ => Value::list(args, head),
};
Ok(value.into_pipeline_data())
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test] #[test]

View File

@ -32,6 +32,10 @@ This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"# https://www.nushell.sh/book/thinking_in_nu.html"#
} }
fn search_terms(&self) -> Vec<&str> {
vec!["unset"]
}
fn command_type(&self) -> CommandType { fn command_type(&self) -> CommandType {
CommandType::Keyword CommandType::Keyword
} }

View File

@ -29,6 +29,10 @@ impl Command for HideEnv {
"Hide environment variables in the current scope." "Hide environment variables in the current scope."
} }
fn search_terms(&self) -> Vec<&str> {
vec!["unset", "drop"]
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,

View File

@ -1,4 +1,5 @@
mod alias; mod alias;
mod attr;
mod break_; mod break_;
mod collect; mod collect;
mod const_; mod const_;
@ -35,6 +36,7 @@ mod version;
mod while_; mod while_;
pub use alias::Alias; pub use alias::Alias;
pub use attr::*;
pub use break_::Break; pub use break_::Break;
pub use collect::Collect; pub use collect::Collect;
pub use const_::Const; pub use const_::Const;

View File

@ -119,31 +119,40 @@ impl Command for OverlayUse {
// Evaluate the export-env block (if any) and keep its environment // Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block { if let Some(block_id) = module.env_block {
let maybe_path = find_in_dirs_env( let maybe_file_path_or_dir = find_in_dirs_env(
&name_arg.item, &name_arg.item,
engine_state, engine_state,
caller_stack, caller_stack,
get_dirs_var_from_call(caller_stack, call), get_dirs_var_from_call(caller_stack, call),
)?; )?;
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
let mut callee_stack = caller_stack let mut callee_stack = caller_stack
.gather_captures(engine_state, &block.captures) .gather_captures(engine_state, &block.captures)
.reset_pipes(); .reset_pipes();
if let Some(path) = &maybe_path { if let Some(path) = &maybe_file_path_or_dir {
// Set the currently evaluated directory, if the argument is a valid path // Set the currently evaluated directory, if the argument is a valid path
let mut parent = path.clone(); let parent = if path.is_dir() {
parent.pop(); path.clone()
} else {
let mut parent = path.clone();
parent.pop();
parent
};
let file_pwd = Value::string(parent.to_string_lossy(), call.head); let file_pwd = Value::string(parent.to_string_lossy(), call.head);
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd); callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
} }
if let Some(file_path) = &maybe_path { if let Some(path) = &maybe_file_path_or_dir {
let file_path = Value::string(file_path.to_string_lossy(), call.head); let module_file_path = if path.is_dir() {
callee_stack.add_env_var("CURRENT_FILE".to_string(), file_path); // the existence of `mod.nu` is verified in parsing time
// so it's safe to use it here.
Value::string(path.join("mod.nu").to_string_lossy(), call.head)
} else {
Value::string(path.to_string_lossy(), call.head)
};
callee_stack.add_env_var("CURRENT_FILE".to_string(), module_file_path);
} }
let eval_block = get_eval_block(engine_state); let eval_block = get_eval_block(engine_state);

View File

@ -10,7 +10,7 @@ impl Command for Return {
} }
fn description(&self) -> &str { fn description(&self) -> &str {
"Return early from a function." "Return early from a custom command."
} }
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {

View File

@ -161,11 +161,7 @@ fn push_version_numbers(record: &mut Record, head: Span) {
} }
fn global_allocator() -> &'static str { fn global_allocator() -> &'static str {
if cfg!(feature = "mimalloc") { "standard"
"mimalloc"
} else {
"standard"
}
} }
fn features_enabled() -> Vec<String> { fn features_enabled() -> Vec<String> {

View File

@ -16,6 +16,9 @@ pub fn create_default_context() -> EngineState {
// Core // Core
bind_command! { bind_command! {
Alias, Alias,
AttrCategory,
AttrExample,
AttrSearchTerms,
Break, Break,
Collect, Collect,
Const, Const,

View File

@ -221,7 +221,7 @@ impl PartialEq for DebuggableValue<'_> {
} }
} }
impl<'a> std::fmt::Debug for DebuggableValue<'a> { impl std::fmt::Debug for DebuggableValue<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 { match self.0 {
Value::Bool { val, .. } => { Value::Bool { val, .. } => {

View File

@ -4,6 +4,8 @@ mod core_commands;
mod default_context; mod default_context;
pub mod example_support; pub mod example_support;
mod example_test; mod example_test;
#[cfg(test)]
mod parse_const_test;
pub use core_commands::*; pub use core_commands::*;
pub use default_context::*; pub use default_context::*;

View File

@ -0,0 +1,19 @@
use nu_protocol::{engine::StateWorkingSet, Span};
use quickcheck_macros::quickcheck;
#[quickcheck]
fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(data.as_bytes(), 0, b"", b"", true);
if err.is_none() {
let context = crate::create_default_context();
{
let mut working_set = StateWorkingSet::new(&context);
let _ = working_set.add_file("quickcheck".into(), data.as_bytes());
let _ =
nu_parser::parse_block(&mut working_set, &tokens, Span::new(0, 0), false, false);
}
}
true
}

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-plugin" name = "nu-cmd-plugin"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin" repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
version = "0.102.0" version = "0.103.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
@ -13,10 +13,10 @@ version = "0.102.0"
workspace = true workspace = true
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.102.0" } nu-engine = { path = "../nu-engine", version = "0.103.0" }
nu-path = { path = "../nu-path", version = "0.102.0" } nu-path = { path = "../nu-path", version = "0.103.0" }
nu-protocol = { path = "../nu-protocol", version = "0.102.0", features = ["plugin"] } nu-protocol = { path = "../nu-protocol", version = "0.103.0", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.102.0" } nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.103.0" }
itertools = { workspace = true } itertools = { workspace = true }

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