Compare commits

...

108 Commits
main ... 0.90.0

Author SHA1 Message Date
f5f21aca2d Bump to 0.90 (#11730)
<!--
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
<!-- 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-02-06 22:42:43 +02:00
9342ad6ae1 Pin reedline to version 0.29.0 (#11782)
<!--
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!
-->

See full release notes:
https://github.com/nushell/reedline/releases/tag/v0.29.0

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

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

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

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-02-06 22:23:41 +02:00
cb4ca157fd Bump crate-ci/typos from 1.17.2 to 1.18.0 (#11743) 2024-02-05 01:00:27 +00:00
d1c807230b default to having border in ide_menu 2024-02-04 08:19:09 -06:00
4e5d3db952 allow strings with thousands separators to be converted to filesize or ints (#11724)
# Description

This PR changes `into int` and `into filesize` so that they allow
thousands separators.

### Before
```nushell
❯ '1,000' | into filesize
Error: nu:🐚:cant_convert

  × Can't convert to int.
   ╭─[entry #1:1:1]
 1 │ '1,000' | into filesize
   · ───┬───
   ·    ╰── can't convert string to int
   ╰────

❯ '1,000' | into int
Error: nu:🐚:cant_convert

  × Can't convert to int.
   ╭─[entry #2:1:1]
 1 │ '1,000' | into int
   ·           ────┬───
   ·               ╰── can't convert string to int
   ╰────
  help: string "1,000" does not represent a valid integer
```
### After
```nushell
❯ '1,000' | into filesize
1.0 KB
❯ '1,000' | into int
1000
```

This works by getting the system locale and from that, determining what
the thousands separator is. So, hopefully, this will work across
locales.
# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-02-03 10:42:44 -06:00
b8d37a7541 Fix panic in rotate; Add safe record creation function (#11718)
<!--
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.
-->

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

The problem is in our [record creation
API](0d518bf813/crates/nu-protocol/src/value/record.rs (L33))
which panics if the numbers of columns and values are different. I added
a safe variant that returns a `Result` and used it in the `rotate`
command.

## TODO in another PR:

Go through all `from_raw_cols_vals_unchecked()` (this includes the
`record!` macro which uses the unchecked version) and make sure that
either
a) it is guaranteed the number of cols and vals is the same, or
b) convert the call to `from_raw_cols_vals()`

Reason: Nushell should never panic.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-02-03 13:23:16 +02:00
c7a8aac883 Tighten def body parsing (#11719)
<!--
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.
-->

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

Previously, syntax `def a [] (echo 4)` was allowed to parse and then
failed with panic duting eval.

Current error:
```
Error: nu::parser::parse_mismatch

  × Parse mismatch during operation.
   ╭─[entry #1:1:1]
 1 │ def a [] (echo 4)
   ·          ────┬───
   ·              ╰── expected definition body closure { ... }
   ╰────
```

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-02-03 13:20:40 +02:00
0d518bf813 query web --query should return list<list<string>> like the scraper crate's ElementRef::text() (#11705)
<!--
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.
-->
## Problem
I tried converting one of my Rust web scrapers to Nushell just to see
how it would be done, but quickly ran into an issue that proved annoying
to fix without diving into the source.

For instance, let's say we have the following HTML
```html
<p>Hello there, <span style="color: red;">World</span></p>
```
and we want to extract only the text within the `p` element, but not the
`span`. With the current version of nu_plugin_query, if we run this code
```nushell
echo `<p>Hello there, <span style="color: red;">World</span></p>` | query web -q "p" | get 0
# returns "Hello there, World"

# but we want only "Hello there, "
```
we will get back a `list<string>` that contains 1 string `Hello there,
World`.
To avoid scraping the span, we would have to do something like this
```nushell
const html = `<p>Hello there, <span style="color: red;">World</span></p>`
$html
| query web -q "p"
| get 0
| str replace ($html | query web -q "p > span" | get 0) ""
# returns "Hello there, "
```
In other words, we would have to make a sub scrape of the text we
*don't* want in order to subtract it from the text we *do* want.

## Solution
I didn't like this behavior, so I decided to change it. I modified the
`execute_selector_query` function to collect all text nodes in the HTML
element matching the query. Now `query web --query` will return a
`list<list<string>>`
```nushell
echo `<p>Hello there, <span style="color: red;">World</span></p>` | query web -q "p" | get 0 | to json --raw
# returns ["Hello there, ","World"]
```
This also brings `query web --query`'s behavior more in line with
[scraper's
ElementRef::text()](https://docs.rs/scraper/latest/scraper/element_ref/struct.ElementRef.html#method.text)
which "Returns an iterator over descendent text nodes", allowing you to
choose how much of an element's text you want to scrape without
resorting to string substitutions.

## Consequences
As this is a user-facing change, the usage examples will produce
different results than before. For example
```nushell
http get https://phoronix.com | query web --query 'header'
```
will return a list of lists of 1 string each, whereas before it was just
a list of strings.

I only modified the 3rd example
```nushell
# old
http get https://www.nushell.sh | query web --query 'h2, h2 + p' | group 2 | each {rotate --ccw tagline description} | flatten
# new
http get https://www.nushell.sh | query web --query 'h2, h2 + p' | each {str join} | group 2 | each {rotate --ccw tagline description} | flatten
```
to make it behave like before because I thought this one ought to show
the same results as before.
However, the second reason I changed the 3rd example is because it
otherwise panics! If we run the original 3rd example with my
modifications, we get a panic
```
thread 'main' panicked at crates/nu-protocol/src/value/record.rs:34:9:
assertion `left == right` failed
  left: 2
 right: 17
```
This happens because `rotate` receives a list of lists where the inner
lists have a different number of elements.

However this panic is unrelated to the changes I've made, because it can
be triggered easily without using the plugin. For instance
```nushell
# this is fine
[[[one] [two]] [[three] [four]]] | each {rotate --ccw tagline description}

# this panics!
[[[one] [two]] [[three] [four five]]] | each {rotate --ccw tagline description}
```
Though beyond the scope of this PR, I thought I'd mention this bug since
I found it while testing the usage examples. However, I intend to make a
proper issue about it tomorrow.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
`query web --query "css selector"` now returns a `list<list<string>>`
instead of a `list<string>` to make it more in line with [scraper's
ElementRef::text()](https://docs.rs/scraper/latest/scraper/element_ref/struct.ElementRef.html#method.text).

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

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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
> ```
-->
I ran `cargo fmt --all -- --check`, `cargo clippy --workspace -- -D
warnings -D clippy::unwrap_used` and the tests in the plugin.

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
PR that updates the documentation to match the new 3rd example:
https://github.com/nushell/nushell.github.io/pull/1235
2024-02-02 19:40:47 -06:00
e083b75aef update to latest reedline after column menu fix (#11715)
# Description

This PR updates to the latest reedline after the column menu fix.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-02-03 08:30:28 +08:00
2e5a857983 update query web wiki example (#11709)
# Description

This PR tries to make `query web` more resilient and easier to debug
with the `--inspect` parameter when trying to scrape tables. Previously
it would just fail, now at least it tries to give you a hint.

This is some example output now of when something went wrong.
```
❯ http get https://en.wikipedia.org/wiki/List_of_cities_in_India_by_population | query web --as-table [Rank City 'Population(2011)[3]' 'Population(2001)[3][a]' 'State or union territory'] --inspect
Passed in Column Headers = ["Rank", "City", "Population(2011)[3]", "Population(2001)[3][a]", "State or union territory"]

First 2048 HTML chars = <!DOCTYPE html>
<html class="client-nojs vector-feature-language-in-header-enabled vector-feature-language-in-main-page-header-disabled vector-feature-sticky-header-disabled vector-feature-page-tools-pinned-disabled vector-feature-toc-pinned-clientpref-1 vector-feature-main-menu-pinned-disabled vector-feature-limited-width-clientpref-1 vector-feature-limited-width-content-enabled vector-feature-custom-font-size-clientpref-0 vector-feature-client-preferences-disabled vector-feature-client-prefs-pinned-disabled vector-toc-available" lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>List of cities in India by population - Wikipedia</title>
<script>(function(){var className="client-js vector-feature-language-in-header-enabled vector-feature-language-in-main-page-header-disabled vector-feature-sticky-header-disabled vector-feature-page-tools-pinned-disabled vector-feature-toc-pinned-clientpref-1 vector-feature-main-menu-pinned-disabled vector-feature-limited-width-clientpref-1 vector-feature-limited-width-content-enabled vector-feature-custom-font-size-clientpref-0 vector-feature-client-preferences-disabled vector-feature-client-prefs-pinned-disabled vector-toc-available";var cookie=document.cookie.match(/(?:^|; )enwikimwclientpreferences=([^;]+)/);if(cookie){cookie[1].split('%2C').forEach(function(pref){className=className.replace(new RegExp('(^| )'+pref.replace(/-clientpref-\w+$|[^\w-]+/g,'')+'-clientpref-\\w+( |$)'),'$1'+pref+'$2');});}document.documentElement.className=className;}());RLCONF={"wgBreakFrames":false,"wgSeparatorTransformTable":["",""],"wgDigitTransformTable":["",""],"wgDefaultDateFormat":"dmy","wgMonthNames":["",
"January","February","March","April","May","June","July","August","September","October","November","December"],"wgRequestId":"9ecdad8f-2dbd-4245-b54d-9c57aea5ca45","wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"List_of_cities_in_India_by_population","wgTitle":"List of cities in India by population","wgCurRevisionId":1192093210,"wgRev

Potential HTML Headers = ["City", "Population(2011)[3]", "Population(2001)[3][a]", "State or unionterritory", "Ref"]

Potential HTML Headers = ["City", "Population(2011)[5]", "Population(2001)", "State or unionterritory"]

Potential HTML Headers = [".mw-parser-output .navbar{display:inline;font-size:88%;font-weight:normal}.mw-parser-output .navbar-collapse{float:left;text-align:left}.mw-parser-output .navbar-boxtext{word-spacing:0}.mw-parser-output .navbar ul{display:inline-block;white-space:nowrap;line-height:inherit}.mw-parser-output .navbar-brackets::before{margin-right:-0.125em;content:\"[ \"}.mw-parser-output .navbar-brackets::after{margin-left:-0.125em;content:\" ]\"}.mw-parser-output .navbar li{word-spacing:-0.125em}.mw-parser-output .navbar a>span,.mw-parser-output .navbar a>abbr{text-decoration:inherit}.mw-parser-output .navbar-mini abbr{font-variant:small-caps;border-bottom:none;text-decoration:none;cursor:inherit}.mw-parser-output .navbar-ct-full{font-size:114%;margin:0 7em}.mw-parser-output .navbar-ct-mini{font-size:114%;margin:0 4em}vtePopulation of cities in India"]

Potential HTML Headers = ["vteGeography of India"]

╭──────────────────────────┬─────────────────────────────────────────────────────╮
│ Rank                     │ error: no data found (column name may be incorrect) │
│ City                     │ error: no data found (column name may be incorrect) │
│ Population(2011)[3]      │ error: no data found (column name may be incorrect) │
│ Population(2001)[3][a]   │ error: no data found (column name may be incorrect) │
│ State or union territory │ error: no data found (column name may be incorrect) │
╰──────────────────────────┴─────────────────────────────────────────────────────╯
```
The key here is to look at the `Passed in Column Headers` and compare
them to the `Potential HTML Headers` and couple that with the error
table at the bottom should give you a hint that, in this situation,
wikipedia has changed the column names, yet again. So we need to update
our query web statement's tables to get closer to what we want.

```
❯ http get https://en.wikipedia.org/wiki/List_of_cities_in_India_by_population | query web --as-table [City 'Population(2011)[3]' 'Population(2001)[3][a]' 'State or unionterritory' 'Ref']
╭─#──┬───────City───────┬─Population(2011)[3]─┬─Population(2001)[3][a]─┬─State or unionterritory─┬──Ref───╮
│ 0  │ Mumbai           │ 12,442,373          │ 11,978,450             │ Maharashtra             │ [3]    │
│ 1  │ Delhi            │ 11,034,555          │ 9,879,172              │ Delhi                   │ [3]    │
│ 2  │ Bangalore        │ 8,443,675           │ 5,682,293              │ Karnataka               │ [3]    │
│ 3  │ Hyderabad        │ 6,993,262           │ 5,496,960              │ Telangana               │ [3]    │
│ 4  │ Ahmedabad        │ 5,577,940           │ 4,470,006              │ Gujarat                 │ [3]    │
│ 5  │ Chennai          │ 4,646,732           │ 4,343,645              │ Tamil Nadu              │ [3]    │
│ 6  │ Kolkata          │ 4,496,694           │ 4,580,546              │ West Bengal             │ [3]    │
│ 7  │ Surat            │ 4,467,797           │ 2,788,126              │ Gujarat                 │ [3]    │
│ 8  │ Pune             │ 3,124,458           │ 2,538,473              │ Maharashtra             │ [3]    │
│ 9  │ Jaipur           │ 3,046,163           │ 2,322,575              │ Rajasthan               │ [3]    │
│ 10 │ Lucknow          │ 2,817,105           │ 2,185,927              │ Uttar Pradesh           │ [3]    │
│ 11 │ Kanpur           │ 2,765,348           │ 2,551,337              │ Uttar Pradesh           │ [3]    │
│ 12 │ Nagpur           │ 2,405,665           │ 2,052,066              │ Maharashtra             │ [3]    │
```
# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-02-02 09:03:28 -06:00
0b6f15b69e Disable riscv64 build target temporarily to make release and nightly-build work (#11700)
Let's disable riscv64 build target temporarily to make release and
nightly-build workflow work, close #11604
We will add this target later if the related issue got fixed
2024-02-01 06:44:07 -06:00
16f3d9b4e1 cp: expand target path before checking (#11692)
# Description
Fixes: #11683

# User-Facing Changes
NaN

# Tests + Formatting
~~I don't think we need to add a test, or else it'll copy some file to
user's directory, it seems bad.~~
Done.

# After Submitting
NaN
2024-02-01 09:06:03 +08:00
3e0fa8ff85 Allow 'url join' to print username without password (#11697)
<!--
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
Fixes: https://github.com/nushell/nushell/issues/11677
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

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

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

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` 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 std testing; testing run-tests --path
crates/nu-std"` 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
> ```
-->
```
'https://example.com' | url parse | update scheme ssh | upda
te username user | url join
# => ssh://user@example.com/
'https://example.com' | url parse | update scheme ssh | upda
te password hackme | url join
# => ssh://example.com/
'https://example.com' | url parse | update scheme ssh | update username user | update password hackme | url join
# => ssh://user:hackme@example.com/
```
# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](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: Richard Westhaver <ellis@rwest.io>
2024-01-31 16:52:23 -06:00
d40019a141 Tidy up the REPL main loop (#11655)
While working on #11288 I was having some trouble tracking the main REPL
loop, so I've sent in a bunch of tiny refactorings on this branch.

These are almost all of the "move code from one place to another"
variety, and each commit is meant to be independent, _except for the
last one_, which is trying to be a bit more clever to handle the
decision of autocd'ing vs running a command. Feel free to just go
through each commit and cherry pick the ones that look good.

This leads to `evaluate_repl` going from ending on line 715 to ending on
line 395. Again, this is mostly just moving code around, but I think
this set of changes will make other changes around juggling the stack to
avoid cloning easier to review.
2024-01-31 09:32:19 -08:00
d9b324c5d3 rollback polars 0.37.0 (#11695)
# Description

This PR rolls back the polars updates to 0.37.0 back to 0.36.2 since it
won't compile yet for some reason.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-31 08:39:38 -06:00
00176c5666 Bump wayland (#11694)
# Description
Bumps wayland, and remove `quick-xml v0.30.0` and duplicate `nix
v0.26.4`.
2024-01-31 08:06:59 -06:00
f16ac886a8 change update cells column param from Table to List (#11691)
# Description

This PR fixes `update cells` parameter `--columns`/`-c` so that it takes
a `SyntaxShape::List` instead of `SyntaxShape::Table`.

closes #11689

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-30 19:36:03 -06:00
c77ec0b667 Bump polars-arrow from 0.36.2 to 0.37.0 (#11660) 2024-01-30 22:26:42 +00:00
6779b248e4 Bump sqlparser from 0.41.0 to 0.43.1 (#11662) 2024-01-30 22:26:31 +00:00
0a355db5c0 make the ansi command const (#11682)
# Description

This PR changes the `ansi` command to be a `const` command. 

- ~~It's breaking because I found that I had to change the way `ansi` is
used in scripts a little bit.
https://github.com/nushell/nu_scripts/pull/751~~

- I had to change one of the examples because apparently `const` can't
be tested yet.

- ~~I'm not sure this is right at all
https://github.com/nushell/nushell/pull/11682/files#diff-ba932369a40eb40d6e1985eac1c784af403dab4500a7f0568e593900bf6cd740R654-R655.
I just didn't want to duplicate a ton of code. Maybe if I duplicated the
code it wouldn't be a breaking change because it would have a run and
run_const?~~

- I had to add `opt_const` to CallExt.

/cc @kubouch Can you take a look at this? I'm a little iffy if I'm doing
this right, or even if we should do this at all.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-30 16:09:43 -06:00
cf9813cbf8 Refactor lines command (#11685)
# Description
This PR uses `str::lines` to simplify the `lines` command (and one other
section of code). This has two main benefits:
1. We no longer need to use regex to split on lines, as `str::lines`
splits on `\r\n` or `\n`.
2. We no longer need to handle blank empty lines at the end. E.g.,
`str::lines` results in `["text"]` for both `"test\n"` and `"text"`.

These changes give a slight boost to performance for the following
benchmarks:
1. lines of `Value::String`:
    ```nushell
    let data = open Cargo.lock
    1..10000 | each { $data | timeit { lines } } | math avg 
    ```
    current main: 392µs
    this PR: 270µs
2. lines of external stream:
    ```nushell
    1..10000 | each { open Cargo.lock | timeit { lines } } | math avg 
    ```
    current main: 794µs
    this PR: 489µs
2024-01-30 15:56:19 -06:00
c371d1a535 fix exit_code handling when running a scripts with ctrlc (#11466)
# Description
Fixes: #11394

When run `^sleep 3` we have an `exit_code ListStream`, and when we press
ctrl-c, this `ListStream` will return None. But it's not expected,
because `exit_code` sender in `run_external` always send an exit code
out.

This pr is trying to fix the issue by introducing a `first_guard` into
ListStream, it will always generate a value from underlying stream if
`first_guard` is true, so it's guarantee to have at least one value to
return.

And the pr also do a little refactor, which makes use of
`ListStream::from_stream` rather than construct it manually.

# User-Facing Changes
## Before
```
> nu -c "^sleep 3"  # press ctrl-c
> echo $env.LAST_EXIT_CODE
0
```

## After
```
> nu -c "^sleep 3"  # press ctrl-c
> echo $env.LAST_EXIT_CODE
255
```

# Tests + Formatting
None, sorry that I don't think it's easy to test the ctrlc behavior.

# After Submitting
None
2024-01-30 22:41:14 +08:00
4e0a65c822 Strict JSON parsing (#11592)
# Description
Adds the `--strict` flag for `from json` which will try to parse text
while following the exact JSON specification (e.g., no comments or
trailing commas allowed). Fixes issue #11548.
2024-01-30 08:10:19 -06:00
6530403ff8 Highlights find upgrade (#11509)
this PR should close #9105

# Description
I have implemented highlights for find which work for all strings. The
implementation also works for lists, but with exceptions (for example,
it does not work for list of lists). The implementation is also not
implemented for --regex.

---------

Co-authored-by: Georgiana <geo@LAPTOP-EQP6H37N>
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2024-01-30 08:06:20 -06:00
c08f46f836 Respect SyntaxShape when parsing spread operator (#11674)
<!--
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!
-->

This fixes an issue brought up by nihilander in
[Discord](https://discord.com/channels/601130461678272522/614593951969574961/1201594105986285649).

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

Nushell panics when the spread operator is used like this (the
`...$rest` shouldn't actually be parsed as a spread operator at all):

```nu
$ def foo [...rest: string] {...$rest}                      
$ foo bar baz                                               
thread 'main' panicked at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nu-protocol-0.89.0/src/signature.rs:650:9:
Internal error: can't run a predeclaration without a body
stack backtrace:
   0: rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <nu_protocol::signature::Predeclaration as nu_protocol::engine::command::Command>::run
   3: nu_engine::eval::eval_call
   4: nu_engine::eval::eval_expression_with_input
   5: nu_engine::eval::eval_element_with_input
   6: nu_engine::eval::eval_block
   7: nu_cli::util::eval_source
   8: nu_cli::repl::evaluate_repl
   9: nu::run::run_repl
  10: nu::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

The problem was that whenever the parser saw something like `{...$`,
`{...(`, or `{...[`, it would treat that as a record with a spread
expression, ignoring the syntax shape of the block it was parsing. This
should now be fixed, and the snippet above instead gives the following
error:

```nu
Error: nu:🐚:external_command

  × External command failed
   ╭─[entry #1:1:1]
 1 │  def foo [...rest] {...$rest}
   ·                     ────┬───
   ·                         ╰── executable was not found
   ╰────
  help: No such file or directory (os error 2)
```

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

Stuff like `do { ...$rest }` will now try to run a command `...$rest`
rather than complaining that variable `$rest` doesn't exist.

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

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->

Sorry about the issue, I am not touching the parser again for a long
time :)
2024-01-30 13:49:42 +08:00
175dab4898 "[11611] fixing dataframe column comparisons" (#11676)
fixes #11611

Co-authored-by: Jack Wright <jack.wright@disqo.com>
2024-01-29 17:28:12 -06:00
798ae7b251 Fix precedence of 'not' operator (#11672)
<!--
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

A bit hackish but this fixes the precedence of the `not` operator.

Before: `not false and false` => true

Now: `not false and false` => false

Fixes #11633

# 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 std testing; testing run-tests --path
crates/nu-std"` 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: Jakub Žádník <kubouch@gmail.com>
2024-01-29 21:42:27 +02:00
f879c00f9d The ability to specify a schema when using dfr open and dfr into-df (#11634)
# Description

There are times where explicitly specifying a schema for a dataframe is
needed such as:
- Opening CSV and JSON lines files and needing provide more information
to polars to keep it from failing or in a desire to override default
type conversion
- When converting a nushell value to a dataframe and wanting to override
the default conversion behaviors.

This pull requests provides:
- A flag to allow specifying a schema when using dfr into-df
- A flag to allow specifying a schema when using dfr open that works for
CSV and JSON types
- A new command `dfr schema` which displays schema information and will
allow display support schema dtypes

Schema is specified creating a record that has the key value and the
dtype. Examples usages:

```
{a:1, b:{a:2}} | dfr into-df -s {a: u8, b: {a: i32}} | dfr schema
{a: 1, b: {a: [1 2 3]}, c: [a b c]} | dfr into-df -s {a: u8, b: {a: list<u64>}, c: list<str>} | dfr schema
 dfr open -s {pid: i32, ppid: i32, name: str, status: str, cpu: f64, mem: i64, virtual: i64} /tmp/ps.jsonl  | dfr schema
```

Supported dtypes:
null                                                   
bool                                                   
u8                                                     
u16                                                    
u32                                                    
u64                                                    
i8                                                     
i16                                                    
i32                                                    
i64                                                    
f32                                                    
f64                                                    
str                                                    
binary                                                 
date                                                   
datetime[time_unit: (ms, us, ns) timezone (optional)]  
duration[time_unit: (ms, us, ns)]                      
time                                                   
object                                                 
unknown                                                
list[dtype]


structs are also supported but are specified via another record:
{a: u8, b: {d: str}}

Another feature with the dfr schema command is that it returns the data
back in a format that can be passed to provide a valid schema that can
be passed in as schema argument:

<img width="638" alt="Screenshot 2024-01-29 at 10 23 58"
src="https://github.com/nushell/nushell/assets/56345/b49c3bff-5cda-4c86-975a-dfd91d991373">

---------

Co-authored-by: Jack Wright <jack.wright@disqo.com>
2024-01-29 13:26:04 -06:00
d03ad6a257 update to latest reedline for the quick completions fix (#11673)
# Description

This PR updates nushell to the latest reedline main to fix the quick
completions.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-29 13:22:07 -06:00
14e4d05a9f use constant instead of <0 for ls fix (#11642)
https://github.com/nushell/nushell/pull/10558#issuecomment-1911076784

more correct than <0.  safer check.
2024-01-29 13:17:04 -06:00
86dd045554 add match-text style + config setting for ide menu (#11670)
the match-text style (https://github.com/nushell/reedline/pull/730) is
now configurable via the config.nu file.
the option ``correct_cursor_pos`` can now also be set in the config.nu
file.
2024-01-29 09:59:01 -06:00
0e023eaa84 add str escape-glob command (#11664)
# Description
This pr is a follow up to #11621, it introduces a `str escape-glob`
command as a workaround for the case:

```nushell
let f = "a[123]b"
ls $f
```

It will glob `a[123]b`, we can get rid of the behavior through `str
escape-glob` command:

```nushll
let f = "a[123]b"
ls ($f | str escape-glob)
```

It's more useful in the `each` context:
`ls | get name | str escape-glob | each {|it| ls $it}`

# User-Facing Changes
NaN

# Tests + Formatting
Done

# After Submitting

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2024-01-29 23:00:15 +08:00
427857a78e Fix wrong error for raw streams in into record (#11668)
Fix #11632
2024-01-29 08:32:43 -06:00
eea3f79c3c Bump ical from 0.9.0 to 0.10.0 (#11661)
Bumps [ical](https://github.com/Peltoche/ical-rs) from 0.9.0 to 0.10.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Peltoche/ical-rs/releases">ical's
releases</a>.</em></p>
<blockquote>
<h2>v0.10.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix newlines by <a
href="https://github.com/westy92"><code>@​westy92</code></a> in <a
href="https://redirect.github.com/Peltoche/ical-rs/pull/56">Peltoche/ical-rs#56</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/Peltoche/ical-rs/compare/v0.9.0...v0.10.0">https://github.com/Peltoche/ical-rs/compare/v0.9.0...v0.10.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2e83429867"><code>2e83429</code></a>
chore: Release ical version 0.10.0</li>
<li><a
href="6c265a53f0"><code>6c265a5</code></a>
Add test to verify parser handles \r\n.</li>
<li><a
href="79b0e6501b"><code>79b0e65</code></a>
Fixes &amp; clippy.</li>
<li><a
href="821bd46bfb"><code>821bd46</code></a>
Fix split lines.</li>
<li><a
href="8bb242a4ba"><code>8bb242a</code></a>
Fix newlines.</li>
<li>See full diff in <a
href="https://github.com/Peltoche/ical-rs/compare/v0.9.0...v0.10.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ical&package-manager=cargo&previous-version=0.9.0&new-version=0.10.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>
2024-01-29 09:34:06 +08:00
b004e80f77 Bump Reedline for the Menu Refactor (#11658)
* [Refactor Menu System with Composition of Menu
Functions](https://github.com/nushell/reedline/issues/706)
* Move Description Menu over to Reedline to consolidate location of the
Menus which will simplify further changes and maintenance to the Menu
system going forward
* Removes lots of code duplication in the Menu system on the Reedline
side which should ease a developers ability to develop new cool menus
for Reedline moving forward
2024-01-28 08:26:03 -08:00
25b62c2ac3 fix force rm: should suppress error if directory is not found (#11656)
# Description
Fix a breaking change which is introduced by #11621

`rm -f /tmp/aaa` shouldn't return error if `/tmp/aaa/` doesn't exist.

# User-Facing Changes
NaN

# Tests + Formatting
Done
2024-01-28 09:01:19 -06:00
859f7b3dc7 add $.extra_usage to modules (#11649)
- should fix https://github.com/nushell/nushell/issues/11648

# Description
this PR
- adds a test that should pass but fails
- adds `$.extra_usage` to the output of `scope modules`, fixing both the
new test and the linked issue

# User-Facing Changes
`$.extra_usage` is now a column in the output of `scope modules`

# Tests + Formatting
a new test case has been added to `correct_scope_modules_fields`

# After Submitting
2024-01-27 17:49:21 +02:00
39b020037d update Reedline so we can begin testing the menu refactor (#11647)
We would like to update Reedline to the latest code on the main
branch...

So we can start testing the menu refactor work...
2024-01-26 16:33:15 -08:00
1334de72b0 make the input_output_types match on each and par-each (#11645)
# Description
@Yethal had a strange problem where a script would work with `each` but
not `par-each`. I found this to be because the `input_output_types` were
different. These io types are weird and I don't think they're correct
but at least they're the same now.

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

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-26 08:45:02 -06:00
56acebb826 making empty list matches list<int> types (#11596)
# Description
Fixes: #11595

The original issue is caused by #11475, we also need to make empty list
matches `list type` or `table type`

cc @amtoine 

# User-Facing Changes
Nan

# Tests + Formatting
Done
2024-01-26 22:24:17 +08:00
d646903161 Unify glob behavior on open, rm, cp-old, mv, umv, cp and du commands (#11621)
# Description
This pr is a follow up to
[#11569](https://github.com/nushell/nushell/pull/11569#issuecomment-1902279587)
> Revert the logic in https://github.com/nushell/nushell/pull/10694 and
apply the logic in this pr to mv, cp, rv will require a larger change, I
need to think how to achieve the bahavior

And sorry @bobhy for reverting some of your changes.

This pr is going to unify glob behavior on the given commands:
* open
* rm
* cp-old
* mv
* umv
* cp
* du

So they have the same behavior to `ls`, which is:
If given parameter is quoted by single quote(`'`) or double quote(`"`),
don't auto-expand the glob pattern. If not quoted, auto-expand the glob
pattern.

Fixes: #9558  Fixes: #10211 Fixes: #9310 Fixes: #10364 

# TODO
But there is one thing remains: if we give a variable to the command, it
will always auto-expand the glob pattern, e.g:
```nushell
let path = "a[123]b"
rm $path
```
I don't think it's expected. But I also think user might want to
auto-expand the glob pattern in variables.

So I'll introduce a new command called `glob escape`, then if user
doesn't want to auto-expand the glob pattern, he can just do this: `rm
($path | glob escape)`

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

# Tests + Formatting
Done

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

## NOTE
This pr changes the semantic of `GlobPattern`, before this pr, it will
`expand path` after evaluated, this makes `nu_engine::glob_from` have no
chance to glob things right if a path contains glob pattern.

e.g: [#9310
](https://github.com/nushell/nushell/issues/9310#issuecomment-1886824030)
#10211

I think changing the semantic is fine, because it makes glob works if
path contains something like '*'.

It maybe a breaking change if a custom command's argument are annotated
by `: glob`.
2024-01-26 21:57:35 +08:00
e43d893ea3 fix panic caused by ls \\.\pipe (#10558)
wrap chrono in panic hooks to handle panic'ing unwraps on Jan 1, 1601
00:00 UTC and other reasons unknown. An overflow if time_u64 is smaller
than EPOCH_AS_FILETIME has been wrapped.

Further discussion
https://github.com/nushell/nushell/issues/10464

There are two issues that are associated with Chrono. I did not test. It
may not relate, but it could.
thread 'main' panicked at 'SystemTimeToFileTime failed with: The
parameter is incorrect.
https://github.com/nushell/nushell/issues/6574
https://github.com/nushell/nushell/issues/9470

# Description

I'm not a fan of this code that was pulled from chrono. negative seconds
and nano seconds?
```rust
    // Adapted from https://github.com/chronotope/chrono/blob/v0.4.19/src/datetime.rs#L755-L767.
    let (sec, nsec, was_success) = match t.duration_since(UNIX_EPOCH) {
        Ok(dur) => {
            (dur.as_secs() as i64, dur.subsec_nanos(),true)
        },
        Err(e) => {
            // unlikely but should be handled
            let dur = e.duration();
            let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos());
            if nsec == 0 {
                (-sec, 0,false)
            } else {
                (-sec - 1, 1_000_000_000 - nsec,false)
            }
        }
    };
```

There's more on the #10464 ticket;


# User-Facing Changes

Use ls and it will not crash when listing windows pipes 
ls \\.\pipe.

# Tests + Formatting

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
DONE

- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
that command yields:

```rust
error: casting raw pointers to the same type and constness is unnecessary (`*mut u16` -> `*mut u16`)
   --> crates\nu-system\src\windows.rs:972:13
    |
972 |             name.as_mut_ptr() as *mut u16,
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `name.as_mut_ptr()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
    = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]`

error: casting raw pointers to the same type and constness is unnecessary (`*mut u16` -> `*mut u16`)
   --> crates\nu-system\src\windows.rs:974:13
    |
974 |             domainname.as_mut_ptr() as *mut u16,
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `domainname.as_mut_ptr()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast

error: could not compile `nu-system` (lib) due to 2 previous errors
```


TBD
- `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 std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library
2024-01-25 15:41:22 -06:00
f03d81b0c8 fix spreading of arguments to externals in toolkit (#11640)
as per title, that's just a simple change to avoid the deprecation
warning introduced recently and that will be removed in `0.91`
2024-01-25 19:40:51 +01:00
7f1fd7699e external completer: support style (#11442)
<!--
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

Adds style support.

Reverse highlighting is recommended for the
[completion-menu](https://www.nushell.sh/book/line_editor.html#completion-menu):
```nushell
style: {
    text: white
    selected_text: {
        attr: r
    }
    description_text: white_dimmed
}
```

needs https://github.com/nushell/reedline/pull/691
related https://github.com/nushell/nushell/issues/5292
fix https://github.com/rsteube/carapace/issues/967

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

Style support for values during completion:


![image](https://github.com/nushell/nushell/assets/9090290/f5e4440e-61db-4eeb-87b3-f887b6918c50)


![image](https://github.com/nushell/nushell/assets/9090290/e59065a6-58c2-4f5f-82ea-e2b9c2464b9a)

<!-- 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-25 08:21:28 -06:00
a3f1116ea8 cleanup hide testing PR (#11638)
# Description

This PR cleans up https://github.com/nushell/nushell/pull/11331. One
line was missed that caused the CI to break.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-25 06:49:04 -06:00
ef1d70eb67 hide std testing (#11331)
follow-up to
- https://github.com/nushell/nushell/pull/11151

> **Important**
> land only between 0.89 and 0.90

# Description
this PR hides the `std testing` module from the outside.
- moves `nu-std/std/testing.nu` to `nu-std/testing.nu`
- removes the module from the standard library list of modules to parse
- fixes `toolkit.nu` and the CI

# User-Facing Changes
`std testing` won't be part of the standard library anymore.
# Tests + Formatting

# After Submitting
2024-01-25 12:50:07 +02:00
cffce7f4b2 properly convert env for buffer editor (#11636)
<!--
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

environment variables were not correctly passed to `buffer_editor`.
Previously the conversion (if you can call it that) is incorrectly done
by `.as_string()`, which works for the most part, but ignores important
things like `PATH`, which are not strings in value form by default

(this issue was introduced in #10269, merged in #10535)

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-25 09:31:46 +02:00
a4809d2f08 Remove --flag: bool support (#11541)
# Description
This is a follow up to: #11365

After this pr, `--flag: bool` is no longer allowed.

I think `ParseWarning::Deprecated` is useful when we want to deprecated
something at syntax level, so I just leave it there for now.

# User-Facing Changes
## Before
```
❯ def foo [--b: bool] {}
Error:   × Deprecated: --flag: bool
   ╭─[entry #15:1:1]
 1 │ def foo [--b: bool] {}
   ·               ──┬─
   ·                 ╰── `--flag: bool` is deprecated and will be removed in 0.90. Please use `--flag` instead, more info: https://www.nushell.sh/book/custom_commands.html
   ╰────
```

## After
```
❯ def foo [--b: bool] {}
Error:   × Type annotations are not allowed for boolean switches.
   ╭─[entry #2:1:1]
 1 │ def foo [--b: bool] {}
   ·               ──┬─
   ·                 ╰── Remove the `: bool` type annotation.
   ╰────
```
# Tests + Formatting
Done
2024-01-25 14:16:49 +08:00
f286286510 update nu-ansi-term to 0.50, lscolors to 0.17, and add the Style attribute to Suggestion (#11635)
* update nu-ansi-term to 0.50.0
* update lscolors to 0.17.0
* add the Style attribute to Suggestion
* bump Reedline to the latest main
2024-01-24 20:57:15 -08:00
2a65d43c13 Add into cell-path for dynamic cell-path creation (#11322)
# Description

The `cell-path` is a type that can be created statically with
`$.nested.structure.5`, but can't be created from user input. This makes
it difficult to take advantage of commands that accept a cell-path to
operate on data structures.

This PR adds `into cell-path` for dynamic cell-path creation.

`into cell-path` accepts the following input shapes:
* Bare integer (equivalent to `$.1`)
* List of strings and integers
* List of records with entries `value` and `optional`
* String (parsed into a cell-path)

## Example usage

An example of where `into cell-path` can be used is in working with `git
config --list`. The git configuration has a tree structure that maps
well to nushell records. With dynamic cell paths it is easy to convert
`git config list` to a record:

```nushell
git config --list
| lines
| parse -r '^(?<key>[^=]+)=(?<value>.*)'
| reduce --fold {} {|entry, result|
  let path = $entry.key | into cell-path

  $result
  | upsert $path {||
    $entry.value
  }
}
| select remote
```

Output:

```
╭────────┬──────────────────────────────────────────────────────────────────╮
│        │ ╭──────────┬───────────────────────────────────────────────────╮ │
│ remote │ │          │ ╭───────┬───────────────────────────────────────╮ │ │
│        │ │ upstream │ │ url   │ git@github.com:nushell/nushell.git    │ │ │
│        │ │          │ │ fetch │ +refs/heads/*:refs/remotes/upstream/* │ │ │
│        │ │          │ ╰───────┴───────────────────────────────────────╯ │ │
│        │ │          │ ╭───────┬─────────────────────────────────────╮   │ │
│        │ │ origin   │ │ url   │ git@github.com:drbrain/nushell      │   │ │
│        │ │          │ │ fetch │ +refs/heads/*:refs/remotes/origin/* │   │ │
│        │ │          │ ╰───────┴─────────────────────────────────────╯   │ │
│        │ ╰──────────┴───────────────────────────────────────────────────╯ │
╰────────┴──────────────────────────────────────────────────────────────────╯
```

## Errors

`lex()` + `parse_cell_path()` are forgiving about what is allowed in a
cell-path so it will allow what appears to be nonsense to become a
cell-path:

```nushell
let table = [["!@$%^&*" value]; [key value]]

$table | get ("!@$%^&*.0" | into cell-path)
# => key
```

But it will reject bad cell-paths:

```
❯ "a b" | into cell-path
Error: nu:🐚:cant_convert

  × Can't convert to cell-path.
   ╭─[entry #14:1:1]
 1 │ "a b" | into cell-path
   ·         ───────┬──────
   ·                ╰── can't convert string to cell-path
   ╰────
  help: "a b" is not a valid cell-path (Parse mismatch during operation.)
```

# User-Facing Changes

New conversion command `into cell-path`

# Tests + Formatting

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

# After Submitting

Automatic documentation updates
2024-01-24 16:20:46 -06:00
0aabe84460 Added --index flag to input list (#11580)
# Description
This PR closes #11571 

Add `--index` flag to input list.

For example:

![image](https://github.com/nushell/nushell/assets/72006223/19efb011-1ff8-4916-b2bd-6f73e89cb186))
 
# Tests + Formatting
 Added new example for `--index` flag.
2024-01-24 11:57:29 -06:00
323207ca1d Adapt tests for internationalization (#11628)
I've noticed that two tests fail on my system when running `toolkit
check pr`. The reason for this is that my locale is set to German. `ls`
is translated, so checking the error message will only work on systems
set to English.

I've adapted the test to check the exit code instead.

Alternatively, we could set the locale, I am not sure if the `nu!` macro
supports that though.
2024-01-24 11:55:27 -06:00
e35376f1e7 Bump heapless from 0.7.17 to 0.8.0
Bumps [heapless](https://github.com/japaric/heapless) from 0.7.17 to 0.8.0.
- [Changelog](https://github.com/rust-embedded/heapless/blob/main/CHANGELOG.md)
- [Commits](https://github.com/japaric/heapless/compare/v0.7.17...v0.8.0)

---
updated-dependencies:
- dependency-name: heapless
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-24 16:24:17 +00:00
7e37e4bb5f Bump sqlparser from 0.39.0 to 0.41.0
Bumps [sqlparser](https://github.com/sqlparser-rs/sqlparser-rs) from 0.39.0 to 0.41.0.
- [Changelog](https://github.com/sqlparser-rs/sqlparser-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sqlparser-rs/sqlparser-rs/compare/v0.39.0...v0.41.0)

---
updated-dependencies:
- dependency-name: sqlparser
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-24 15:49:09 +00:00
a44ad949f1 Bump polars from 0.35 to 0.36 (#11624)
# Description
* release notes
 https://github.com/pola-rs/polars/releases/tag/rs-0.36.2

* dependencies 
remove `sysinfo` 0.29.11
add `polars-compute` 0.36.2

# User-Facing Changes
[Change value_counts resulting column name from counts to
count](https://github.com/pola-rs/polars/pull/12506)

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

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-24 09:27:06 -06:00
4105255a5a resolving external highlight should take current PATH into account (#11618)
<!--
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

When resolving external commands, the current `PATH` was not taken into
account.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-24 09:02:53 -06:00
ff5815c0a3 remove cp-old (#11622)
# Description

The `cp-old` command has been deprecated for a few releases now. It
should be safe to remove it once and for all. Let's see.

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

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-24 07:38:15 +08:00
3af4f34f11 Bump shlex from 1.2.0 to 1.3.0 (#11616)
Bumps [shlex](https://github.com/comex/rust-shlex) from 1.2.0 to 1.3.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md">shlex's
changelog</a>.</em></p>
<blockquote>
<h1>1.3.0</h1>
<ul>
<li>Full fix for the high-severity security vulnerability <a
href="https://rustsec.org/advisories/RUSTSEC-2024-0006.html">RUSTSEC-2024-0006</a>
a.k.a. <a
href="https://github.com/comex/rust-shlex/security/advisories/GHSA-r7qv-8r2h-pg27">GHSA-r7qv-8r2h-pg27</a>:
<ul>
<li>Deprecates quote APIs in favor of <code>try_</code> equivalents that
complain about nul bytes.</li>
<li>Also adds a builder API, which allows re-enabling nul bytes without
using the deprecated interface, and in the future can allow other things
(as discussed in quoting_warning).</li>
<li>Adds documentation about various security risks that remain,
particularly with interactive shells.</li>
</ul>
</li>
<li>Adds explicit MSRV of 1.46.0.</li>
</ul>
<h1>1.2.1</h1>
<ul>
<li>Partial fix for the high-severity security vulnerability <a
href="https://rustsec.org/advisories/RUSTSEC-2024-0006.html">RUSTSEC-2024-0006</a>
a.k.a. <a
href="https://github.com/comex/rust-shlex/security/advisories/GHSA-r7qv-8r2h-pg27">GHSA-r7qv-8r2h-pg27</a>
without bumping MSRV:
<ul>
<li>The bytes <code>{</code> and <code>\xa0</code> are now escaped by
quoting functions.</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/comex/rust-shlex/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=shlex&package-manager=cargo&previous-version=1.2.0&new-version=1.3.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)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/nushell/nushell/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-22 16:21:02 -06:00
092d496ff5 Plugin explicit flags (#11581)
# Description
#11492 fixed flags for builtin commands but I missed that plugins don't
use the same `has_flag` that builtins do. This PR addresses this.

Unfortunately this means that return value of `has_flag` needs to change
from `bool` to `Result<bool, ShellError>` to produce an error when
explicit value is not a boolean (just like in case of `has_flag` for
builtin commands. It is not possible to check this in
`EvaluatedCall::try_from_call` because

# User-Facing Changes
Passing explicit values to flags of plugin commands (like `--flag=true`
`--flag=false`) should work now.
BREAKING: changed return value of `EvaluatedCall::has_flag` method from
`bool` to `Result<bool, ShellError>`

# Tests + Formatting
Added tests and updated documentation and examples
2024-01-22 15:00:43 -06:00
415ebf207f Remove duplicate which 4.4.2 (#11613)
# Description

`which` 5.0.0 is already in the dependency tree, so remove v4.4.2

Related: https://github.com/nushell/nushell/issues/8060
2024-01-22 09:28:47 -06:00
aaac273cd0 Fix regression in help menu introduced by #11488 (#11608)
<!--
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!
-->

For fixing #11599

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

Turns out I didn't properly test the description menu in #11488,
apologies for that. It turns out that `NuHelpCompleter`, which provides
completions for the description/help menu, was treating the position it
was given as the start of the line rather than the start. Flipping that
appears to fix the issue.

I missed not only `NuHelpCompleter` but also `NuMenuCompleter` in my
previous PR. If the menu's source is a closure and it doesn't return a
record with the start and end, then `pos` is again treated as the start,
so I've changed that too. External completers shouldn't need changing.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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
> ```
-->

- [X] Test description menu
- [X] Test menu sources that return records that don't have `start` and
`end`
- [ ] <s>Test external completers if any changes have to be made
there</s> No changes needed, it looks like

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2024-01-22 07:01:45 -06:00
90d65bb987 Evaluate string interpolation at parse time (#11562)
<!--
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!
-->

Closes #11561

# 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 will allow string interpolation at parse time.

Since the actual config hasn't been loaded at parse time, this uses the
`get_config()` method on `StateWorkingSet`. So file sizes and datetimes
(I think those are the only things whose string representations depend
on the config) may be formatted differently from how users have
configured things, which may come as a surprise to some. It does seem
unlikely that anyone would be formatting file sizes or date times at
parse time. Still, something to think about if/before this PR merged.

Also, I changed the `ModuleNotFound` error to include the name of the
module.

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

Users will be able to do stuff like:

```nu
const x = [1 2 3]
const y = $"foo($x)" // foo[1, 2, 3]
```

The main use case is `use`-ing and `source`-ing files at parse time:

```nu
const file = "foo.nu"
use $"($file)"
```

If the module isn't found, you'll see an error like this:
```
Error: nu::parser::module_not_found

  × Module not found.
   ╭─[entry #3:1:1]
 1 │  use $"($file)"
   ·      ─────┬────
   ·           ╰── module foo.nu not found
   ╰────
  help: module files and their paths must be available before your script is run as parsing occurs before anything is evaluated
```

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

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->

Although there's user-facing changes, there's probably no need to change
the docs since people probably already expect string interpolation to
work at parse time.

Edit: @kubouch pointed out that we'd need to document the fact that
stuff like file sizes and datetimes won't get formatted according to
user's runtime configs, so I'll make a PR to nushell.github.io after
this one
2024-01-22 09:13:48 +02:00
4c5a8c1804 Bump crate-ci/typos from 1.17.1 to 1.17.2
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.17.1 to 1.17.2.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.17.1...v1.17.2)

---
updated-dependencies:
- dependency-name: crate-ci/typos
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 01:46:37 +00:00
188aca8fe6 Upgrade byte-unit from 4.0 to 5.1 (#11584)
<!--
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 is for using version 5.1 of
[byte_unit](https://docs.rs/byte-unit/latest/byte_unit/index.html)
instead of 4.0. dependabot opened
https://github.com/nushell/nushell/pull/11499 to do this but it's a
major version increment so some minor changes were necessary.

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

If something is on the boundary of a unit (e.g. 1024 bytes = 1
kibibytes), that will now be formatted as `1.0 KiB` where it used to be
formatted as `1,024 B`.

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

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-21 14:17:28 -06:00
c59d6d31bc do not attempt to glob expand if the file path is wrapped in quotes (#11569)
# Description
Fixes: #11455

### For arguments which is annotated with `:path/:directory/:glob`
To fix the issue, we need to have a way to know if a path is originally
quoted during runtime. So the information needed to be added at several
levels:
* parse time (from user input to expression)
We need to add quoted information into `Expr::Filepath`,
`Expr::Directory`, `Expr::GlobPattern`
* eval time
When convert from `Expr::Filepath`, `Expr::Directory`,
`Expr::GlobPattern` to `Value::String` during runtime, we won't auto
expanded the path if it's quoted

### For `ls`
It's really special, because it accepts a `String` as a pattern, and it
generates `glob` expression inside the command itself.

So the idea behind the change is introducing a special SyntaxShape to
ls: `SyntaxShape::LsGlobPattern`. So we can track if the pattern is
originally quoted easier, and we don't auto expand the path either.

Then when constructing a glob pattern inside ls, we check if input
pattern is quoted, if so: we escape the input pattern, so we can run `ls
a[123]b`, because it's already escaped.
Finally, to accomplish the checking process, we also need to introduce a
new value type called `Value::QuotedString` to differ from
`Value::String`, it's used to generate an enum called `NuPath`, which is
finally used in `ls` function. `ls` learned from `NuPath` to know if
user input is quoted.

# User-Facing Changes
Actually it contains several changes
### For arguments which is annotated with `:path/:directory/:glob`
#### Before
```nushell
> def foo [p: path] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
> def foo [p: directory] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
> def foo [p: glob] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
```
#### After
```nushell
> def foo [p: path] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
> def foo [p: directory] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
> def foo [p: glob] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
```
### For ls command
`touch '[uwu]'`
#### Before
```
❯ ls -D "[uwu]"
Error:   × No matches found for [uwu]
   ╭─[entry #6:1:1]
 1 │ ls -D "[uwu]"
   ·       ───┬───
   ·          ╰── Pattern, file or folder not found
   ╰────
  help: no matches found
```

#### After
```
❯ ls -D "[uwu]"
╭───┬───────┬──────┬──────┬──────────╮
│ # │ name  │ type │ size │ modified │
├───┼───────┼──────┼──────┼──────────┤
│ 0 │ [uwu] │ file │  0 B │ now      │
╰───┴───────┴──────┴──────┴──────────╯
```

# Tests + Formatting
Done

# After Submitting
NaN
2024-01-21 23:22:25 +08:00
64f34e0287 allow math avg to work with durations (#11598)
# Description

I ran into a problem where one of our benchmark tests in nu_scripts
wouldn't work because math avg wouldn't work with durations, so I made
these changes to support it. I'm confident that there are other math
commands that probably need this "fix".

Side note - we should really fix our inout_output_type system.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-21 08:41:23 -06:00
6edf91dcae Allow string to copmpare with another string (#11590)
# Description

Nushell parser now reject comparison operator with 2 strings (e.g.
`"abc" < "cba"`). This pr fixes it.

## before

```nu
~
❯ "abc" < "bca"
Error: nu::parser::unsupported_operation

  × less-than comparison is not supported on values of type string
   ╭─[entry #43:1:1]
 1 │ "abc" < "bca"
   · ──┬── ┬
   ·   │   ╰── doesn't support this value
   ·   ╰── string
   ╰────


~
❯ def foo []: nothing -> string { "abc" }

~
❯ (foo) < "bca"
Error: nu::parser::unsupported_operation

  × less-than comparison is not supported on values of type string
   ╭─[entry #53:1:1]
 1 │ (foo) < "bca"
   · ──┬── ┬
   ·   │   ╰── doesn't support this value
   ·   ╰── string
   ╰────
```

## after

```nu
~
❯ "abc" < "bca"
true

~
❯ def foo []: nothing -> string { "abc" }

~
❯ (foo) < "bca"
true
```

# User-Facing Changes

Following pattern will be allowed.

| operator | type of lhs | type of rhs | result |
| -------- | ----------- | ----------- | ------ |
| `<`      | string      | string      | bool   |
| `<=`     | string      | string      | bool   |
| `>`      | string      | string      | bool   |
| `>=`     | string      | string      | bool   |

# Tests + Formatting

- [x] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used`
to check that you're using the standard code style
- [x] `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))
- [x] `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library

# After Submitting
2024-01-21 07:43:40 -06:00
bc872a1a2a update Cargo.lock with the latest reedline (#11594)
This updates the Cargo.lock file for reedline which will enable testing
of this Reedline PR

https://github.com/nushell/reedline/pull/653
2024-01-20 19:53:24 -08:00
ea1bd9f8f9 IDE style completion (#11593)
<!--
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 an IDE-Style completion menu

![grafik](https://github.com/nushell/nushell/assets/104733404/df7f1039-2bbc-42f7-9501-fe28507b5cfe)

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-20 18:14:02 -06:00
4458aae3d4 update reedline (#11589)
update to support the latest reedline changes from
https://github.com/nushell/reedline/pull/696
2024-01-20 08:39:20 -06:00
e7a4af14cd Add shift + navigation functionality through reedline (#11535)
This PR should close #1171

# 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 introduces the capability to select text using the existing
move.. `EditCommand`s of `reedline`. Those commands are extended with an
optional parameter specifying if text should be selected while
navigating. This enables a workflow familiar from a wide variety of text
editors, where holding `shift` while navigating selects all text between
the initial cursor position when pressing `shift` and the current cursor
position.

Before this PR can be merged the [sibling PR for
reedline](https://github.com/nushell/reedline/pull/689) has to land
first.

# User-Facing Changes
## Additional `EditCommand`s
1. `SelectAll`
2. `CutSelection`
3. `CopySelection`
## New optional parameter on existing `EditCommand`s
All `EditCommand`s of `EditType` `MoveCursor` have a new optional
parameter named `select` of type `bool`. If this parameter is not set by
a user it is treated as false, which corresponds to their behavior up to
now.

I am relatively new to `nushell` and as such may not know of existing
behavior that might change through this PR. However, I believe there
should be none. I come to this conclusion because
1. Existing commands are extended only with an *optional* additional
parameter, users who currently use these EditCommands keep their
existing behavior if they don't use it.
2. A few new commands are introduced which were previously not valid.
3. The default keybindings specified in `default_config.nu` are
untouched.

# Tests + Formatting
Tests for the new optional parameter for the move commands are included
to make sure that they truly are optional and an unused optional
parameter conforms to the previous behavior.
2024-01-20 08:04:06 -06:00
90095c72f6 Bump h2 from 0.3.22 to 0.3.24 (#11579)
Bumps [h2](https://github.com/hyperium/h2) from 0.3.22 to 0.3.24.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/hyperium/h2/releases">h2's
releases</a>.</em></p>
<blockquote>
<h2>v0.3.24</h2>
<h2>Fixed</h2>
<ul>
<li>Limit error resets for misbehaving connections.</li>
</ul>
<h2>v0.3.23</h2>
<h2>What's Changed</h2>
<ul>
<li>cherry-pick fix: streams awaiting capacity lockout in <a
href="https://redirect.github.com/hyperium/h2/pull/734">hyperium/h2#734</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/hyperium/h2/blob/v0.3.24/CHANGELOG.md">h2's
changelog</a>.</em></p>
<blockquote>
<h1>0.3.24 (January 17, 2024)</h1>
<ul>
<li>Limit error resets for misbehaving connections.</li>
</ul>
<h1>0.3.23 (January 10, 2024)</h1>
<ul>
<li>Backport fix from 0.4.1 for stream capacity assignment.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7243ab5854"><code>7243ab5</code></a>
Prepare v0.3.24</li>
<li><a
href="d919cd6fd8"><code>d919cd6</code></a>
streams: limit error resets for misbehaving connections</li>
<li><a
href="a7eb14a487"><code>a7eb14a</code></a>
v0.3.23</li>
<li><a
href="b668c7fbe2"><code>b668c7f</code></a>
fix: streams awaiting capacity lockout (<a
href="https://redirect.github.com/hyperium/h2/issues/730">#730</a>) (<a
href="https://redirect.github.com/hyperium/h2/issues/734">#734</a>)</li>
<li>See full diff in <a
href="https://github.com/hyperium/h2/compare/v0.3.22...v0.3.24">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=h2&package-manager=cargo&previous-version=0.3.22&new-version=0.3.24)](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)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/nushell/nushell/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 04:27:24 +08:00
f12f590d82 update deps calamine and quick-xml (#11582)
# Description

This PR updates a few outdated dependencies.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-19 12:23:51 -06:00
c8f30fa3bf Fix parsing of strings with special characters (#11030)
<!--
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.
-->
If there were brackets in a string argument of a script it was always
interpreted as interpolation before the change. That lead to unexpected
outputs of such scripts. After this change arguments which are not
intended as interpolation (not starting with $) and containing brackets
will have implicitly added backticks for correct interpretation in the
scripts. This fixes #10908.

To fix other issues mentioned in #11035 I changed the deparsing logic.
Initially we added backticks for multi word variables and double quote
if there was \ or " in the string. My change would add double quotes any
time string starts with $ or contains any of character that might break
parsing. The characters I identified are white space, (, ', `, ",and \.
It's possible other characters should be added to this list.

I tested this solution with few simple scripts using both stand alone
arguments and flags and it seems to work but I would appreciate if
someone with more experience checked it with some more unusual cases I
missed.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Erroneous behaviour described  in the issue will no longer happen.

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

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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
> ```
-->
Added tests for new formatting.

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2024-01-19 10:20:14 -06:00
ff290a5c3d Add self-closed tag support for to xml (#11577)
# Description
This PR closes #11524
Add `to xml --self-closed` flag to output empty tags as self close.
For example:

![image](https://github.com/nushell/nushell/assets/17511668/bdf040f7-8ac1-4e8b-80bb-0043d7cec7f9)


# User-Facing Changes
New `to xml` flag `--self-closed`.

# Tests + Formatting
Added new example for `to xml` command and new test for self-closed
tags.
2024-01-19 05:35:29 -06:00
56067da39c Send only absolute paths to uu_mv (#11576)
# Description
Fixes: #11127 

It's something similar to #11080, applying the same logic to `uu_mv`.

# User-Facing Changes

# After Submitting
2024-01-19 05:34:18 -06:00
5d63f47c85 Replace htmlescape with v_htmlescape (#11572)
# Description

`htmlescape` is unmaintained: https://crates.io/crates/htmlescape

while `v_htmlescape` is: https://crates.io/crates/v_htmlescape

and is used by two popular crates (`actix-files` and `minijinja`)

Let's use this instead - I'm packaging `nu` in Fedora and there is
understandable reluctance in bringing in an unmaintained crate if we can
avoid it.

# User-Facing Changes
Should not be any; drop-in replacement

# Tests + Formatting
Tested using:
- `cargo build` in the root folder (needed by some `nu-command` tests)
- `cargo test --features sqlite` in `crates/nu-command`
(`tests/commands/database/into_sqlite.rs` needs `rusqlite`)
- `cargo test` in `crates/nu-cmd-extra`

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](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

Signed-off-by: Michel Lind <salimma@fedoraproject.org>
2024-01-18 12:58:35 -06:00
ee6547dbb7 Initial implementation of umv from uutils (#10822)
<!--
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
Hi,
This closes #10446 , wherein we start implementing `mv` from `uutils`.
There are some stuff to iron out, particularly
* Decide on behavior from ignored tests 
* Wait for release/PRs to be approved on `uutils` side, but still can be
tested for now. See [PR
approved](https://github.com/uutils/coreutils/pull/5428), and
[pending](https://github.com/uutils/coreutils/pull/5429).
* `--progress` does not seem to work on `uutils mv` either and have not
checked whether certain `X` size has to be achieved in order for it to
appear, thus something to investigate as well, but thought it wasnt
important enough to not make the PR.

See [issue
comment](https://github.com/nushell/nushell/issues/10446#issuecomment-1764497988),
on the possible strategy to follow, mainly copy what we did with `ucp`.

I still left some comments on purpose particularly on tests, which of
course would be removed before something is decided here. :) @fdncred
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

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

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

# Tests + Formatting

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

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

- [X] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [X] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used`
to check that you're using the standard code style
- [X] `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))
- [X] `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-18 10:20:57 -06:00
19e76332fa fix: items doesn't support lazy records (#11567)
<!--
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.
-->
Implement support for lazy records for `items`, the same way `columns`
or `values` do.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Something like `sys | items {|k,v| $"($k): ($v)"}` used to fail with an
error. Now it works as expected.
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-18 07:57:21 -06:00
55bf4d847f Add CLI flag to disable history (#11550)
# Description
Adds a CLI flag for nushell that disables reading and writing to the
history file. This will be useful for future testing and possibly our
users as well. To borrow `fish` shell's terminology, this allows users
to start nushell in "private" mode.

# User-Facing Changes
Breaking API change for `nu-protocol` (changed `Config`).
2024-01-17 09:40:59 -06:00
a4199ea312 Fix tarpaulin skip attribute to latest (#11552)
<!--
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
Updated the attribute as per the latest version of
[tarpaulin](https://github.com/xd009642/tarpaulin) to fix compilation
error when used as library with latest tarpaulin.

https://github.com/xd009642/tarpaulin/tree/develop?tab=readme-ov-file#ignoring-code-in-files

# User-Facing Changes
None

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-17 07:26:34 -06:00
afb7e1cf66 Allow tables and records as input to math commands (#11496)
# Description
The math functions `avg`, `max`, `median`, `min`, `product`, `stddev`,
`sum` and `variance` all takes a list as input and return a number.
<https://github.com/nushell/nushell/blob/main/crates/nu-command/src/math/utils.rs>
contains code that makes these functions work for tables (by running the
function on each column), but this functionality has not been accessible
because the input types are too strict. This PR remedies this.

The functions should also work on records, since a record is basically a
one-row table.

Most of these functions also make sense for durations and file sizes,
except `product` of course. There's an implementation issue with
`stddev` and `variance` for durations and file sizes, but they could in
principle support it.

# User-Facing Changes
This PR only adds supported types, and doesn't remove any, so there
should be no breaking changes.
2024-01-17 06:39:50 -06:00
61d5aed0a2 Fix deprecation in default_config.nu (#11547)
# Description
in https://github.com/nushell/nushell/pull/11289, spreading lists into
command invocations was made possible and its implicit version was
deprecated, but not everything was updated accordingly.

# User-Facing Changes
A commented part of the default config no longer throws a deprecation
warning when uncommented


# After Submitting
After https://github.com/nushell/nushell/pull/11289, the mention of
carapace in the documentation wasn’t updated. See
https://github.com/nushell/nushell.github.io/pull/1211
2024-01-16 06:42:17 -06:00
41a7c351cb Bump zerocopy from 0.7.29 to 0.7.32 (#11545)
Bumps [zerocopy](https://github.com/google/zerocopy) from 0.7.29 to
0.7.32.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/google/zerocopy/releases">zerocopy's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.32</h2>
<h2>What's Changed</h2>
<ul>
<li>[derive] Exclude large test files when publishing by <a
href="https://github.com/joshlf"><code>@​joshlf</code></a> in <a
href="https://redirect.github.com/google/zerocopy/pull/744">google/zerocopy#744</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google/zerocopy/compare/v0.7.31...v0.7.32">https://github.com/google/zerocopy/compare/v0.7.31...v0.7.32</a></p>
<h2>v0.7.31</h2>
<p>This release fixes the soundness issue described in <a
href="https://redirect.github.com/google/zerocopy/issues/716">#716</a>.
The affected versions will soon be yanked.</p>
<p>This release is also described in security advisories <a
href="https://rustsec.org/advisories/RUSTSEC-2023-0074.html">RUSTSEC-2023-0074</a>
and <a
href="https://github.com/google/zerocopy/security/advisories/GHSA-3mv5-343c-w2qg">GHSA-3mv5-343c-w2qg</a>.</p>
<h2>What's Changed</h2>
<ul>
<li>Fix soundness hole in Ref::into_ref and into_mut by <a
href="https://github.com/joshlf"><code>@​joshlf</code></a> in <a
href="https://redirect.github.com/google/zerocopy/pull/721">google/zerocopy#721</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google/zerocopy/compare/v0.7.30...v0.7.31">https://github.com/google/zerocopy/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>[policies] Document yanking policy by <a
href="https://github.com/joshlf"><code>@​joshlf</code></a> in <a
href="https://redirect.github.com/google/zerocopy/pull/677">google/zerocopy#677</a></li>
<li>[ci] Roll pinned nightly toolchain by <a
href="https://github.com/google-pr-creation-bot"><code>@​google-pr-creation-bot</code></a>
in <a
href="https://redirect.github.com/google/zerocopy/pull/680">google/zerocopy#680</a></li>
<li>[readme] Link to GitHub Releases (<a
href="https://redirect.github.com/google/zerocopy/issues/692">#692</a>)
by <a href="https://github.com/joshlf"><code>@​joshlf</code></a> in <a
href="https://redirect.github.com/google/zerocopy/pull/693">google/zerocopy#693</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google/zerocopy/compare/v0.7.29...v0.7.30">https://github.com/google/zerocopy/compare/v0.7.29...v0.7.30</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="94b7e0a44d"><code>94b7e0a</code></a>
[derive] Exclude large test files when publishing (<a
href="https://redirect.github.com/google/zerocopy/issues/744">#744</a>)</li>
<li><a
href="7d3a8f9ea6"><code>7d3a8f9</code></a>
Fix soundness hole in Ref::into_ref and into_mut (<a
href="https://redirect.github.com/google/zerocopy/issues/721">#721</a>)</li>
<li><a
href="961612f110"><code>961612f</code></a>
[readme] Link to GitHub Releases (<a
href="https://redirect.github.com/google/zerocopy/issues/692">#692</a>)
(<a
href="https://redirect.github.com/google/zerocopy/issues/693">#693</a>)</li>
<li><a
href="449b78c67f"><code>449b78c</code></a>
[ci] Roll pinned nightly toolchain (<a
href="https://redirect.github.com/google/zerocopy/issues/680">#680</a>)</li>
<li><a
href="8f7d88b90e"><code>8f7d88b</code></a>
[policies] Document yanking policy (<a
href="https://redirect.github.com/google/zerocopy/issues/677">#677</a>)</li>
<li>See full diff in <a
href="https://github.com/google/zerocopy/compare/v0.7.29...v0.7.32">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zerocopy&package-manager=cargo&previous-version=0.7.29&new-version=0.7.32)](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)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/nushell/nushell/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-16 14:30:22 +08:00
7ac3e97bfe Fix memory consumption of into sqlite (#10232)
# Description

Currently, the `into sqlite` command collects the entire input stream
into a single Value, which soaks up the entire input into memory, before
it ever tries to write anything to the DB. This is very problematic for
large inputs; for example, I tried transforming a multi-gigabyte CSV
file into SQLite, and before I knew what was happening, my system's
memory was completely exhausted, and I had to hard reboot to recover.

This PR fixes this problem by working directly with the pipeline stream,
inserting into the DB as values are read from the stream.

In order to facilitate working with the stream directly, I introduced a
new `Table` struct to store the connection and a few configuration
parameters, as well as to make it easier to lazily create the table on
the first read value.

In addition to the purely functional fixes, a few other changes were
made to the serialization and user facing behavior.

### Serialization

Much of the preexisting code was focused on generating the exact text
needed for a SQL statement. This is unneeded and less safe than using
the `rusqlite` crate's serialization for native Rust types along with
prepared statements.

### User-Facing Changes

Currently, the command is very liberal in the input types it accepts.
The strategy is basically if it is a record, try to follow its structure
and make an analogous SQL row, which is pretty reasonable. However, when
it's not a record, it basically tries to guess what the user wanted and
just makes a single column table and serializes the value into that one
column, whatever type it may be.

This has been changed so that it only accepts records as input. If the
user wants to serialize non-record types into SQL, then they must
explicitly opt into doing this by constructing a record or table with it
first. For a utility for inserting data into SQL, I think it makes more
sense to let the user choose how to convert their data, rather than make
a choice for them that may surprise them.

However, I understand this may be a controversial change. If the
maintainers don't agree, I can change this back.

#### Long switch names

The `file_name` and `table_name` long form switches are currently
snake_case and expect to be as such at the command line. These have been
changed to kebab-case to be more conventional.

# Tests + Formatting

To test the memory consumption, I used [this publicly available index of
all Wikipedia articles](https://dumps.wikimedia.org/enwiki/20230820/),
using the first 10,000, 100,000, and 1,000,000 entries, in that order. I
ran the following script to benchmark the changes against the current
stable release:

```nu
#!/usr/bin/nu

# let shellbin = $"($env.HOME)/src/nushell/target/aarch64-linux-android/release/nu"
let shellbin = "nu"
const dbpath = 'enwiki-index.db'

[10000, 100000, 1000000]
  | each {|rows|
      rm -f $dbpath;
      do { time -f '%M %e %U %S' $shellbin -c (
        $"bzip2 -cdk ~/enwiki-20230820-pages-articles-multistream-index.txt.bz2
            | head -n ($rows)
            | lines
            | parse '{offset}:{id}:{title}'
            | update cells -c [offset, id] { into int }
            | into sqlite ($dbpath)"
        )
      }
      | complete
      | get stderr
      | str trim
      | parse '{rss_max} {real} {user} {kernel}'
      | update cells -c [rss_max] { $"($in)kb" | into filesize }
      | update cells -c [real, user, kernel] { $"($in)sec" | into duration }
      | insert rows $rows
      | roll right
    }
  | flatten
  | to nuon
```

This yields the following results

Current stable release:

|rows|rss_max|real|user|kernel|
|-|-|-|-|-|
|10000|53.6 MiB|770ms|460ms|420ms|
|100000|209.6 MiB|6sec 940ms|3sec 740ms|4sec 380ms|
|1000000|1.7 GiB|1min 8sec 810ms|38sec 690ms|42sec 550ms|

This PR:

|rows|rss_max|real|user|kernel|
|-|-|-|-|-|
|10000|38.2 MiB|780ms|440ms|410ms|
|100000|39.8 MiB|6sec 450ms|3sec 530ms|4sec 160ms|
|1000000|39.8 MiB|1min 3sec 230ms|37sec 440ms|40sec 180ms|

# Note

I started this branch kind of at the same time as my others, but I
understand the feedback that smaller PRs are preferred. Let me know if
it would be better to split this up.

I do think the scope of the changes are on the bigger side even without
the behavior changes I mentioned, so I'm not sure if that will help this
particular PR very much, but I'm happy to oblige on request.
2024-01-15 21:41:25 -06:00
924986576d Do not block signals for child processes (#11402)
# Description / User-Facing Changes
Signals are no longer blocked for child processes launched from both
interactive and non-interactive mode. The only exception is that
`SIGTSTP`, `SIGTTIN`, and `SIGTTOU` remain blocked for child processes
launched only from **interactive** mode. This is to help prevent nushell
from getting into an unrecoverable state, since we don't support
background jobs. Anyways, this fully fixes #9026.

# Other Notes
- Needs Rust version `>= 1.66` for a fix in
`std::process::Command::spawn`, but it looks our current Rust version is
way above this.
- Uses `sigaction` instead of `signal`, since the behavior of `signal`
can apparently differ across systems. Also, the `sigaction` man page
says:
> The sigaction() function supersedes the signal() function, and should
be used in preference.

Additionally, using both `sigaction` and `signal` is not recommended.
Since we were already using `sigaction` in some places (and possibly
some of our dependencies as well), this PR replaces all usages of
`signal`.

# Tests
Might want to wait for #11178 for testing.
2024-01-15 16:08:21 -06:00
7071617f18 Allow plugins to receive configuration from the nushell configuration (#10955)
# Description

When nushell calls a plugin it now sends a configuration `Value` from
the nushell config under `$env.config.plugins.PLUGIN_SHORT_NAME`. This
allows plugin authors to read configuration provided by plugin users.

The `PLUGIN_SHORT_NAME` must match the registered filename after
`nu_plugin_`. If you register `target/debug/nu_plugin_config` the
`PLUGIN_NAME` will be `config` and the nushell config will loook like:

        $env.config = {
          # ...
          plugins: {
            config: [
              some
              values
            ]
          }
        }

Configuration may also use a closure which allows passing values from
`$env` to a plugin:

        $env.config = {
          # ...
          plugins: {
            config: {||
              $env.some_value
            }
          }
        }

This is a breaking change for the plugin API as the `Plugin::run()`
function now accepts a new configuration argument which is an
`&Option<Value>`. If no configuration was supplied the value is `None`.

Plugins compiled after this change should work with older nushell, and
will behave as if the configuration was not set.

Initially discussed in #10867

# User-Facing Changes

* Plugins can read configuration data stored in `$env.config.plugins`
* The plugin `CallInfo` now includes a `config` entry, existing plugins
will require updates

# Tests + Formatting

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

# After Submitting

- [ ] Update [Creating a plugin (in
Rust)](https://www.nushell.sh/contributor-book/plugins.html#creating-a-plugin-in-rust)
[source](https://github.com/nushell/nushell.github.io/blob/main/contributor-book/plugins.md)
- [ ] Add "Configuration" section to [Plugins
documentation](https://www.nushell.sh/contributor-book/plugins.html)
2024-01-15 16:59:47 +08:00
e72a4116ec adjust some commansd input_output type (#11436)
# Description
1. Make table to be a subtype of `list<any>`, so some input_output_types
of filter commands are unnecessary
2. Change some commands which accept an input type, but generates
different output types. In this case, delete duplicate entry, and change
relative output type to `<any>`

Yeah it makes some commands more permissive, but I think it's better to
run into strange issue that why my script runs to failed during parse
time.

Fixes  #11193

# User-Facing Changes
NaN

# Tests + Formatting
NaN

# After Submitting
NaN
2024-01-15 16:58:26 +08:00
a109283118 Apply nightly clippy fixes (#11508)
# Description

Clippy fixes

# User-Facing Changes
N/A
2024-01-15 10:52:16 +08:00
42b03917fb Bump actions-rust-lang/setup-rust-toolchain from 1.6.0 to 1.8.0 (#11539)
Bumps
[actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain)
from 1.6.0 to 1.8.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.8.0</h2>
<ul>
<li>Allow specifying subdirectories for cache.</li>
<li>Fix toolchain file overriding.</li>
</ul>
<h2>v1.7.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Allow overriding the toolchain file by <a
href="https://github.com/Twey"><code>@​Twey</code></a> in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/26">actions-rust-lang/setup-rust-toolchain#26</a></li>
<li>ci: update checked rust-toolchain version 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/29">actions-rust-lang/setup-rust-toolchain#29</a></li>
<li>Prepare 1.7.0 release 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/30">actions-rust-lang/setup-rust-toolchain#30</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Twey"><code>@​Twey</code></a> made their
first contribution in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/26">actions-rust-lang/setup-rust-toolchain#26</a></li>
<li><a href="https://github.com/robjtede"><code>@​robjtede</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/pull/29">actions-rust-lang/setup-rust-toolchain#29</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.6...v1.7.0">https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.6...v1.7.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.8.0] - 2024-01-13</h2>
<ul>
<li>Allow specifying subdirectories for cache.</li>
<li>Fix toolchain file overriding.</li>
</ul>
<h2>[1.7.0] - 2024-01-11</h2>
<ul>
<li>Allow overriding the toolchain file with explicit
<code>toolchain</code> input. (<a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/26">#26</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b113a30d27"><code>b113a30</code></a>
Merge pull request <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/34">#34</a>
from actions-rust-lang/rel-180</li>
<li><a
href="039765bd18"><code>039765b</code></a>
chore: prepare release 1.8.0</li>
<li><a
href="84e65ce315"><code>84e65ce</code></a>
Merge pull request <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/32">#32</a>
from gruebel/add-workspaces</li>
<li><a
href="6f719a240e"><code>6f719a2</code></a>
Merge branch 'main' into add-workspaces</li>
<li><a
href="15d0afaad9"><code>15d0afa</code></a>
ci: fix cache test</li>
<li><a
href="922cc935eb"><code>922cc93</code></a>
ci: add cache-workspaces test</li>
<li><a
href="bcda41b18d"><code>bcda41b</code></a>
Merge pull request <a
href="https://redirect.github.com/actions-rust-lang/setup-rust-toolchain/issues/33">#33</a>
from fprasx/fprasx/fix-toolchain-overrides</li>
<li><a
href="ba41ca6f71"><code>ba41ca6</code></a>
ci: clearer test names</li>
<li><a
href="aa089182f2"><code>aa08918</code></a>
ci: fix conditional</li>
<li><a
href="c6086ae6e5"><code>c6086ae</code></a>
test: add toolchain file override test</li>
<li>Additional commits viewable in <a
href="https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.6.0...v1.8.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.6.0&new-version=1.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>
2024-01-15 10:27:41 +08:00
12a07052f9 Bump rust-embed from 8.1.0 to 8.2.0 (#11538)
Bumps [rust-embed](https://github.com/pyros2097/rust-embed) from 8.1.0
to 8.2.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.2.0] - 2023-12-29</h2>
<ul>
<li>Fix naming collisions in macros <a
href="https://redirect.github.com/pyrossh/rust-embed/pull/230/files">#230</a>.
Thanks to <a href="https://github.com/hwittenborn">hwittenborn</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.1.0&new-version=8.2.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>
2024-01-15 10:27:09 +08:00
0cd927d634 Bump crate-ci/typos from 1.17.0 to 1.17.1 (#11540)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.17.0 to
1.17.1.
<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.17.1</h2>
<h2>[1.17.1] - 2024-01-12</h2>
<h3>Features</h3>
<ul>
<li>First attempt at aarch64 for Mac</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.17.1] - 2024-01-12</h2>
<h3>Features</h3>
<ul>
<li>First attempt at aarch64 for Mac</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a35b382b44"><code>a35b382</code></a>
chore: Release</li>
<li><a
href="4c32fc5ec4"><code>4c32fc5</code></a>
docs: Update changelog</li>
<li><a
href="b02f5895d3"><code>b02f589</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/908">#908</a>
from epage/ci</li>
<li><a
href="7b12730b18"><code>7b12730</code></a>
chore(ci): Build for aarch64</li>
<li><a
href="5adfdc2ade"><code>5adfdc2</code></a>
chore(ci): Show what gets bundled</li>
<li><a
href="657d1b1764"><code>657d1b1</code></a>
chore(ci): Remove use of undefined variable</li>
<li><a
href="a7762787d8"><code>a776278</code></a>
chore(ci): Ensure rustfmt is available</li>
<li>See full diff in <a
href="https://github.com/crate-ci/typos/compare/v1.17.0...v1.17.1">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.17.0&new-version=1.17.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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

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

---

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

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot 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>
2024-01-15 10:26:31 +08:00
e4c2c123ab Support for disabling automatic escaping in to xml (#11536)
# Description
This PR addresses #11525 by adding `--partial-escape` which makes `to
xml` only escape `<>&` in text and `<>&"` in comments. This PR also
fixes issue where comment and PI content was escaped even though [it
should not be](https://stackoverflow.com/a/46637835)

# User-Facing Changes
Correct comments and PIs
 `to xml --partial-escape` flag to emit less escaped characters

# Tests + Formatting
Added tests for specified issues
2024-01-14 07:36:53 -06:00
d25be66929 check existance w/o traversing symlinks (#10872)
# Description

Currently `path exists` checks the file/folder's existence by traversing
symlinks. I've added a `-n` switch/flag that disables symlink
traversing, similar to what `path expand -n` does.

## The Long Story (for those interested)

Hello! 👋 While working on one of my scripts, I discovered that the `path
exists` command was traversing symlinks. This meant that even if the
file existed, it would fail if the pointed location didn't exist. To
address this, I've introduced a new `-n` flag, which I borrowed from the
`path expand` command. This addition should make the behavior more
consistent within the *path commands universe*.

## But, is it any useful?
 
```nushell
let compat = /run/media/userX/DriveX/steam/steamapps/compatdata
if "symlink" == ($compat | path expand -n | path type) {}
# to this
if ($compat | path exists -n) {}
```

# User-Facing Changes

Users, will not efect. Unless they use the mentioned `-n` flag/switch.
2024-01-14 07:33:33 +08:00
6eb6086823 Show last command and running commands in title with shell_integration (#11532)
Closes #10260

I'm not 100% convinced about the star thing for running commands because
on short commands it makes the title jitter.

Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
2024-01-12 21:32:25 -06:00
e88a531945 Fix commandline --cursor-end (#11504)
<!--
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 `commandline --cursor-end`, set `repl.cursor_pos` to the number of
bytes in the buffer, not the number of graphemes.

fixes: #11503

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-13 08:24:14 +08:00
a093e66822 update query web param --as-table from Table to List (#11531)
# Description

This is a small change that updates the `--as-table`/`-t` parameter to
`SyntaxShape::List` instead of `SyntaxShape::Table`. It was always
supposed to be a list of headers. Not sure where Table came from.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-12 13:26:40 -06:00
387c5462e9 Add file attribute handling flag to cp (#11491)
# Description
This PR adds possibility to preserve/strip attributes from files when
using `cp` (via uu_cp::Attributes). To achieve this a single `--preserve
<list of attributes>` flag is added. This is different from how
coreutils and uutils cp function, but I believe this is better for
nushell.

Coreutils cp has three options `-p`, `--preserve` and `--no-presevce`.
The logic of these two options is not straightforward. As far as I
understand it is:
1. By default only mode attributes are preserved
2. `--preserve` option adds to default preserved attributes specified
ones (e.g. `--preserve=xattr,timestamps` will preserve mode, timestamps
and xattr)
3. `-p` is the same as `--preserve=mode,ownership,timestamps`
4. `--no-preserve` option rejects specified attributes (having priority
over `--preserve`)

However (in my opinion) the `--no-preserve` option is not needed,
because its only use seems to be rejecting attributes preserved by
default. But there is no need for this in nushell, because `--preserve`
can be specified with empty list as argument (whereas coreutils cp will
display a `cp: ambiguous argument ‘’ for ‘--preserve’` error if
`--preserve` is used with empty string as argument).

So to simplify this command is suggest (and implemented) only the
`--preserve` with the following logic:
1. By default mode attribute is preserved (as in coreutils cp)
2. `--preserve [ ... ]` will overwrite default with whatever is
specified in list (empty list meaning preserve nothing)

This way cp without `--preserve` behaves the same as coreutils `cp`, but
instead of using combinations of `--preserve` and `--no-preserve` one
needs to use `--preserve [ ... ]` with all attributes specified
explicitly. This seems more user-friendly to me as it does not require
remembering what the attributes preserved by default are and rejecting
them manually. However I see the possible problem with behavior
different from coreutils implementation, so some feedback is
apprecieated!

# User-Facing Changes
Users can now preserve or reject file attributes when using `cp`

# Tests + Formatting
Added tests manipulating mode and timestamps attributes.
2024-01-12 12:02:55 -06:00
724818030d add type check during eval time (#11475)
# Description
Fixes: #11438 

Take the following as example:
```nushell
def spam [foo: string] {
    $'foo: ($foo | describe)'
}
def outer [--foo: string] {
    spam $foo
}

outer
```
When we call `outer`, type checker only check the all for `outer`, but
doesn't check inside the body of `outer`. This pr is trying to introduce
a type checking process through `Type::is_subtype()` during eval time.

## NOTE
I'm not really sure if it's easy to make a check inside the body of
`outer`. Adding an eval time type checker seems like an easier solution.
As a result: `outer` will be caught by runtime, not parse time type
checker

cc @kubouch 

# User-Facing Changes
After this pr the following call will failed:
```nushell
> outer
Error: nu:🐚:cant_convert

  × Can't convert to string.
   ╭─[entry #27:1:1]
 1 │ def outer [--foo: any] {
 2 │     spam $foo
   ·          ──┬─
   ·            ╰── can't convert nothing to string
 3 │ }
   ╰────
```

# Tests + Formatting
Done

# After Submitting
NaN
2024-01-12 23:48:53 +08:00
8cad12a05c update nushell to latest reedline main after pr revert (#11528)
# Description

This PR updates nushell to the latest reedline main branch after a
reedline PR was reverted.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-11 15:58:54 -06:00
41119d3f88 Fix "Char index out of bounds" Error (#11526)
# Description

The code that converts Nushell's span into LSP line and character
indices accidentally treated the span as character indices while they
are byte indices. Fixes #11522.

# User-Facing Changes

None, just a bugfix.
2024-01-11 15:24:49 -06:00
0ebbc8f71c Make only_buffer_difference: true work (#11488) 2024-01-11 11:58:14 -06:00
bd07f7b302 bump to reedline latest main (#11520)
# Description

Bump to latest reedline main in order to dogfood latest reedline
changes.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-11 10:04:54 -06:00
1867bb1a88 Fix incorrect handling of boolean flags for builtin commands (#11492)
# Description
Possible fix of #11456
This PR fixes a bug where builtin commands did not respect the logic of
dynamically passed boolean flags. The reason is
[has_flag](6f59abaf43/crates/nu-protocol/src/ast/call.rs (L204C5-L212C6))
method did not evaluate and take into consideration expression used with
flag.

To address this issue a solution is proposed:
1. `has_flag` method is moved to `CallExt` and new logic to evaluate
expression and check if it is a boolean value is added
2. `has_flag_const` method is added to `CallExt` which is a constant
version of `has_flag`
3. `has_named` method is added to `Call` which is basically the old
logic of `has_flag`
4. All usages of `has_flag` in code are updated, mostly to pass
`engine_state` and `stack` to new `has_flag`. In `run_const` commands it
is replaced with `has_flag_const`. And in a few select places: parser,
`to nuon` and `into string` old logic via `has_named` is used.

# User-Facing Changes
Explicit values of boolean flags are now respected in builtin commands.
Before:

![image](https://github.com/nushell/nushell/assets/17511668/f9fbabb2-3cfd-43f9-ba9e-ece76d80043c)
After:

![image](https://github.com/nushell/nushell/assets/17511668/21867596-2075-437f-9c85-45563ac70083)

Another example:
Before:

![image](https://github.com/nushell/nushell/assets/17511668/efdbc5ca-5227-45a4-ac5b-532cdc2bbf5f)
After:

![image](https://github.com/nushell/nushell/assets/17511668/2907d5c5-aa93-404d-af1c-21cdc3d44646)


# Tests + Formatting
Added test reproducing some variants of original issue.
2024-01-11 17:19:48 +02:00
62272975f2 path exists: Empty path shouldn't be marked as exists (#11515)
<!--
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.
-->

Simply, `""` doesn't exist. 

It's easy to test the truthfulness of this:

```rust
fn main() {
    println!("{}", std::path::Path::new("").exists());
}
```
gives `false`

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Technically this is a breaking change, so:
- **breaking:** `path exists` no longer considers an empty path (`""`)
as exists

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

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

- `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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-11 07:19:34 +08:00
7bb9ee55c4 Bump to dev version 0.89.1 (#11513)
<!--
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
<!-- 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-11 00:19:21 +13:00
2c1560e281 Bump version for 0.89.0 release (#11511)
<!--
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!
-->

- [x] reedline
  - [x] released
  - [x] pinned
- [ ] git dependency check
- [ ] release notes


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

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

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

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-09 22:16:29 +02:00
d5206cbd68 Pin reedline to 0.28 release (#11510)
<!--
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!
-->

See full release notes:
[nushell/reedline@v0.28.0
(release)](https://github.com/nushell/reedline/releases/tag/v0.28.0)

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

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

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

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

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

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2024-01-09 21:42:44 +02:00
428 changed files with 11168 additions and 6551 deletions

View File

@ -41,7 +41,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with:
rustflags: ""
@ -63,6 +63,10 @@ jobs:
platform: [windows-latest, macos-latest, ubuntu-20.04]
feature: [default, dataframe, extra]
include:
# linux CI cannot handle clipboard feature
- default-flags: ""
- platform: ubuntu-20.04
default-flags: "--no-default-features --features=default-no-clipboard"
- feature: default
flags: ""
- feature: dataframe
@ -85,12 +89,12 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with:
rustflags: ""
- name: Tests
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.default-flags }} ${{ matrix.flags }}
- name: Check for clean repo
shell: bash
@ -117,7 +121,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with:
rustflags: ""
@ -125,7 +129,7 @@ jobs:
run: cargo install --path . --locked --no-default-features
- name: Standard library tests
run: nu -c 'use std testing; testing run-tests --path crates/nu-std'
run: nu -c 'use crates/nu-std/testing.nu; testing run-tests --path crates/nu-std'
- name: Setup Python
uses: actions/setup-python@v5
@ -163,7 +167,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with:
rustflags: ""

View File

@ -81,7 +81,7 @@ jobs:
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf
- riscv64gc-unknown-linux-gnu
# - riscv64gc-unknown-linux-gnu
extra: ['bin']
include:
- target: aarch64-apple-darwin
@ -118,9 +118,9 @@ jobs:
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04
target_rustflags: ''
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
target_rustflags: ''
# - target: riscv64gc-unknown-linux-gnu
# os: ubuntu-latest
# target_rustflags: ''
runs-on: ${{matrix.os}}
@ -135,7 +135,7 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''
@ -249,7 +249,7 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''

View File

@ -28,7 +28,7 @@ jobs:
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf
- riscv64gc-unknown-linux-gnu
# - riscv64gc-unknown-linux-gnu
extra: ['bin']
include:
- target: aarch64-apple-darwin
@ -65,9 +65,9 @@ jobs:
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04
target_rustflags: ''
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
target_rustflags: ''
# - target: riscv64gc-unknown-linux-gnu
# os: ubuntu-latest
# target_rustflags: ''
runs-on: ${{matrix.os}}
@ -79,7 +79,7 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''
@ -170,7 +170,7 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''

View File

@ -10,6 +10,6 @@ jobs:
uses: actions/checkout@v4
- name: Check spelling
uses: crate-ci/typos@v1.17.0
uses: crate-ci/typos@v1.18.0
with:
config: ./.github/.typos.toml

1485
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.72.1"
version = "0.88.2"
version = "0.90.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -53,30 +53,32 @@ members = [
]
[dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.88.2" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.88.2" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.88.2" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.88.2" }
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.88.2", features = ["dataframe"], optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.88.2", optional = true }
nu-command = { path = "./crates/nu-command", version = "0.88.2" }
nu-engine = { path = "./crates/nu-engine", version = "0.88.2" }
nu-explore = { path = "./crates/nu-explore", version = "0.88.2" }
nu-json = { path = "./crates/nu-json", version = "0.88.2" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.88.2" }
nu-parser = { path = "./crates/nu-parser", version = "0.88.2" }
nu-path = { path = "./crates/nu-path", version = "0.88.2" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.88.2" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.88.2" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.88.2" }
nu-system = { path = "./crates/nu-system", version = "0.88.2" }
nu-table = { path = "./crates/nu-table", version = "0.88.2" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.88.2" }
nu-std = { path = "./crates/nu-std", version = "0.88.2" }
nu-utils = { path = "./crates/nu-utils", version = "0.88.2" }
nu-cli = { path = "./crates/nu-cli", version = "0.90.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.90.0" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.90.0" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.90.0" }
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.90.0", features = [
"dataframe",
], optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.90.0", optional = true }
nu-command = { path = "./crates/nu-command", version = "0.90.0" }
nu-engine = { path = "./crates/nu-engine", version = "0.90.0" }
nu-explore = { path = "./crates/nu-explore", version = "0.90.0" }
nu-json = { path = "./crates/nu-json", version = "0.90.0" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.90.0" }
nu-parser = { path = "./crates/nu-parser", version = "0.90.0" }
nu-path = { path = "./crates/nu-path", version = "0.90.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.90.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.90.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.90.0" }
nu-system = { path = "./crates/nu-system", version = "0.90.0" }
nu-table = { path = "./crates/nu-table", version = "0.90.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.90.0" }
nu-std = { path = "./crates/nu-std", version = "0.90.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.90.0" }
nu-ansi-term = "0.49.0"
reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
nu-ansi-term = "0.50.0"
reedline = { version = "0.29.0", features = ["bashisms", "sqlite"] }
crossterm = "0.27"
ctrlc = "3.4"
@ -90,7 +92,6 @@ time = "0.3"
[target.'cfg(not(target_os = "windows"))'.dependencies]
# Our dependencies don't use OpenSSL on Windows
openssl = { version = "0.10", features = ["vendored"], optional = true }
signal-hook = { version = "0.3", default-features = false }
[target.'cfg(windows)'.build-dependencies]
winresource = "0.1"
@ -104,7 +105,7 @@ nix = { version = "0.27", default-features = false, features = [
] }
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.88.2" }
nu-test-support = { path = "./crates/nu-test-support", version = "0.90.0" }
assert_cmd = "2.0"
criterion = "0.5"
pretty_assertions = "1.4"
@ -121,7 +122,16 @@ plugin = [
"nu-protocol/plugin",
"nu-engine/plugin",
]
default = ["plugin", "which-support", "trash-support", "sqlite", "mimalloc"]
default = ["default-no-clipboard", "system-clipboard"]
# Enables convenient omitting of the system-clipboard feature, as it leads to problems in ci on linux
# See https://github.com/nushell/nushell/pull/11535
default-no-clipboard = [
"plugin",
"which-support",
"trash-support",
"sqlite",
"mimalloc",
]
stable = ["default"]
wasi = ["nu-cmd-lang/wasi"]
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
@ -131,6 +141,7 @@ wasi = ["nu-cmd-lang/wasi"]
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
mimalloc = ["nu-cmd-lang/mimalloc", "dep:mimalloc"]
system-clipboard = ["reedline/system_clipboard"]
# Stable (Default)
which-support = ["nu-command/which-support", "nu-cmd-lang/which-support"]
@ -172,8 +183,8 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
#[patch.crates-io]
#reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# uu_cp = { git = "https://github.com/uutils/coreutils.git", branch = "main" }

View File

@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.88.2"
version = "0.90.0"
[lib]
bench = false
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.88.2" }
nu-command = { path = "../nu-command", version = "0.88.2" }
nu-test-support = { path = "../nu-test-support", version = "0.88.2" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.90.0" }
nu-command = { path = "../nu-command", version = "0.90.0" }
nu-test-support = { path = "../nu-test-support", version = "0.90.0" }
rstest = { version = "0.18.1", default-features = false }
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.88.2" }
nu-engine = { path = "../nu-engine", version = "0.88.2" }
nu-path = { path = "../nu-path", version = "0.88.2" }
nu-parser = { path = "../nu-parser", version = "0.88.2" }
nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
nu-utils = { path = "../nu-utils", version = "0.88.2" }
nu-color-config = { path = "../nu-color-config", version = "0.88.2" }
nu-ansi-term = "0.49.0"
reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.90.0" }
nu-engine = { path = "../nu-engine", version = "0.90.0" }
nu-path = { path = "../nu-path", version = "0.90.0" }
nu-parser = { path = "../nu-parser", version = "0.90.0" }
nu-protocol = { path = "../nu-protocol", version = "0.90.0" }
nu-utils = { path = "../nu-utils", version = "0.90.0" }
nu-color-config = { path = "../nu-color-config", version = "0.90.0" }
nu-ansi-term = "0.50.0"
reedline = { version = "0.29.0", features = ["bashisms", "sqlite"] }
chrono = { default-features = false, features = ["std"], version = "0.4" }
crossterm = "0.27"

View File

@ -71,7 +71,7 @@ impl Command for Commandline {
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
if call.has_flag("cursor") {
if call.has_flag(engine_state, stack, "cursor")? {
let cmd_str = cmd.as_string()?;
match cmd_str.parse::<i64>() {
Ok(n) => {
@ -96,9 +96,9 @@ impl Command for Commandline {
})
}
}
} else if call.has_flag("append") {
} else if call.has_flag(engine_state, stack, "append")? {
repl.buffer.push_str(&cmd.as_string()?);
} else if call.has_flag("insert") {
} else if call.has_flag(engine_state, stack, "insert")? {
let cmd_str = cmd.as_string()?;
let cursor_pos = repl.cursor_pos;
repl.buffer.insert_str(cursor_pos, &cmd_str);
@ -110,10 +110,10 @@ impl Command for Commandline {
Ok(Value::nothing(call.head).into_pipeline_data())
} else {
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
if call.has_flag("cursor-end") {
repl.cursor_pos = repl.buffer.graphemes(true).count();
if call.has_flag(engine_state, stack, "cursor-end")? {
repl.cursor_pos = repl.buffer.len();
Ok(Value::nothing(call.head).into_pipeline_data())
} else if call.has_flag("cursor") {
} else if call.has_flag(engine_state, stack, "cursor")? {
let char_pos = repl
.buffer
.grapheme_indices(true)

View File

@ -1,3 +1,4 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
@ -23,10 +24,7 @@ impl Command for History {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("history")
.input_output_types(vec![
(Type::Nothing, Type::Table(vec![])),
(Type::Nothing, Type::Nothing),
])
.input_output_types(vec![(Type::Nothing, Type::Any)])
.allow_variants_without_examples(true)
.switch("clear", "Clears out the history entries", Some('c'))
.switch(
@ -40,21 +38,25 @@ impl Command for History {
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let Some(history) = engine_state.history_config() else {
return Ok(PipelineData::empty());
};
// todo for sqlite history this command should be an alias to `open ~/.config/nushell/history.sqlite3 | get history`
if let Some(config_path) = nu_path::config_dir() {
let clear = call.has_flag("clear");
let long = call.has_flag("long");
let clear = call.has_flag(engine_state, stack, "clear")?;
let long = call.has_flag(engine_state, stack, "long")?;
let ctrlc = engine_state.ctrlc.clone();
let mut history_path = config_path;
history_path.push("nushell");
match engine_state.config.history_file_format {
match history.file_format {
HistoryFileFormat::Sqlite => {
history_path.push("history.sqlite3");
}
@ -68,29 +70,27 @@ impl Command for History {
// TODO: FIXME also clear the auxiliary files when using sqlite
Ok(PipelineData::empty())
} else {
let history_reader: Option<Box<dyn ReedlineHistory>> =
match engine_state.config.history_file_format {
HistoryFileFormat::Sqlite => {
SqliteBackedHistory::with_file(history_path, None, None)
.map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
boxed
})
.ok()
}
let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format {
HistoryFileFormat::Sqlite => {
SqliteBackedHistory::with_file(history_path, None, None)
.map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
boxed
})
.ok()
}
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
engine_state.config.max_history_size as usize,
history_path,
)
.map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
boxed
})
.ok(),
};
HistoryFileFormat::PlainText => {
FileBackedHistory::with_file(history.max_size as usize, history_path)
.map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
boxed
})
.ok()
}
};
match engine_state.config.history_file_format {
match history.file_format {
HistoryFileFormat::PlainText => Ok(history_reader
.and_then(|h| {
h.search(SearchQuery::everything(SearchDirection::Forward, None))

View File

@ -94,6 +94,7 @@ impl CommandCompletion {
.map(move |x| Suggestion {
value: String::from_utf8_lossy(&x.0).to_string(),
description: x.1,
style: None,
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true,
@ -110,6 +111,7 @@ impl CommandCompletion {
.map(move |x| Suggestion {
value: x,
description: None,
style: None,
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true,
@ -123,6 +125,7 @@ impl CommandCompletion {
results.push(Suggestion {
value: format!("^{}", external.value),
description: None,
style: None,
extra: None,
span: external.span,
append_whitespace: true,

View File

@ -2,6 +2,7 @@ use crate::completions::{
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
DotNuCompletion, FileCompletion, FlagCompletion, VariableCompletion,
};
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
use nu_engine::eval_block;
use nu_parser::{flatten_expression, parse, FlatShape};
use nu_protocol::{
@ -110,10 +111,16 @@ impl NuCompleter {
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
let mut working_set = StateWorkingSet::new(&self.engine_state);
let offset = working_set.next_span_start();
// TODO: Callers should be trimming the line themselves
let line = if line.len() > pos { &line[..pos] } else { line };
// Adjust offset so that the spans of the suggestions will start at the right
// place even with `only_buffer_difference: true`
let fake_offset = offset + line.len() - pos;
let pos = offset + line.len();
let initial_line = line.to_string();
let mut line = line.to_string();
line.insert(pos, 'a');
let pos = offset + pos;
line.push('a');
let config = self.engine_state.get_config();
let output = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
@ -186,7 +193,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
}
@ -200,7 +207,7 @@ impl NuCompleter {
&working_set,
prefix.clone(),
new_span,
offset,
fake_offset,
pos,
);
@ -211,9 +218,12 @@ impl NuCompleter {
// We got no results for internal completion
// now we can check if external completer is set and use it
if let Some(block_id) = config.external_completer {
if let Some(external_result) = self
.external_completion(block_id, &spans, offset, new_span)
{
if let Some(external_result) = self.external_completion(
block_id,
&spans,
fake_offset,
new_span,
) {
return external_result;
}
}
@ -237,7 +247,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
}
@ -262,7 +272,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
} else if prev_expr_str == b"ls" {
@ -274,7 +284,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
}
@ -296,7 +306,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
}
@ -309,7 +319,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
}
@ -322,7 +332,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
}
@ -341,7 +351,7 @@ impl NuCompleter {
&working_set,
prefix.clone(),
new_span,
offset,
fake_offset,
pos,
);
@ -352,7 +362,10 @@ impl NuCompleter {
// Try to complete using an external completer (if set)
if let Some(block_id) = config.external_completer {
if let Some(external_result) = self.external_completion(
block_id, &spans, offset, new_span,
block_id,
&spans,
fake_offset,
new_span,
) {
return external_result;
}
@ -366,7 +379,7 @@ impl NuCompleter {
&working_set,
prefix,
new_span,
offset,
fake_offset,
pos,
);
@ -453,6 +466,7 @@ pub fn map_value_completions<'a>(
return Some(Suggestion {
value: s,
description: None,
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
@ -467,6 +481,7 @@ pub fn map_value_completions<'a>(
let mut suggestion = Suggestion {
value: String::from(""), // Initialize with empty string
description: None,
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
@ -494,6 +509,16 @@ pub fn map_value_completions<'a>(
suggestion.description = Some(desc_str);
}
}
// Match `style` column
if it.0 == "style" {
// Convert the value to string
suggestion.style = match it.1 {
Value::String { val, .. } => Some(lookup_ansi_color_style(val)),
Value::Record { .. } => Some(color_record_to_nustyle(it.1)),
_ => None,
};
}
});
return Some(suggestion);

View File

@ -44,6 +44,7 @@ impl Completer for DirectoryCompletion {
.map(move |x| Suggestion {
value: x.1,
description: None,
style: None,
extra: None,
span: reedline::Span {
start: x.0.start - offset,

View File

@ -111,6 +111,7 @@ impl Completer for DotNuCompletion {
.map(move |x| Suggestion {
value: x.1,
description: None,
style: None,
extra: None,
span: reedline::Span {
start: x.0.start - offset,

View File

@ -49,6 +49,7 @@ impl Completer for FileCompletion {
.map(move |x| Suggestion {
value: x.1,
description: None,
style: None,
extra: None,
span: reedline::Span {
start: x.0.start - offset,

View File

@ -46,6 +46,7 @@ impl Completer for FlagCompletion {
output.push(Suggestion {
value: String::from_utf8_lossy(&named).to_string(),
description: Some(flag_desc.to_string()),
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
@ -68,6 +69,7 @@ impl Completer for FlagCompletion {
output.push(Suggestion {
value: String::from_utf8_lossy(&named).to_string(),
description: Some(flag_desc.to_string()),
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,

View File

@ -95,6 +95,7 @@ impl Completer for VariableCompletion {
output.push(Suggestion {
value: env_var.0,
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
@ -165,6 +166,7 @@ impl Completer for VariableCompletion {
output.push(Suggestion {
value: builtin.to_string(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
@ -187,6 +189,7 @@ impl Completer for VariableCompletion {
output.push(Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
@ -208,6 +211,7 @@ impl Completer for VariableCompletion {
output.push(Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
@ -239,6 +243,7 @@ fn nested_suggestions(
output.push(Suggestion {
value: col,
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
@ -253,6 +258,7 @@ fn nested_suggestions(
output.push(Suggestion {
value: column_name.to_string(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
@ -266,6 +272,7 @@ fn nested_suggestions(
output.push(Suggestion {
value: column_name,
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,

View File

@ -19,7 +19,7 @@ pub use completions::{FileCompletion, NuCompleter};
pub use config_files::eval_config_contents;
pub use eval_cmds::evaluate_commands;
pub use eval_file::evaluate_file;
pub use menus::{DescriptionMenu, NuHelpCompleter};
pub use menus::NuHelpCompleter;
pub use nu_cmd_base::util::get_init_cwd;
pub use nu_highlight::NuHighlight;
pub use print::Print;

View File

@ -1,730 +0,0 @@
use {
nu_ansi_term::{ansi::RESET, Style},
reedline::{
menu_functions::string_difference, Completer, Editor, Menu, MenuEvent, MenuTextStyle,
Painter, Suggestion, UndoBehavior,
},
};
/// Default values used as reference for the menu. These values are set during
/// the initial declaration of the menu and are always kept as reference for the
/// changeable [`WorkingDetails`]
struct DefaultMenuDetails {
/// Number of columns that the menu will have
pub columns: u16,
/// Column width
pub col_width: Option<usize>,
/// Column padding
pub col_padding: usize,
/// Number of rows for commands
pub selection_rows: u16,
/// Number of rows allowed to display the description
pub description_rows: usize,
}
impl Default for DefaultMenuDetails {
fn default() -> Self {
Self {
columns: 4,
col_width: None,
col_padding: 2,
selection_rows: 4,
description_rows: 10,
}
}
}
/// Represents the actual column conditions of the menu. These conditions change
/// since they need to accommodate possible different line sizes for the column values
#[derive(Default)]
struct WorkingDetails {
/// Number of columns that the menu will have
pub columns: u16,
/// Column width
pub col_width: usize,
/// Number of rows for description
pub description_rows: usize,
}
/// Completion menu definition
pub struct DescriptionMenu {
/// Menu name
name: String,
/// Menu status
active: bool,
/// Menu coloring
color: MenuTextStyle,
/// Default column details that are set when creating the menu
/// These values are the reference for the working details
default_details: DefaultMenuDetails,
/// Number of minimum rows that are displayed when
/// the required lines is larger than the available lines
min_rows: u16,
/// Working column details keep changing based on the collected values
working_details: WorkingDetails,
/// Menu cached values
values: Vec<Suggestion>,
/// column position of the cursor. Starts from 0
col_pos: u16,
/// row position in the menu. Starts from 0
row_pos: u16,
/// Menu marker when active
marker: String,
/// Event sent to the menu
event: Option<MenuEvent>,
/// String collected after the menu is activated
input: Option<String>,
/// Examples to select
examples: Vec<String>,
/// Example index
example_index: Option<usize>,
/// Examples may not be shown if there is not enough space in the screen
show_examples: bool,
/// Skipped description rows
skipped_rows: usize,
/// Calls the completer using only the line buffer difference difference
/// after the menu was activated
only_buffer_difference: bool,
}
impl Default for DescriptionMenu {
fn default() -> Self {
Self {
name: "description_menu".to_string(),
active: false,
color: MenuTextStyle::default(),
default_details: DefaultMenuDetails::default(),
min_rows: 3,
working_details: WorkingDetails::default(),
values: Vec::new(),
col_pos: 0,
row_pos: 0,
marker: "? ".to_string(),
event: None,
input: None,
examples: Vec::new(),
example_index: None,
show_examples: true,
skipped_rows: 0,
only_buffer_difference: true,
}
}
}
// Menu configuration
impl DescriptionMenu {
/// Menu builder with new name
pub fn with_name(mut self, name: &str) -> Self {
self.name = name.into();
self
}
/// Menu builder with new value for text style
pub fn with_text_style(mut self, text_style: Style) -> Self {
self.color.text_style = text_style;
self
}
/// Menu builder with new value for text style
pub fn with_selected_text_style(mut self, selected_text_style: Style) -> Self {
self.color.selected_text_style = selected_text_style;
self
}
/// Menu builder with new value for text style
pub fn with_description_text_style(mut self, description_text_style: Style) -> Self {
self.color.description_style = description_text_style;
self
}
/// Menu builder with new columns value
pub fn with_columns(mut self, columns: u16) -> Self {
self.default_details.columns = columns;
self
}
/// Menu builder with new column width value
pub fn with_column_width(mut self, col_width: Option<usize>) -> Self {
self.default_details.col_width = col_width;
self
}
/// Menu builder with new column width value
pub fn with_column_padding(mut self, col_padding: usize) -> Self {
self.default_details.col_padding = col_padding;
self
}
/// Menu builder with new selection rows value
pub fn with_selection_rows(mut self, selection_rows: u16) -> Self {
self.default_details.selection_rows = selection_rows;
self
}
/// Menu builder with new description rows value
pub fn with_description_rows(mut self, description_rows: usize) -> Self {
self.default_details.description_rows = description_rows;
self
}
/// Menu builder with marker
pub fn with_marker(mut self, marker: String) -> Self {
self.marker = marker;
self
}
/// Menu builder with new only buffer difference
pub fn with_only_buffer_difference(mut self, only_buffer_difference: bool) -> Self {
self.only_buffer_difference = only_buffer_difference;
self
}
}
// Menu functionality
impl DescriptionMenu {
/// Move menu cursor to the next element
fn move_next(&mut self) {
let mut new_col = self.col_pos + 1;
let mut new_row = self.row_pos;
if new_col >= self.get_cols() {
new_row += 1;
new_col = 0;
}
if new_row >= self.get_rows() {
new_row = 0;
new_col = 0;
}
let position = new_row * self.get_cols() + new_col;
if position >= self.get_values().len() as u16 {
self.reset_position();
} else {
self.col_pos = new_col;
self.row_pos = new_row;
}
}
/// Move menu cursor to the previous element
fn move_previous(&mut self) {
let new_col = self.col_pos.checked_sub(1);
let (new_col, new_row) = match new_col {
Some(col) => (col, self.row_pos),
None => match self.row_pos.checked_sub(1) {
Some(row) => (self.get_cols().saturating_sub(1), row),
None => (
self.get_cols().saturating_sub(1),
self.get_rows().saturating_sub(1),
),
},
};
let position = new_row * self.get_cols() + new_col;
if position >= self.get_values().len() as u16 {
self.col_pos = (self.get_values().len() as u16 % self.get_cols()).saturating_sub(1);
self.row_pos = self.get_rows().saturating_sub(1);
} else {
self.col_pos = new_col;
self.row_pos = new_row;
}
}
/// Menu index based on column and row position
fn index(&self) -> usize {
let index = self.row_pos * self.get_cols() + self.col_pos;
index as usize
}
/// Get selected value from the menu
fn get_value(&self) -> Option<Suggestion> {
self.get_values().get(self.index()).cloned()
}
/// Calculates how many rows the Menu will use
fn get_rows(&self) -> u16 {
let values = self.get_values().len() as u16;
if values == 0 {
// When the values are empty the no_records_msg is shown, taking 1 line
return 1;
}
let rows = values / self.get_cols();
if values % self.get_cols() != 0 {
rows + 1
} else {
rows
}
}
/// Returns working details col width
fn get_width(&self) -> usize {
self.working_details.col_width
}
/// Reset menu position
fn reset_position(&mut self) {
self.col_pos = 0;
self.row_pos = 0;
self.skipped_rows = 0;
}
fn no_records_msg(&self, use_ansi_coloring: bool) -> String {
let msg = "TYPE TO START SEARCH";
if use_ansi_coloring {
format!(
"{}{}{}",
self.color.selected_text_style.prefix(),
msg,
RESET
)
} else {
msg.to_string()
}
}
/// Returns working details columns
fn get_cols(&self) -> u16 {
self.working_details.columns.max(1)
}
/// End of line for menu
fn end_of_line(&self, column: u16, index: usize) -> &str {
let is_last = index == self.values.len().saturating_sub(1);
if column == self.get_cols().saturating_sub(1) || is_last {
"\r\n"
} else {
""
}
}
/// Update list of examples from the actual value
fn update_examples(&mut self) {
self.examples = self
.get_value()
.and_then(|suggestion| suggestion.extra)
.unwrap_or_default();
self.example_index = None;
}
/// Creates default string that represents one suggestion from the menu
fn create_entry_string(
&self,
suggestion: &Suggestion,
index: usize,
column: u16,
empty_space: usize,
use_ansi_coloring: bool,
) -> String {
if use_ansi_coloring {
if index == self.index() {
format!(
"{}{}{}{:>empty$}{}",
self.color.selected_text_style.prefix(),
&suggestion.value,
RESET,
"",
self.end_of_line(column, index),
empty = empty_space,
)
} else {
format!(
"{}{}{}{:>empty$}{}",
self.color.text_style.prefix(),
&suggestion.value,
RESET,
"",
self.end_of_line(column, index),
empty = empty_space,
)
}
} else {
// If no ansi coloring is found, then the selection word is
// the line in uppercase
let (marker, empty_space) = if index == self.index() {
(">", empty_space.saturating_sub(1))
} else {
("", empty_space)
};
let line = format!(
"{}{}{:>empty$}{}",
marker,
&suggestion.value,
"",
self.end_of_line(column, index),
empty = empty_space,
);
if index == self.index() {
line.to_uppercase()
} else {
line
}
}
}
/// Description string with color
fn create_description_string(&self, use_ansi_coloring: bool) -> String {
let description = self
.get_value()
.and_then(|suggestion| suggestion.description)
.unwrap_or_default()
.lines()
.skip(self.skipped_rows)
.take(self.working_details.description_rows)
.collect::<Vec<&str>>()
.join("\r\n");
if use_ansi_coloring && !description.is_empty() {
format!(
"{}{}{}",
self.color.description_style.prefix(),
description,
RESET,
)
} else {
description
}
}
/// Selectable list of examples from the actual value
fn create_example_string(&self, use_ansi_coloring: bool) -> String {
if !self.show_examples {
return "".into();
}
let examples: String = self
.examples
.iter()
.enumerate()
.map(|(index, example)| {
if let Some(example_index) = self.example_index {
if index == example_index {
format!(
" {}{}{}\r\n",
self.color.selected_text_style.prefix(),
example,
RESET
)
} else {
format!(" {example}\r\n")
}
} else {
format!(" {example}\r\n")
}
})
.collect();
if examples.is_empty() {
"".into()
} else if use_ansi_coloring {
format!(
"{}\r\n\r\nExamples:\r\n{}{}",
self.color.description_style.prefix(),
RESET,
examples,
)
} else {
format!("\r\n\r\nExamples:\r\n{examples}",)
}
}
}
impl Menu for DescriptionMenu {
/// Menu name
fn name(&self) -> &str {
self.name.as_str()
}
/// Menu indicator
fn indicator(&self) -> &str {
self.marker.as_str()
}
/// Deactivates context menu
fn is_active(&self) -> bool {
self.active
}
/// The menu stays active even with one record
fn can_quick_complete(&self) -> bool {
false
}
/// The menu does not need to partially complete
fn can_partially_complete(
&mut self,
_values_updated: bool,
_editor: &mut Editor,
_completer: &mut dyn Completer,
) -> bool {
false
}
/// Selects what type of event happened with the menu
fn menu_event(&mut self, event: MenuEvent) {
match &event {
MenuEvent::Activate(_) => self.active = true,
MenuEvent::Deactivate => {
self.active = false;
self.input = None;
self.values = Vec::new();
}
_ => {}
};
self.event = Some(event);
}
/// Updates menu values
fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) {
if self.only_buffer_difference {
if let Some(old_string) = &self.input {
let (start, input) = string_difference(editor.get_buffer(), old_string);
if !input.is_empty() {
self.reset_position();
self.values = completer.complete(input, start);
}
}
} else {
let trimmed_buffer = editor.get_buffer().replace('\n', " ");
self.values = completer.complete(
trimmed_buffer.as_str(),
editor.line_buffer().insertion_point(),
);
self.reset_position();
}
}
/// The working details for the menu changes based on the size of the lines
/// collected from the completer
fn update_working_details(
&mut self,
editor: &mut Editor,
completer: &mut dyn Completer,
painter: &Painter,
) {
if let Some(event) = self.event.take() {
// Updating all working parameters from the menu before executing any of the
// possible event
let max_width = self.get_values().iter().fold(0, |acc, suggestion| {
let str_len = suggestion.value.len() + self.default_details.col_padding;
if str_len > acc {
str_len
} else {
acc
}
});
// If no default width is found, then the total screen width is used to estimate
// the column width based on the default number of columns
let default_width = if let Some(col_width) = self.default_details.col_width {
col_width
} else {
let col_width = painter.screen_width() / self.default_details.columns;
col_width as usize
};
// Adjusting the working width of the column based the max line width found
// in the menu values
if max_width > default_width {
self.working_details.col_width = max_width;
} else {
self.working_details.col_width = default_width;
};
// The working columns is adjusted based on possible number of columns
// that could be fitted in the screen with the calculated column width
let possible_cols = painter.screen_width() / self.working_details.col_width as u16;
if possible_cols > self.default_details.columns {
self.working_details.columns = self.default_details.columns.max(1);
} else {
self.working_details.columns = possible_cols;
}
// Updating the working rows to display the description
if self.menu_required_lines(painter.screen_width()) <= painter.remaining_lines() {
self.working_details.description_rows = self.default_details.description_rows;
self.show_examples = true;
} else {
self.working_details.description_rows = painter
.remaining_lines()
.saturating_sub(self.default_details.selection_rows + 1)
as usize;
self.show_examples = false;
}
match event {
MenuEvent::Activate(_) => {
self.reset_position();
self.input = Some(editor.get_buffer().to_string());
self.update_values(editor, completer);
}
MenuEvent::Deactivate => self.active = false,
MenuEvent::Edit(_) => {
self.reset_position();
self.update_values(editor, completer);
self.update_examples()
}
MenuEvent::NextElement => {
self.skipped_rows = 0;
self.move_next();
self.update_examples();
}
MenuEvent::PreviousElement => {
self.skipped_rows = 0;
self.move_previous();
self.update_examples();
}
MenuEvent::MoveUp => {
if let Some(example_index) = self.example_index {
if let Some(index) = example_index.checked_sub(1) {
self.example_index = Some(index);
} else {
self.example_index = Some(self.examples.len().saturating_sub(1));
}
} else if !self.examples.is_empty() {
self.example_index = Some(0);
}
}
MenuEvent::MoveDown => {
if let Some(example_index) = self.example_index {
let index = example_index + 1;
if index < self.examples.len() {
self.example_index = Some(index);
} else {
self.example_index = Some(0);
}
} else if !self.examples.is_empty() {
self.example_index = Some(0);
}
}
MenuEvent::MoveLeft => self.skipped_rows = self.skipped_rows.saturating_sub(1),
MenuEvent::MoveRight => {
let skipped = self.skipped_rows + 1;
let description_rows = self
.get_value()
.and_then(|suggestion| suggestion.description)
.unwrap_or_default()
.lines()
.count();
let allowed_skips =
description_rows.saturating_sub(self.working_details.description_rows);
if skipped < allowed_skips {
self.skipped_rows = skipped;
} else {
self.skipped_rows = allowed_skips;
}
}
MenuEvent::PreviousPage | MenuEvent::NextPage => {}
}
}
}
/// The buffer gets replaced in the Span location
fn replace_in_buffer(&self, editor: &mut Editor) {
if let Some(Suggestion { value, span, .. }) = self.get_value() {
let start = span.start.min(editor.line_buffer().len());
let end = span.end.min(editor.line_buffer().len());
let replacement = if let Some(example_index) = self.example_index {
self.examples
.get(example_index)
.expect("the example index is always checked")
} else {
&value
};
editor.edit_buffer(
|lb| {
lb.replace_range(start..end, replacement);
let mut offset = lb.insertion_point();
offset += lb
.len()
.saturating_sub(end.saturating_sub(start))
.saturating_sub(start);
lb.set_insertion_point(offset);
},
UndoBehavior::CreateUndoPoint,
);
}
}
/// Minimum rows that should be displayed by the menu
fn min_rows(&self) -> u16 {
self.get_rows().min(self.min_rows)
}
/// Gets values from filler that will be displayed in the menu
fn get_values(&self) -> &[Suggestion] {
&self.values
}
fn menu_required_lines(&self, _terminal_columns: u16) -> u16 {
let example_lines = self
.examples
.iter()
.fold(0, |acc, example| example.lines().count() + acc);
self.default_details.selection_rows
+ self.default_details.description_rows as u16
+ example_lines as u16
+ 3
}
fn menu_string(&self, _available_lines: u16, use_ansi_coloring: bool) -> String {
if self.get_values().is_empty() {
self.no_records_msg(use_ansi_coloring)
} else {
// The skip values represent the number of lines that should be skipped
// while printing the menu
let available_lines = self.default_details.selection_rows;
let skip_values = if self.row_pos >= available_lines {
let skip_lines = self.row_pos.saturating_sub(available_lines) + 1;
(skip_lines * self.get_cols()) as usize
} else {
0
};
// It seems that crossterm prefers to have a complete string ready to be printed
// rather than looping through the values and printing multiple things
// This reduces the flickering when printing the menu
let available_values = (available_lines * self.get_cols()) as usize;
let selection_values: String = self
.get_values()
.iter()
.skip(skip_values)
.take(available_values)
.enumerate()
.map(|(index, suggestion)| {
// Correcting the enumerate index based on the number of skipped values
let index = index + skip_values;
let column = index as u16 % self.get_cols();
let empty_space = self.get_width().saturating_sub(suggestion.value.len());
self.create_entry_string(
suggestion,
index,
column,
empty_space,
use_ansi_coloring,
)
})
.collect();
format!(
"{}{}{}",
selection_values,
self.create_description_string(use_ansi_coloring),
self.create_example_string(use_ansi_coloring)
)
}
}
}

View File

@ -102,10 +102,11 @@ impl NuHelpCompleter {
Suggestion {
value: sig.name.clone(),
description: Some(long_desc),
style: None,
extra: Some(extra),
span: reedline::Span {
start: pos,
end: pos + line.len(),
start: pos - line.len(),
end: pos,
},
append_whitespace: false,
}
@ -119,3 +120,42 @@ impl Completer for NuHelpCompleter {
self.completion_helper(line, pos)
}
}
#[cfg(test)]
mod test {
use super::*;
use rstest::rstest;
#[rstest]
#[case("who", 5, 8, &["whoami"])]
#[case("hash", 1, 5, &["hash", "hash md5", "hash sha256"])]
#[case("into f", 0, 6, &["into float", "into filesize"])]
#[case("into nonexistent", 0, 16, &[])]
fn test_help_completer(
#[case] line: &str,
#[case] start: usize,
#[case] end: usize,
#[case] expected: &[&str],
) {
let engine_state =
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context());
let mut completer = NuHelpCompleter::new(engine_state.into());
let suggestions = completer.complete(line, end);
assert_eq!(
expected.len(),
suggestions.len(),
"expected {:?}, got {:?}",
expected,
suggestions
.iter()
.map(|s| s.value.clone())
.collect::<Vec<_>>()
);
for (exp, actual) in expected.iter().zip(suggestions) {
assert_eq!(exp, &actual.value);
assert_eq!(reedline::Span::new(start, end), actual.span);
}
}
}

View File

@ -101,9 +101,13 @@ fn convert_to_suggestions(
}
}
_ => reedline::Span {
start: if only_buffer_difference { pos } else { 0 },
start: if only_buffer_difference {
pos - line.len()
} else {
0
},
end: if only_buffer_difference {
pos + line.len()
pos
} else {
line.len()
},
@ -111,9 +115,13 @@ fn convert_to_suggestions(
}
}
_ => reedline::Span {
start: if only_buffer_difference { pos } else { 0 },
start: if only_buffer_difference {
pos - line.len()
} else {
0
},
end: if only_buffer_difference {
pos + line.len()
pos
} else {
line.len()
},
@ -138,6 +146,7 @@ fn convert_to_suggestions(
vec![Suggestion {
value: text,
description,
style: None,
extra,
span,
append_whitespace: false,
@ -150,10 +159,19 @@ fn convert_to_suggestions(
_ => vec![Suggestion {
value: format!("Not a record: {value:?}"),
description: None,
style: None,
extra: None,
span: reedline::Span {
start: 0,
end: line.len(),
start: if only_buffer_difference {
pos - line.len()
} else {
0
},
end: if only_buffer_difference {
pos
} else {
line.len()
},
},
append_whitespace: false,
}],

View File

@ -1,7 +1,5 @@
mod description_menu;
mod help_completions;
mod menu_completions;
pub use description_menu::DescriptionMenu;
pub use help_completions::NuHelpCompleter;
pub use menu_completions::NuMenuCompleter;

View File

@ -28,7 +28,7 @@ impl Command for NuHighlight {
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
@ -40,6 +40,7 @@ impl Command for NuHighlight {
let highlighter = crate::NuHighlighter {
engine_state,
stack: std::sync::Arc::new(stack.clone()),
config,
};

View File

@ -54,8 +54,8 @@ Since this command has no output, there is no point in piping it with other comm
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
let no_newline = call.has_flag("no-newline");
let to_stderr = call.has_flag("stderr");
let no_newline = call.has_flag(engine_state, stack, "no-newline")?;
let to_stderr = call.has_flag(engine_state, stack, "stderr")?;
// This will allow for easy printing of pipelines as well
if !args.is_empty() {

View File

@ -1,4 +1,3 @@
use super::DescriptionMenu;
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
use crossterm::event::{KeyCode, KeyModifiers};
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
@ -12,7 +11,8 @@ use nu_protocol::{
};
use reedline::{
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
ColumnarMenu, EditCommand, Keybindings, ListMenu, Reedline, ReedlineEvent, ReedlineMenu,
ColumnarMenu, DescriptionMenu, DescriptionMode, EditCommand, IdeMenu, Keybindings, ListMenu,
MenuBuilder, Reedline, ReedlineEvent, ReedlineMenu,
};
use std::sync::Arc;
@ -138,9 +138,10 @@ fn add_menu(
match layout.as_str() {
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config),
"list" => add_list_menu(line_editor, menu, engine_state, stack, config),
"ide" => add_ide_menu(line_editor, menu, engine_state, stack, config),
"description" => add_description_menu(line_editor, menu, engine_state, stack, config),
_ => Err(ShellError::UnsupportedConfigValue {
expected: "columnar, list or description".to_string(),
expected: "columnar, list, ide or description".to_string(),
value: menu.menu_type.into_abbreviated_string(config),
span: menu.menu_type.span(),
}),
@ -235,10 +236,26 @@ pub(crate) fn add_columnar_menu(
columnar_menu,
ColumnarMenu::with_description_text_style
);
add_style!(
"match_text",
val,
span,
config,
columnar_menu,
ColumnarMenu::with_match_text_style
);
add_style!(
"selected_match_text",
val,
span,
config,
columnar_menu,
ColumnarMenu::with_selected_match_text_style
);
}
let marker = menu.marker.into_string("", config);
columnar_menu = columnar_menu.with_marker(marker);
columnar_menu = columnar_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
columnar_menu = columnar_menu.with_only_buffer_difference(only_buffer_difference);
@ -320,7 +337,7 @@ pub(crate) fn add_list_menu(
}
let marker = menu.marker.into_string("", config);
list_menu = list_menu.with_marker(marker);
list_menu = list_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
list_menu = list_menu.with_only_buffer_difference(only_buffer_difference);
@ -351,6 +368,232 @@ pub(crate) fn add_list_menu(
}
}
// Adds an IDE menu to the line editor
pub(crate) fn add_ide_menu(
line_editor: Reedline,
menu: &ParsedMenu,
engine_state: Arc<EngineState>,
stack: &Stack,
config: &Config,
) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span();
let name = menu.name.into_string("", config);
let mut ide_menu = IdeMenu::default().with_name(&name);
if let Value::Record { val, .. } = &menu.menu_type {
ide_menu = match extract_value("min_completion_width", val, span) {
Ok(min_completion_width) => {
let min_completion_width = min_completion_width.as_int()?;
ide_menu.with_min_completion_width(min_completion_width as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("max_completion_width", val, span) {
Ok(max_completion_width) => {
let max_completion_width = max_completion_width.as_int()?;
ide_menu.with_max_completion_width(max_completion_width as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("max_completion_height", val, span) {
Ok(max_completion_height) => {
let max_completion_height = max_completion_height.as_int()?;
ide_menu.with_max_completion_height(max_completion_height as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("padding", val, span) {
Ok(padding) => {
let padding = padding.as_int()?;
ide_menu.with_padding(padding as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("border", val, span) {
Ok(border) => {
if let Ok(border) = border.as_bool() {
if border {
ide_menu.with_default_border()
} else {
ide_menu
}
} else if let Ok(border_chars) = border.as_record() {
let top_right = extract_value("top_right", border_chars, span)?.as_char()?;
let top_left = extract_value("top_left", border_chars, span)?.as_char()?;
let bottom_right =
extract_value("bottom_right", border_chars, span)?.as_char()?;
let bottom_left =
extract_value("bottom_left", border_chars, span)?.as_char()?;
let horizontal = extract_value("horizontal", border_chars, span)?.as_char()?;
let vertical = extract_value("vertical", border_chars, span)?.as_char()?;
ide_menu.with_border(
top_right,
top_left,
bottom_right,
bottom_left,
horizontal,
vertical,
)
} else {
return Err(ShellError::UnsupportedConfigValue {
expected: "bool or record".to_string(),
value: border.into_abbreviated_string(config),
span: border.span(),
});
}
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("cursor_offset", val, span) {
Ok(cursor_offset) => {
let cursor_offset = cursor_offset.as_int()?;
ide_menu.with_cursor_offset(cursor_offset as i16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("description_mode", val, span) {
Ok(description_mode) => {
let description_mode_str = description_mode.as_string()?;
match description_mode_str.as_str() {
"left" => ide_menu.with_description_mode(DescriptionMode::Left),
"right" => ide_menu.with_description_mode(DescriptionMode::Right),
"prefer_right" => ide_menu.with_description_mode(DescriptionMode::PreferRight),
_ => {
return Err(ShellError::UnsupportedConfigValue {
expected: "\"left\", \"right\" or \"prefer_right\"".to_string(),
value: description_mode.into_abbreviated_string(config),
span: description_mode.span(),
});
}
}
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("min_description_width", val, span) {
Ok(min_description_width) => {
let min_description_width = min_description_width.as_int()?;
ide_menu.with_min_description_width(min_description_width as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("max_description_width", val, span) {
Ok(max_description_width) => {
let max_description_width = max_description_width.as_int()?;
ide_menu.with_max_description_width(max_description_width as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("max_description_height", val, span) {
Ok(max_description_height) => {
let max_description_height = max_description_height.as_int()?;
ide_menu.with_max_description_height(max_description_height as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("description_offset", val, span) {
Ok(description_padding) => {
let description_padding = description_padding.as_int()?;
ide_menu.with_description_offset(description_padding as u16)
}
Err(_) => ide_menu,
};
ide_menu = match extract_value("correct_cursor_pos", val, span) {
Ok(correct_cursor_pos) => {
let correct_cursor_pos = correct_cursor_pos.as_bool()?;
ide_menu.with_correct_cursor_pos(correct_cursor_pos)
}
Err(_) => ide_menu,
};
}
let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
add_style!(
"text",
val,
span,
config,
ide_menu,
IdeMenu::with_text_style
);
add_style!(
"selected_text",
val,
span,
config,
ide_menu,
IdeMenu::with_selected_text_style
);
add_style!(
"description_text",
val,
span,
config,
ide_menu,
IdeMenu::with_description_text_style
);
add_style!(
"match_text",
val,
span,
config,
ide_menu,
IdeMenu::with_match_text_style
);
add_style!(
"selected_match_text",
val,
span,
config,
ide_menu,
IdeMenu::with_selected_match_text_style
);
}
let marker = menu.marker.into_string("", config);
ide_menu = ide_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
ide_menu = ide_menu.with_only_buffer_difference(only_buffer_difference);
let span = menu.source.span();
match &menu.source {
Value::Nothing { .. } => {
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(ide_menu))))
}
Value::Closure { val, .. } => {
let menu_completer = NuMenuCompleter::new(
val.block_id,
span,
stack.captures_to_stack(val.captures.clone()),
engine_state,
only_buffer_difference,
);
Ok(line_editor.with_menu(ReedlineMenu::WithCompleter {
menu: Box::new(ide_menu),
completer: Box::new(menu_completer),
}))
}
_ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(),
value: menu.source.into_abbreviated_string(config),
span,
}),
}
}
// Adds a description menu to the line editor
pub(crate) fn add_description_menu(
line_editor: Reedline,
@ -434,7 +677,7 @@ pub(crate) fn add_description_menu(
}
let marker = menu.marker.into_string("", config);
description_menu = description_menu.with_marker(marker);
description_menu = description_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
description_menu = description_menu.with_only_buffer_difference(only_buffer_difference);
@ -861,22 +1104,82 @@ fn edit_from_record(
span: Span,
) -> Result<EditCommand, ShellError> {
let edit = match name {
"movetostart" => EditCommand::MoveToStart,
"movetolinestart" => EditCommand::MoveToLineStart,
"movetoend" => EditCommand::MoveToEnd,
"movetolineend" => EditCommand::MoveToLineEnd,
"moveleft" => EditCommand::MoveLeft,
"moveright" => EditCommand::MoveRight,
"movewordleft" => EditCommand::MoveWordLeft,
"movebigwordleft" => EditCommand::MoveBigWordLeft,
"movewordright" => EditCommand::MoveWordRight,
"movewordrightend" => EditCommand::MoveWordRightEnd,
"movebigwordrightend" => EditCommand::MoveBigWordRightEnd,
"movewordrightstart" => EditCommand::MoveWordRightStart,
"movebigwordrightstart" => EditCommand::MoveBigWordRightStart,
"movetostart" => EditCommand::MoveToStart {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movetolinestart" => EditCommand::MoveToLineStart {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movetoend" => EditCommand::MoveToEnd {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movetolineend" => EditCommand::MoveToLineEnd {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"moveleft" => EditCommand::MoveLeft {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"moveright" => EditCommand::MoveRight {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movewordleft" => EditCommand::MoveWordLeft {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movebigwordleft" => EditCommand::MoveBigWordLeft {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movewordright" => EditCommand::MoveWordRight {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.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 {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movebigwordrightstart" => EditCommand::MoveBigWordRightStart {
select: extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false),
},
"movetoposition" => {
let value = extract_value("value", record, span)?;
EditCommand::MoveToPosition(value.as_int()? as usize)
let select = extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false);
EditCommand::MoveToPosition {
position: value.as_int()? as usize,
select,
}
}
"insertchar" => {
let value = extract_value("value", record, span)?;
@ -928,12 +1231,18 @@ fn edit_from_record(
"moverightuntil" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value, config)?;
EditCommand::MoveRightUntil(char)
let select = extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false);
EditCommand::MoveRightUntil { c: char, select }
}
"moverightbefore" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value, config)?;
EditCommand::MoveRightBefore(char)
let select = extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false);
EditCommand::MoveRightBefore { c: char, select }
}
"cutleftuntil" => {
let value = extract_value("value", record, span)?;
@ -948,14 +1257,23 @@ fn edit_from_record(
"moveleftuntil" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value, config)?;
EditCommand::MoveLeftUntil(char)
let select = extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false);
EditCommand::MoveLeftUntil { c: char, select }
}
"moveleftbefore" => {
let value = extract_value("value", record, span)?;
let char = extract_char(value, config)?;
EditCommand::MoveLeftBefore(char)
let select = extract_value("select", record, span)
.and_then(|value| value.as_bool())
.unwrap_or(false);
EditCommand::MoveLeftBefore { c: char, select }
}
"complete" => EditCommand::Complete,
"cutselection" => EditCommand::CutSelection,
"copyselection" => EditCommand::CopySelection,
"selectall" => EditCommand::SelectAll,
e => {
return Err(ShellError::UnsupportedConfigValue {
expected: "reedline EditCommand".to_string(),
@ -1109,4 +1427,57 @@ mod test {
let b = EventType::try_from_record(&event, span);
assert!(matches!(b, Err(ShellError::MissingConfigValue { .. })));
}
#[test]
fn test_move_without_optional_select() {
let event = record! {
"edit" => Value::test_string("moveleft")
};
let event = Value::test_record(event);
let config = Config::default();
let parsed_event = parse_event(&event, &config).unwrap();
assert_eq!(
parsed_event,
Some(ReedlineEvent::Edit(vec![EditCommand::MoveLeft {
select: false
}]))
);
}
#[test]
fn test_move_with_select_false() {
let event = record! {
"edit" => Value::test_string("moveleft"),
"select" => Value::test_bool(false)
};
let event = Value::test_record(event);
let config = Config::default();
let parsed_event = parse_event(&event, &config).unwrap();
assert_eq!(
parsed_event,
Some(ReedlineEvent::Edit(vec![EditCommand::MoveLeft {
select: false
}]))
);
}
#[test]
fn test_move_with_select_true() {
let event = record! {
"edit" => Value::test_string("moveleft"),
"select" => Value::test_bool(true)
};
let event = Value::test_record(event);
let config = Config::default();
let parsed_event = parse_event(&event, &config).unwrap();
assert_eq!(
parsed_event,
Some(ReedlineEvent::Edit(vec![EditCommand::MoveLeft {
select: true
}]))
);
}
}

View File

@ -11,13 +11,13 @@ use miette::{ErrReport, IntoDiagnostic, Result};
use nu_cmd_base::util::get_guaranteed_cwd;
use nu_cmd_base::{hook::eval_hook, util::get_editor};
use nu_color_config::StyleComputer;
use nu_engine::convert_env_values;
use nu_engine::{convert_env_values, env_to_strings};
use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::{
config::NuCursorShape,
engine::{EngineState, Stack, StateWorkingSet},
eval_const::create_nu_constant,
report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
report_error_new, HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
Value, NU_VARIABLE_ID,
};
use nu_utils::utils::perf;
@ -28,9 +28,9 @@ use reedline::{
use std::{
env::temp_dir,
io::{self, IsTerminal, Write},
path::Path,
path::PathBuf,
sync::atomic::Ordering,
time::Instant,
time::{Duration, Instant},
};
use sysinfo::System;
@ -44,6 +44,9 @@ const PRE_EXECUTE_MARKER: &str = "\x1b]133;C\x1b\\";
// const CMD_FINISHED_MARKER: &str = "\x1b]133;D;{}\x1b\\";
const RESET_APPLICATION_MODE: &str = "\x1b[?1l";
///
/// The main REPL loop, including spinning up the prompt itself.
///
pub fn evaluate_repl(
engine_state: &mut EngineState,
stack: &mut Stack,
@ -57,15 +60,7 @@ pub fn evaluate_repl(
let config = engine_state.get_config();
let use_color = config.use_ansi_coloring;
// Guard against invocation without a connected terminal.
// reedline / crossterm event polling will fail without a connected tty
if !std::io::stdin().is_terminal() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Nushell launched as a REPL, but STDIN is not a TTY; either launch in a valid terminal or provide arguments to invoke a script!",
))
.into_diagnostic();
}
confirm_stdin_is_terminal()?;
let mut entry_num = 0;
@ -74,8 +69,7 @@ pub fn evaluate_repl(
let start_time = std::time::Instant::now();
// Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
report_error_new(engine_state, &e);
}
perf(
"translate env vars",
@ -109,31 +103,20 @@ pub fn evaluate_repl(
use_color,
);
// Setup history_isolation aka "history per session"
let history_isolation = engine_state.get_config().history_isolation;
let history_session_id = if history_isolation {
Reedline::create_history_session_id()
} else {
None
};
if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now();
start_time = std::time::Instant::now();
let history_path = crate::config_files::get_history_path(
nushell_path,
engine_state.config.history_file_format,
);
if let Some(history_path) = history_path.as_deref() {
line_editor =
update_line_editor_history(engine_state, history_path, line_editor, history_session_id)?
};
perf(
"setup history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
line_editor = setup_history(nushell_path, engine_state, line_editor, history)?;
perf(
"setup history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
}
if let Some(s) = prerun_command {
eval_source(
@ -164,9 +147,7 @@ pub fn evaluate_repl(
);
}
if engine_state.get_config().use_kitty_protocol && !reedline::kitty_protocol_available() {
warn!("Terminal doesn't support use_kitty_protocol config");
}
kitty_protocol_healthcheck(engine_state);
loop {
let loop_start_time = std::time::Instant::now();
@ -231,6 +212,7 @@ pub fn evaluate_repl(
.use_bracketed_paste(cfg!(not(target_os = "windows")) && config.bracketed_paste)
.with_highlighter(Box::new(NuHighlighter {
engine_state: engine_reference.clone(),
stack: std::sync::Arc::new(stack.clone()),
config: config.clone(),
}))
.with_validator(Box::new(NuValidator {
@ -276,8 +258,7 @@ pub fn evaluate_repl(
start_time = std::time::Instant::now();
line_editor = add_menus(line_editor, engine_reference, stack, config).unwrap_or_else(|e| {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
report_error_new(engine_state, &e);
Reedline::create()
});
perf(
@ -294,12 +275,9 @@ pub fn evaluate_repl(
line_editor = if let Ok((cmd, args)) = buffer_editor {
let mut command = std::process::Command::new(&cmd);
command.args(args).envs(
engine_state
.render_env_vars()
.into_iter()
.filter_map(|(k, v)| v.as_string().ok().map(|v| (k, v))),
);
command
.args(args)
.envs(env_to_strings(engine_state, stack)?);
line_editor.with_buffer_editor(command, temp_file.clone())
} else {
line_editor
@ -313,43 +291,26 @@ pub fn evaluate_repl(
use_color,
);
start_time = std::time::Instant::now();
if config.sync_history_on_enter {
if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {}", e);
if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now();
if history.sync_on_enter {
if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {}", e);
}
}
perf(
"sync_history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
}
perf(
"sync_history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now();
// Changing the line editor based on the found keybindings
line_editor = match create_keybindings(config) {
Ok(keybindings) => match keybindings {
KeybindingsMode::Emacs(keybindings) => {
let edit_mode = Box::new(Emacs::new(keybindings));
line_editor.with_edit_mode(edit_mode)
}
KeybindingsMode::Vi {
insert_keybindings,
normal_keybindings,
} => {
let edit_mode = Box::new(Vi::new(insert_keybindings, normal_keybindings));
line_editor.with_edit_mode(edit_mode)
}
},
Err(e) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
line_editor
}
};
line_editor = setup_keybindings(engine_state, line_editor);
perf(
"keybindings",
start_time,
@ -418,19 +379,13 @@ pub fn evaluate_repl(
match input {
Ok(Signal::Success(s)) => {
let hostname = System::host_name();
let history_supports_meta =
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
{
line_editor
.update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now());
c.hostname = hostname.clone();
let history_supports_meta = matches!(
engine_state.history_config().map(|h| h.file_format),
Some(HistoryFileFormat::Sqlite)
);
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
c
})
.into_diagnostic()?; // todo: don't stop repl if error here?
if history_supports_meta {
prepare_history_metadata(&s, &hostname, engine_state, &mut line_editor)?;
}
// Right before we start running the code the user gave us, fire the `pre_execution`
@ -457,107 +412,27 @@ pub fn evaluate_repl(
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
}
// Actual command execution logic starts from here
let start_time = Instant::now();
let tokens = lex(s.as_bytes(), 0, &[], &[], false);
// Check if this is a single call to a directory, if so auto-cd
let cwd = nu_engine::env::current_dir_str(engine_state, stack)?;
let mut orig = s.clone();
if orig.starts_with('`') {
orig = trim_quotes_str(&orig).to_string()
}
let path = nu_path::expand_path_with(&orig, &cwd);
if looks_like_path(&orig) && path.is_dir() && tokens.0.len() == 1 {
// We have an auto-cd
let (path, span) = {
if !path.exists() {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::DirectoryNotFound {
dir: path.to_string_lossy().to_string(),
span: tokens.0[0].span,
},
);
}
let path = nu_path::canonicalize_with(path, &cwd)
.expect("internal error: cannot canonicalize known path");
(path.to_string_lossy().to_string(), tokens.0[0].span)
};
stack.add_env_var("OLDPWD".into(), Value::string(cwd.clone(), Span::unknown()));
//FIXME: this only changes the current scope, but instead this environment variable
//should probably be a block that loads the information from the state in the overlay
stack.add_env_var("PWD".into(), Value::string(path.clone(), Span::unknown()));
let cwd = Value::string(cwd, span);
let shells = stack.get_env_var(engine_state, "NUSHELL_SHELLS");
let mut shells = if let Some(v) = shells {
v.as_list()
.map(|x| x.to_vec())
.unwrap_or_else(|_| vec![cwd])
} else {
vec![cwd]
};
let current_shell = stack.get_env_var(engine_state, "NUSHELL_CURRENT_SHELL");
let current_shell = if let Some(v) = current_shell {
v.as_int().unwrap_or_default() as usize
} else {
0
};
let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL");
let last_shell = if let Some(v) = last_shell {
v.as_int().unwrap_or_default() as usize
} else {
0
};
shells[current_shell] = Value::string(path, span);
stack.add_env_var("NUSHELL_SHELLS".into(), Value::list(shells, span));
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::int(last_shell as i64, span),
);
} else if !s.trim().is_empty() {
trace!("eval source: {}", s);
let mut cmds = s.split_whitespace();
if let Some("exit") = cmds.next() {
let mut working_set = StateWorkingSet::new(engine_state);
let _ = parse(&mut working_set, None, s.as_bytes(), false);
if working_set.parse_errors.is_empty() {
match cmds.next() {
Some(s) => {
if let Ok(n) = s.parse::<i32>() {
drop(line_editor);
std::process::exit(n);
}
}
None => {
drop(line_editor);
std::process::exit(0);
}
}
}
match parse_operation(s.clone(), engine_state, stack)? {
ReplOperation::AutoCd { cwd, target, span } => {
do_auto_cd(target, cwd, stack, engine_state, span);
}
eval_source(
engine_state,
stack,
s.as_bytes(),
&format!("entry #{entry_num}"),
PipelineData::empty(),
false,
);
ReplOperation::RunCommand(cmd) => {
line_editor = do_run_cmd(
&cmd,
stack,
engine_state,
line_editor,
shell_integration,
entry_num,
)?;
}
// as the name implies, we do nothing in this case
ReplOperation::DoNothing => {}
}
let cmd_duration = start_time.elapsed();
stack.add_env_var(
@ -565,74 +440,21 @@ pub fn evaluate_repl(
Value::string(format!("{}", cmd_duration.as_millis()), Span::unknown()),
);
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
{
line_editor
.update_last_command_context(&|mut c| {
c.duration = Some(cmd_duration);
c.exit_status = stack
.get_env_var(engine_state, "LAST_EXIT_CODE")
.and_then(|e| e.as_i64().ok());
c
})
.into_diagnostic()?; // todo: don't stop repl if error here?
if history_supports_meta {
fill_in_result_related_history_metadata(
&s,
engine_state,
cmd_duration,
stack,
&mut line_editor,
)?;
}
if shell_integration {
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
let path = cwd.as_string()?;
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
// https://code.visualstudio.com/docs/terminal/shell-integration#_supported-escape-sequences
if stack.get_env_var(engine_state, "TERM_PROGRAM")
== Some(Value::test_string("vscode"))
{
// If we're in vscode, run their specific ansi escape sequence.
// This is helpful for ctrl+g to change directories in the terminal.
run_ansi_sequence(&format!("\x1b]633;P;Cwd={}\x1b\\", path))?;
} else {
// Otherwise, communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
run_ansi_sequence(&format!(
"\x1b]7;file://{}{}{}\x1b\\",
percent_encoding::utf8_percent_encode(
&hostname.unwrap_or_else(|| "localhost".to_string()),
percent_encoding::CONTROLS
),
if path.starts_with('/') { "" } else { "/" },
percent_encoding::utf8_percent_encode(
&path,
percent_encoding::CONTROLS
)
))?;
}
// Try to abbreviate string for windows title
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
path.replace(&p.as_path().display().to_string(), "~")
} else {
path
};
// Set window title too
// https://tldp.org/HOWTO/Xterm-Title-3.html
// ESC]0;stringBEL -- Set icon name and window title to string
// ESC]1;stringBEL -- Set icon name to string
// ESC]2;stringBEL -- Set window title to string
run_ansi_sequence(&format!("\x1b]2;{maybe_abbrev_path}\x07"))?;
}
run_ansi_sequence(RESET_APPLICATION_MODE)?;
do_shell_integration_finalize_command(hostname, engine_state, stack)?;
}
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
line_editor.run_edit_commands(&[
EditCommand::Clear,
EditCommand::InsertString(repl.buffer.to_string()),
EditCommand::MoveToPosition(repl.cursor_pos),
]);
repl.buffer = "".to_string();
repl.cursor_pos = 0;
drop(repl);
flush_engine_state_repl_buffer(engine_state, &mut line_editor);
}
Ok(Signal::CtrlC) => {
// `Reedline` clears the line content. New prompt is shown
@ -684,6 +506,364 @@ pub fn evaluate_repl(
Ok(())
}
///
/// Put in history metadata not related to the result of running the command
///
fn prepare_history_metadata(
s: &str,
hostname: &Option<String>,
engine_state: &EngineState,
line_editor: &mut Reedline,
) -> Result<()> {
if !s.is_empty() && line_editor.has_last_command_context() {
line_editor
.update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now());
c.hostname = hostname.clone();
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
c
})
.into_diagnostic()?; // todo: don't stop repl if error here?
}
Ok(())
}
///
/// Fills in history item metadata based on the execution result (notably duration and exit code)
///
fn fill_in_result_related_history_metadata(
s: &str,
engine_state: &EngineState,
cmd_duration: Duration,
stack: &mut Stack,
line_editor: &mut Reedline,
) -> Result<()> {
if !s.is_empty() && line_editor.has_last_command_context() {
line_editor
.update_last_command_context(&|mut c| {
c.duration = Some(cmd_duration);
c.exit_status = stack
.get_env_var(engine_state, "LAST_EXIT_CODE")
.and_then(|e| e.as_i64().ok());
c
})
.into_diagnostic()?; // todo: don't stop repl if error here?
}
Ok(())
}
/// The kinds of operations you can do in a single loop iteration of the REPL
enum ReplOperation {
/// "auto-cd": change directory by typing it in directly
AutoCd {
/// the current working directory
cwd: String,
/// the target
target: PathBuf,
/// span information for debugging
span: Span,
},
/// run a command
RunCommand(String),
/// do nothing (usually through an empty string)
DoNothing,
}
///
/// Parses one "REPL line" of input, to try and derive intent.
/// Notably, this is where we detect whether the user is attempting an
/// "auto-cd" (writing a relative path directly instead of `cd path`)
///
/// Returns the ReplOperation we believe the user wants to do
///
fn parse_operation(
s: String,
engine_state: &EngineState,
stack: &Stack,
) -> Result<ReplOperation, ErrReport> {
let tokens = lex(s.as_bytes(), 0, &[], &[], false);
// Check if this is a single call to a directory, if so auto-cd
let cwd = nu_engine::env::current_dir_str(engine_state, stack)?;
let mut orig = s.clone();
if orig.starts_with('`') {
orig = trim_quotes_str(&orig).to_string()
}
let path = nu_path::expand_path_with(&orig, &cwd);
if looks_like_path(&orig) && path.is_dir() && tokens.0.len() == 1 {
Ok(ReplOperation::AutoCd {
cwd,
target: path,
span: tokens.0[0].span,
})
} else if !s.trim().is_empty() {
Ok(ReplOperation::RunCommand(s))
} else {
Ok(ReplOperation::DoNothing)
}
}
///
/// Execute an "auto-cd" operation, changing the current working directory.
///
fn do_auto_cd(
path: PathBuf,
cwd: String,
stack: &mut Stack,
engine_state: &mut EngineState,
span: Span,
) {
let path = {
if !path.exists() {
report_error_new(
engine_state,
&ShellError::DirectoryNotFound {
dir: path.to_string_lossy().to_string(),
span,
},
);
}
let path = nu_path::canonicalize_with(path, &cwd)
.expect("internal error: cannot canonicalize known path");
path.to_string_lossy().to_string()
};
stack.add_env_var("OLDPWD".into(), Value::string(cwd.clone(), Span::unknown()));
//FIXME: this only changes the current scope, but instead this environment variable
//should probably be a block that loads the information from the state in the overlay
stack.add_env_var("PWD".into(), Value::string(path.clone(), Span::unknown()));
let cwd = Value::string(cwd, span);
let shells = stack.get_env_var(engine_state, "NUSHELL_SHELLS");
let mut shells = if let Some(v) = shells {
v.as_list()
.map(|x| x.to_vec())
.unwrap_or_else(|_| vec![cwd])
} else {
vec![cwd]
};
let current_shell = stack.get_env_var(engine_state, "NUSHELL_CURRENT_SHELL");
let current_shell = if let Some(v) = current_shell {
v.as_int().unwrap_or_default() as usize
} else {
0
};
let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL");
let last_shell = if let Some(v) = last_shell {
v.as_int().unwrap_or_default() as usize
} else {
0
};
shells[current_shell] = Value::string(path, span);
stack.add_env_var("NUSHELL_SHELLS".into(), Value::list(shells, span));
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::int(last_shell as i64, span),
);
}
///
/// Run a command as received from reedline. This is where we are actually
/// running a thing!
///
fn do_run_cmd(
s: &str,
stack: &mut Stack,
engine_state: &mut EngineState,
// we pass in the line editor so it can be dropped in the case of a process exit
// (in the normal case we don't want to drop it so return it as-is otherwise)
line_editor: Reedline,
shell_integration: bool,
entry_num: usize,
) -> Result<Reedline> {
trace!("eval source: {}", s);
let mut cmds = s.split_whitespace();
if let Some("exit") = cmds.next() {
let mut working_set = StateWorkingSet::new(engine_state);
let _ = parse(&mut working_set, None, s.as_bytes(), false);
if working_set.parse_errors.is_empty() {
match cmds.next() {
Some(s) => {
if let Ok(n) = s.parse::<i32>() {
drop(line_editor);
std::process::exit(n);
}
}
None => {
drop(line_editor);
std::process::exit(0);
}
}
}
}
if shell_integration {
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
let path = cwd.as_string()?;
// Try to abbreviate string for windows title
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
path.replace(&p.as_path().display().to_string(), "~")
} else {
path
};
let binary_name = s.split_whitespace().next();
if let Some(binary_name) = binary_name {
run_ansi_sequence(&format!("\x1b]2;{maybe_abbrev_path}> {binary_name}\x07"))?;
}
}
}
eval_source(
engine_state,
stack,
s.as_bytes(),
&format!("entry #{entry_num}"),
PipelineData::empty(),
false,
);
Ok(line_editor)
}
///
/// Output some things and set environment variables so shells with the right integration
/// can have more information about what is going on (after we have run a command)
///
fn do_shell_integration_finalize_command(
hostname: Option<String>,
engine_state: &EngineState,
stack: &mut Stack,
) -> Result<()> {
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
let path = cwd.as_string()?;
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
// https://code.visualstudio.com/docs/terminal/shell-integration#_supported-escape-sequences
if stack.get_env_var(engine_state, "TERM_PROGRAM") == Some(Value::test_string("vscode")) {
// If we're in vscode, run their specific ansi escape sequence.
// This is helpful for ctrl+g to change directories in the terminal.
run_ansi_sequence(&format!("\x1b]633;P;Cwd={}\x1b\\", path))?;
} else {
// Otherwise, communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
run_ansi_sequence(&format!(
"\x1b]7;file://{}{}{}\x1b\\",
percent_encoding::utf8_percent_encode(
&hostname.unwrap_or_else(|| "localhost".to_string()),
percent_encoding::CONTROLS
),
if path.starts_with('/') { "" } else { "/" },
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
))?;
}
// Try to abbreviate string for windows title
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
path.replace(&p.as_path().display().to_string(), "~")
} else {
path
};
// Set window title too
// https://tldp.org/HOWTO/Xterm-Title-3.html
// ESC]0;stringBEL -- Set icon name and window title to string
// ESC]1;stringBEL -- Set icon name to string
// ESC]2;stringBEL -- Set window title to string
run_ansi_sequence(&format!("\x1b]2;{maybe_abbrev_path}\x07"))?;
}
run_ansi_sequence(RESET_APPLICATION_MODE)?;
Ok(())
}
///
/// Clear the screen and output anything remaining in the EngineState buffer.
///
fn flush_engine_state_repl_buffer(engine_state: &mut EngineState, line_editor: &mut Reedline) {
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
line_editor.run_edit_commands(&[
EditCommand::Clear,
EditCommand::InsertString(repl.buffer.to_string()),
EditCommand::MoveToPosition {
position: repl.cursor_pos,
select: false,
},
]);
repl.buffer = "".to_string();
repl.cursor_pos = 0;
}
///
/// Setup history management for Reedline
///
fn setup_history(
nushell_path: &str,
engine_state: &mut EngineState,
line_editor: Reedline,
history: HistoryConfig,
) -> Result<Reedline> {
// Setup history_isolation aka "history per session"
let history_session_id = if history.isolation {
Reedline::create_history_session_id()
} else {
None
};
if let Some(path) = crate::config_files::get_history_path(nushell_path, history.file_format) {
return update_line_editor_history(
engine_state,
path,
history,
line_editor,
history_session_id,
);
};
Ok(line_editor)
}
///
/// Setup Reedline keybindingds based on the provided config
///
fn setup_keybindings(engine_state: &EngineState, line_editor: Reedline) -> Reedline {
return match create_keybindings(engine_state.get_config()) {
Ok(keybindings) => match keybindings {
KeybindingsMode::Emacs(keybindings) => {
let edit_mode = Box::new(Emacs::new(keybindings));
line_editor.with_edit_mode(edit_mode)
}
KeybindingsMode::Vi {
insert_keybindings,
normal_keybindings,
} => {
let edit_mode = Box::new(Vi::new(insert_keybindings, normal_keybindings));
line_editor.with_edit_mode(edit_mode)
}
},
Err(e) => {
report_error_new(engine_state, &e);
line_editor
}
};
}
///
/// Make sure that the terminal supports the kitty protocol if the config is asking for it
///
fn kitty_protocol_healthcheck(engine_state: &EngineState) {
if engine_state.get_config().use_kitty_protocol && !reedline::kitty_protocol_available() {
warn!("Terminal doesn't support use_kitty_protocol config");
}
}
fn store_history_id_in_engine(engine_state: &mut EngineState, line_editor: &Reedline) {
let session_id = line_editor
.get_history_session_id()
@ -695,18 +875,15 @@ fn store_history_id_in_engine(engine_state: &mut EngineState, line_editor: &Reed
fn update_line_editor_history(
engine_state: &mut EngineState,
history_path: &Path,
history_path: PathBuf,
history: HistoryConfig,
line_editor: Reedline,
history_session_id: Option<HistorySessionId>,
) -> Result<Reedline, ErrReport> {
let config = engine_state.get_config();
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
let history: Box<dyn reedline::History> = match history.file_format {
HistoryFileFormat::PlainText => Box::new(
FileBackedHistory::with_file(
config.max_history_size as usize,
history_path.to_path_buf(),
)
.into_diagnostic()?,
FileBackedHistory::with_file(history.max_size as usize, history_path)
.into_diagnostic()?,
),
HistoryFileFormat::Sqlite => Box::new(
SqliteBackedHistory::with_file(
@ -727,6 +904,18 @@ fn update_line_editor_history(
Ok(line_editor)
}
fn confirm_stdin_is_terminal() -> Result<()> {
// Guard against invocation without a connected terminal.
// reedline / crossterm event polling will fail without a connected tty
if !std::io::stdin().is_terminal() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Nushell launched as a REPL, but STDIN is not a TTY; either launch in a valid terminal or provide arguments to invoke a script!",
))
.into_diagnostic();
}
Ok(())
}
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorStyle> {
match shape {
NuCursorShape::Block => Some(SetCursorStyle::SteadyBlock),
@ -814,14 +1003,18 @@ fn trailing_slash_looks_like_path() {
#[test]
fn are_session_ids_in_sync() {
let engine_state = &mut EngineState::new();
let history_path_o =
crate::config_files::get_history_path("nushell", engine_state.config.history_file_format);
assert!(history_path_o.is_some());
let history_path = history_path_o.as_deref().unwrap();
let history = engine_state.history_config().unwrap();
let history_path =
crate::config_files::get_history_path("nushell", history.file_format).unwrap();
let line_editor = reedline::Reedline::create();
let history_session_id = reedline::Reedline::create_history_session_id();
let line_editor =
update_line_editor_history(engine_state, history_path, line_editor, history_session_id);
let line_editor = update_line_editor_history(
engine_state,
history_path,
history,
line_editor,
history_session_id,
);
assert_eq!(
i64::from(line_editor.unwrap().get_history_session_id().unwrap()),
engine_state.history_session_id

View File

@ -1,15 +1,17 @@
use log::trace;
use nu_ansi_term::Style;
use nu_color_config::{get_matching_brackets_style, get_shape_color};
use nu_engine::env;
use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::ast::{Argument, Block, Expr, Expression, PipelineElement, RecordItem};
use nu_protocol::engine::{EngineState, StateWorkingSet};
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
use nu_protocol::{Config, Span};
use reedline::{Highlighter, StyledText};
use std::sync::Arc;
pub struct NuHighlighter {
pub engine_state: Arc<EngineState>,
pub stack: Arc<Stack>,
pub config: Config,
}
@ -32,7 +34,17 @@ impl Highlighter for NuHighlighter {
working_set.get_span_contents(Span::new(span.start, span.end));
let str_word = String::from_utf8_lossy(str_contents).to_string();
if which::which(str_word).ok().is_some() {
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
let res = if let Ok(cwd) =
env::current_dir_str(&self.engine_state, &self.stack)
{
which::which_in(str_word, paths.as_ref(), cwd).ok()
} else {
which::which_in_global(str_word, paths.as_ref())
.ok()
.and_then(|mut i| i.next())
};
if res.is_some() {
*shape = FlatShape::ExternalResolved;
}
}
@ -321,9 +333,9 @@ fn find_matching_block_end_in_expr(
Expr::Keyword(..) => None,
Expr::ValueWithUnit(..) => None,
Expr::DateTime(_) => None,
Expr::Filepath(_) => None,
Expr::Directory(_) => None,
Expr::GlobPattern(_) => None,
Expr::Filepath(_, _) => None,
Expr::Directory(_, _) => None,
Expr::GlobPattern(_, _) => None,
Expr::String(_) => None,
Expr::CellPath(_) => None,
Expr::ImportPattern(_) => None,

View File

@ -66,7 +66,7 @@ fn custom_completer() -> NuCompleter {
// Add record value as example
let record = r#"
let external_completer = {|spans|
let external_completer = {|spans|
$spans
}
@ -684,13 +684,14 @@ fn variables_completions() {
// Test completions for $nu
let suggestions = completer.complete("$nu.", 4);
assert_eq!(14, suggestions.len());
assert_eq!(15, suggestions.len());
let expected: Vec<String> = vec![
"config-path".into(),
"current-exe".into(),
"default-config-dir".into(),
"env-path".into(),
"history-enabled".into(),
"history-path".into(),
"home-path".into(),
"is-interactive".into(),
@ -709,9 +710,13 @@ fn variables_completions() {
// Test completions for $nu.h (filter)
let suggestions = completer.complete("$nu.h", 5);
assert_eq!(2, suggestions.len());
assert_eq!(3, suggestions.len());
let expected: Vec<String> = vec!["history-path".into(), "home-path".into()];
let expected: Vec<String> = vec![
"history-enabled".into(),
"history-path".into(),
"home-path".into(),
];
// Match results
match_suggestions(expected, suggestions);

View File

@ -5,21 +5,21 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.88.2"
version = "0.90.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.88.2" }
nu-glob = { path = "../nu-glob", version = "0.88.2" }
nu-parser = { path = "../nu-parser", version = "0.88.2" }
nu-path = { path = "../nu-path", version = "0.88.2" }
nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
nu-utils = { path = "../nu-utils", version = "0.88.2" }
nu-engine = { path = "../nu-engine", version = "0.90.0" }
nu-glob = { path = "../nu-glob", version = "0.90.0" }
nu-parser = { path = "../nu-parser", version = "0.90.0" }
nu-path = { path = "../nu-path", version = "0.90.0" }
nu-protocol = { path = "../nu-protocol", version = "0.90.0" }
nu-utils = { path = "../nu-utils", version = "0.90.0" }
indexmap = "2.1"
miette = "5.10.0"
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.88.2" }
nu-test-support = { path = "../nu-test-support", version = "0.90.0" }
rstest = "0.18.2"

View File

@ -1,205 +0,0 @@
// utilities for expanding globs in command arguments
use nu_glob::{glob_with_parent, MatchOptions, Paths};
use nu_protocol::{ShellError, Spanned};
use std::fs;
use std::path::{Path, PathBuf};
// standard glob options to use for filesystem command arguments
const GLOB_PARAMS: MatchOptions = MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
// handle an argument that could be a literal path or a glob.
// if literal path, return just that (whether user can access it or not).
// if glob, expand into matching paths, using GLOB_PARAMS options.
pub fn arg_glob(
pattern: &Spanned<String>, // alleged path or glob
cwd: &Path, // current working directory
) -> Result<Paths, ShellError> {
arg_glob_opt(pattern, cwd, GLOB_PARAMS)
}
// variant of [arg_glob] that requires literal dot prefix in pattern to match dot-prefixed path.
pub fn arg_glob_leading_dot(pattern: &Spanned<String>, cwd: &Path) -> Result<Paths, ShellError> {
arg_glob_opt(
pattern,
cwd,
MatchOptions {
require_literal_leading_dot: true,
..GLOB_PARAMS
},
)
}
fn arg_glob_opt(
pattern: &Spanned<String>,
cwd: &Path,
options: MatchOptions,
) -> Result<Paths, ShellError> {
// remove ansi coloring (?)
let pattern = {
Spanned {
item: nu_utils::strip_ansi_string_unlikely(pattern.item.clone()),
span: pattern.span,
}
};
// if there's a file with same path as the pattern, just return that.
let pp = cwd.join(&pattern.item);
let md = fs::metadata(pp);
#[allow(clippy::single_match)]
match md {
Ok(_metadata) => {
return Ok(Paths::single(&PathBuf::from(pattern.item), cwd));
}
// file not found, but also "invalid chars in file" (e.g * on Windows). Fall through and glob
Err(_) => {}
}
// user wasn't referring to a specific thing in filesystem, try to glob it.
match glob_with_parent(&pattern.item, options, cwd) {
Ok(p) => Ok(p),
Err(pat_err) => Err(ShellError::InvalidGlobPattern {
msg: pat_err.msg.into(),
span: pattern.span,
}),
}
}
#[cfg(test)]
mod test {
use super::*;
use nu_glob::GlobResult;
use nu_protocol::{Span, Spanned};
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::playground::Playground;
use rstest::rstest;
fn spanned_string(str: &str) -> Spanned<String> {
Spanned {
item: str.to_string(),
span: Span::test_data(),
}
}
#[test]
fn does_something() {
let act = arg_glob(&spanned_string("*"), &PathBuf::from("."));
assert!(act.is_ok());
for f in act.expect("checked ok") {
match f {
Ok(p) => {
assert!(!p.to_str().unwrap().is_empty());
}
Err(e) => panic!("unexpected error {:?}", e),
};
}
}
#[test]
fn glob_format_error() {
let act = arg_glob(&spanned_string(r#"ab]c[def"#), &PathBuf::from("."));
assert!(act.is_err());
}
#[rstest]
#[case("*", 4, "no dirs")]
#[case("**/*", 7, "incl dirs")]
fn glob_subdirs(#[case] pat: &str, #[case] exp_count: usize, #[case] case: &str) {
Playground::setup("glob_subdirs", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("yehuda.txt"),
EmptyFile("jttxt"),
EmptyFile("andres.txt"),
]);
sandbox.mkdir(".children");
sandbox.within(".children").with_files(vec![
EmptyFile("timothy.txt"),
EmptyFile("tiffany.txt"),
EmptyFile("trish.txt"),
]);
let p: Vec<GlobResult> = arg_glob(&spanned_string(pat), &dirs.test)
.expect("no error")
.collect();
assert_eq!(
exp_count,
p.iter().filter(|i| i.is_ok()).count(),
" case: {case} ",
);
// expected behavior -- that directories are included in results (if name matches pattern)
let t = p
.iter()
.any(|i| i.as_ref().unwrap().to_string_lossy().contains(".children"));
assert!(t, "check for dir, case {case}");
})
}
#[rstest]
#[case("yehuda.txt", true, 1, "matches literal path")]
#[case("*", false, 3, "matches glob")]
#[case(r#"bad[glob.foo"#, true, 1, "matches literal, would be bad glob pat")]
fn exact_vs_glob(
#[case] pat: &str,
#[case] exp_matches_input: bool,
#[case] exp_count: usize,
#[case] case: &str,
) {
Playground::setup("exact_vs_glob", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("yehuda.txt"),
EmptyFile("jttxt"),
EmptyFile("bad[glob.foo"),
]);
let res = arg_glob(&spanned_string(pat), &dirs.test)
.expect("no error")
.collect::<Vec<GlobResult>>();
eprintln!("res: {:?}", res);
if exp_matches_input {
assert_eq!(
exp_count,
res.len(),
" case {case}: matches input, but count not 1? "
);
assert_eq!(
&res[0].as_ref().unwrap().to_string_lossy(),
pat, // todo: is it OK for glob to return relative paths (not to current cwd, but to arg cwd of arg_glob)?
);
} else {
assert_eq!(exp_count, res.len(), " case: {}: matched glob", case);
}
})
}
#[rstest]
#[case(r#"realbad[glob.foo"#, true, 1, "error, bad glob")]
fn exact_vs_bad_glob(
// if path doesn't exist but pattern is not valid glob, should get error.
#[case] pat: &str,
#[case] _exp_matches_input: bool,
#[case] _exp_count: usize,
#[case] _tag: &str,
) {
Playground::setup("exact_vs_bad_glob", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("yehuda.txt"),
EmptyFile("jttxt"),
EmptyFile("bad[glob.foo"),
]);
let res = arg_glob(&spanned_string(pat), &dirs.test);
assert!(res
.expect_err("expected error")
.to_string()
.contains("Invalid glob pattern"));
})
}
}

View File

@ -1,7 +1,4 @@
mod arg_glob;
pub mod formats;
pub mod hook;
pub mod input_handler;
pub mod util;
pub use arg_glob::arg_glob;
pub use arg_glob::arg_glob_leading_dot;

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-dataframe"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
version = "0.88.2"
version = "0.90.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,9 +13,9 @@ version = "0.88.2"
bench = false
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.88.2" }
nu-parser = { path = "../nu-parser", version = "0.88.2" }
nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
nu-engine = { path = "../nu-engine", version = "0.90.0" }
nu-parser = { path = "../nu-parser", version = "0.90.0" }
nu-protocol = { path = "../nu-protocol", version = "0.90.0" }
# Potential dependencies for extras
chrono = { version = "0.4", features = ["std", "unstable-locales"], default-features = false }
@ -24,11 +24,12 @@ fancy-regex = "0.12"
indexmap = { version = "2.1" }
num = { version = "0.4", optional = true }
serde = { version = "1.0", features = ["derive"] }
sqlparser = { version = "0.39", optional = true }
polars-io = { version = "0.35", features = ["avro"], optional = true }
polars-arrow = { version = "0.35", optional = true }
polars-ops = { version = "0.35", optional = true }
polars-plan = { version = "0.35", optional = true }
sqlparser = { version = "0.43", optional = true }
polars-io = { version = "0.36", features = ["avro"], optional = true }
polars-arrow = { version = "0.36", optional = true }
polars-ops = { version = "0.36", optional = true }
polars-plan = { version = "0.36", optional = true }
polars-utils = { version = "0.36", optional = true }
[dependencies.polars]
features = [
@ -62,12 +63,12 @@ features = [
"to_dummies",
]
optional = true
version = "0.35"
version = "0.36"
[features]
dataframe = ["num", "polars", "polars-io", "polars-arrow", "polars-ops", "polars-plan", "sqlparser"]
dataframe = ["num", "polars", "polars-io", "polars-arrow", "polars-ops", "polars-plan", "polars-utils", "sqlparser"]
default = []
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.88.2" }
nu-test-support = { path = "../nu-test-support", version = "0.88.2" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.90.0" }
nu-test-support = { path = "../nu-test-support", version = "0.90.0" }

View File

@ -37,24 +37,27 @@ impl Command for AppendDF {
example: r#"let a = ([[a b]; [1 2] [3 4]] | dfr into-df);
$a | dfr append $a"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"a_x".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b_x".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"a_x".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b_x".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -64,26 +67,29 @@ impl Command for AppendDF {
example: r#"let a = ([[a b]; [1 2] [3 4]] | dfr into-df);
$a | dfr append $a --col"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![
Value::test_int(1),
Value::test_int(3),
Value::test_int(1),
Value::test_int(3),
],
),
Column::new(
"b".to_string(),
vec![
Value::test_int(2),
Value::test_int(4),
Value::test_int(2),
Value::test_int(4),
],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![
Value::test_int(1),
Value::test_int(3),
Value::test_int(1),
Value::test_int(3),
],
),
Column::new(
"b".to_string(),
vec![
Value::test_int(2),
Value::test_int(4),
Value::test_int(2),
Value::test_int(4),
],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -110,7 +116,7 @@ fn command(
) -> Result<PipelineData, ShellError> {
let other: Value = call.req(engine_state, stack, 0)?;
let axis = if call.has_flag("col") {
let axis = if call.has_flag(engine_state, stack, "col")? {
Axis::Column
} else {
Axis::Row

View File

@ -35,10 +35,13 @@ impl Command for DropDF {
description: "drop column a",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr drop a",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -46,16 +46,19 @@ impl Command for DropDuplicates {
description: "drop duplicates",
example: "[[a b]; [1 2] [3 4] [1 2]] | dfr into-df | dfr drop-duplicates",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(3), Value::test_int(1)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(4), Value::test_int(2)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(3), Value::test_int(1)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(4), Value::test_int(2)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -92,7 +95,7 @@ fn command(
let subset_slice = subset.as_ref().map(|cols| &cols[..]);
let keep_strategy = if call.has_flag("last") {
let keep_strategy = if call.has_flag(engine_state, stack, "last")? {
UniqueKeepStrategy::Last
} else {
UniqueKeepStrategy::First

View File

@ -43,20 +43,23 @@ impl Command for DropNulls {
let a = ($df | dfr with-column $res --name res);
$a | dfr drop-nulls"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(1)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
),
Column::new(
"res".to_string(),
vec![Value::test_int(1), Value::test_int(1)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(1)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
),
Column::new(
"res".to_string(),
vec![Value::test_int(1), Value::test_int(1)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -66,15 +69,18 @@ impl Command for DropNulls {
example: r#"let s = ([1 2 0 0 3 4] | dfr into-df);
($s / $s) | dfr drop-nulls"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"div_0_0".to_string(),
vec![
Value::test_int(1),
Value::test_int(1),
Value::test_int(1),
Value::test_int(1),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"div_0_0".to_string(),
vec![
Value::test_int(1),
Value::test_int(1),
Value::test_int(1),
Value::test_int(1),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -31,16 +31,19 @@ impl Command for DataTypes {
description: "Dataframe dtypes",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr dtypes",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"column".to_string(),
vec![Value::test_string("a"), Value::test_string("b")],
),
Column::new(
"dtype".to_string(),
vec![Value::test_string("i64"), Value::test_string("i64")],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"column".to_string(),
vec![Value::test_string("a"), Value::test_string("b")],
),
Column::new(
"dtype".to_string(),
vec![Value::test_string("i64"), Value::test_string("i64")],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -79,6 +82,7 @@ fn command(
.dtype();
let dtype_str = dtype.to_string();
dtypes.push(Value::string(dtype_str, call.head));
Value::string(*v, call.head)
@ -88,7 +92,7 @@ fn command(
let names_col = Column::new("column".to_string(), names);
let dtypes_col = Column::new("dtype".to_string(), dtypes);
NuDataFrame::try_from_columns(vec![names_col, dtypes_col])
NuDataFrame::try_from_columns(vec![names_col, dtypes_col], None)
.map(|df| PipelineData::Value(df.into_value(call.head), None))
}

View File

@ -1,4 +1,5 @@
use super::super::values::NuDataFrame;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
@ -78,12 +79,12 @@ impl Command for Dummies {
}
fn command(
_engine_state: &EngineState,
_stack: &mut Stack,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let drop_first: bool = call.has_flag("drop-first");
let drop_first: bool = call.has_flag(engine_state, stack, "drop-first")?;
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
df.as_ref()

View File

@ -43,10 +43,13 @@ impl Command for FilterWith {
example: r#"let mask = ([true false] | dfr into-df);
[[a b]; [1 2] [3 4]] | dfr into-df | dfr filter-with $mask"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
Column::new("b".to_string(), vec![Value::test_int(2)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
Column::new("b".to_string(), vec![Value::test_int(2)]),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -55,10 +58,13 @@ impl Command for FilterWith {
description: "Filter dataframe using an expression",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr filter-with ((dfr col a) > 1)",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(3)]),
Column::new("b".to_string(), vec![Value::test_int(4)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(3)]),
Column::new("b".to_string(), vec![Value::test_int(4)]),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -44,10 +44,13 @@ impl Command for FirstDF {
description: "Return the first row of a dataframe",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr first",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
Column::new("b".to_string(), vec![Value::test_int(2)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
Column::new("b".to_string(), vec![Value::test_int(2)]),
],
None,
)
.expect("should not fail")
.into_value(Span::test_data()),
),
@ -56,16 +59,19 @@ impl Command for FirstDF {
description: "Return the first two rows of a dataframe",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr first 2",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
],
None,
)
.expect("should not fail")
.into_value(Span::test_data()),
),

View File

@ -36,10 +36,13 @@ impl Command for GetDF {
description: "Returns the selected column",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr get a",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -40,10 +40,13 @@ impl Command for LastDF {
description: "Create new dataframe with last rows",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr last 1",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(3)]),
Column::new("b".to_string(), vec![Value::test_int(4)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(3)]),
Column::new("b".to_string(), vec![Value::test_int(4)]),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -106,7 +106,7 @@ impl Command for MeltDF {
Value::test_string("c"),
],
),
])
], None)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -15,6 +15,7 @@ mod open;
mod query_df;
mod rename;
mod sample;
mod schema;
mod shape;
mod slice;
mod sql_context;
@ -49,10 +50,10 @@ pub use melt::MeltDF;
pub use query_df::QueryDf;
pub use rename::RenameDF;
pub use sample::SampleDF;
pub use schema::SchemaDF;
pub use shape::ShapeDF;
pub use slice::SliceDF;
pub use sql_context::SQLContext;
pub use sql_expr::parse_sql_expr;
pub use summary::Summary;
pub use take::TakeDF;
pub use to_arrow::ToArrow;
@ -94,6 +95,7 @@ pub fn add_eager_decls(working_set: &mut StateWorkingSet) {
QueryDf,
RenameDF,
SampleDF,
SchemaDF,
ShapeDF,
SliceDF,
TakeDF,

View File

@ -1,3 +1,5 @@
use crate::dataframe::values::NuSchema;
use super::super::values::{NuDataFrame, NuLazyFrame};
use nu_engine::CallExt;
use nu_protocol::{
@ -70,6 +72,12 @@ impl Command for OpenDataFrame {
"Columns to be selected from csv file. CSV and Parquet file",
None,
)
.named(
"schema",
SyntaxShape::Record(vec![]),
r#"Polars Schema in format [{name: str}]. CSV, JSON, and JSONL files"#,
Some('s')
)
.input_output_type(Type::Any, Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
@ -139,7 +147,7 @@ fn from_parquet(
stack: &mut Stack,
call: &Call,
) -> Result<Value, ShellError> {
if call.has_flag("lazy") {
if call.has_flag(engine_state, stack, "lazy")? {
let file: String = call.req(engine_state, stack, 0)?;
let args = ScanArgsParquet {
n_rows: None,
@ -238,7 +246,7 @@ fn from_ipc(
stack: &mut Stack,
call: &Call,
) -> Result<Value, ShellError> {
if call.has_flag("lazy") {
if call.has_flag(engine_state, stack, "lazy")? {
let file: String = call.req(engine_state, stack, 0)?;
let args = ScanArgsIpc {
n_rows: None,
@ -305,10 +313,19 @@ fn from_json(
help: None,
inner: vec![],
})?;
let maybe_schema = call
.get_flag(engine_state, stack, "schema")?
.map(|schema| NuSchema::try_from(&schema))
.transpose()?;
let buf_reader = BufReader::new(file);
let reader = JsonReader::new(buf_reader);
let reader = match maybe_schema {
Some(schema) => reader.with_schema(schema.into()),
None => reader,
};
let df: NuDataFrame = reader
.finish()
.map_err(|e| ShellError::GenericError {
@ -329,6 +346,10 @@ fn from_jsonl(
call: &Call,
) -> Result<Value, ShellError> {
let infer_schema: Option<usize> = call.get_flag(engine_state, stack, "infer-schema")?;
let maybe_schema = call
.get_flag(engine_state, stack, "schema")?
.map(|schema| NuSchema::try_from(&schema))
.transpose()?;
let file: Spanned<PathBuf> = call.req(engine_state, stack, 0)?;
let file = File::open(&file.item).map_err(|e| ShellError::GenericError {
error: "Error opening file".into(),
@ -343,6 +364,11 @@ fn from_jsonl(
.with_json_format(JsonFormat::JsonLines)
.infer_schema_len(infer_schema);
let reader = match maybe_schema {
Some(schema) => reader.with_schema(schema.into()),
None => reader,
};
let df: NuDataFrame = reader
.finish()
.map_err(|e| ShellError::GenericError {
@ -363,12 +389,17 @@ fn from_csv(
call: &Call,
) -> Result<Value, ShellError> {
let delimiter: Option<Spanned<String>> = call.get_flag(engine_state, stack, "delimiter")?;
let no_header: bool = call.has_flag("no-header");
let no_header: bool = call.has_flag(engine_state, stack, "no-header")?;
let infer_schema: Option<usize> = call.get_flag(engine_state, stack, "infer-schema")?;
let skip_rows: Option<usize> = call.get_flag(engine_state, stack, "skip-rows")?;
let columns: Option<Vec<String>> = call.get_flag(engine_state, stack, "columns")?;
if call.has_flag("lazy") {
let maybe_schema = call
.get_flag(engine_state, stack, "schema")?
.map(|schema| NuSchema::try_from(&schema))
.transpose()?;
if call.has_flag(engine_state, stack, "lazy")? {
let file: String = call.req(engine_state, stack, 0)?;
let csv_reader = LazyCsvReader::new(file);
@ -395,6 +426,11 @@ fn from_csv(
let csv_reader = csv_reader.has_header(!no_header);
let csv_reader = match maybe_schema {
Some(schema) => csv_reader.with_schema(Some(schema.into())),
None => csv_reader,
};
let csv_reader = match infer_schema {
None => csv_reader,
Some(r) => csv_reader.with_infer_schema_length(Some(r)),
@ -452,6 +488,11 @@ fn from_csv(
let csv_reader = csv_reader.has_header(!no_header);
let csv_reader = match maybe_schema {
Some(schema) => csv_reader.with_schema(Some(schema.into())),
None => csv_reader,
};
let csv_reader = match infer_schema {
None => csv_reader,
Some(r) => csv_reader.infer_schema(Some(r)),

View File

@ -44,10 +44,13 @@ impl Command for QueryDf {
description: "Query dataframe using SQL",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr query 'select a from df'",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -46,15 +46,18 @@ impl Command for RenameDF {
description: "Renames a series",
example: "[5 6 7 8] | dfr into-df | dfr rename '0' new_name",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"new_name".to_string(),
vec![
Value::test_int(5),
Value::test_int(6),
Value::test_int(7),
Value::test_int(8),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"new_name".to_string(),
vec![
Value::test_int(5),
Value::test_int(6),
Value::test_int(7),
Value::test_int(8),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -63,16 +66,19 @@ impl Command for RenameDF {
description: "Renames a dataframe column",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr rename a a_new",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a_new".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a_new".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -81,16 +87,19 @@ impl Command for RenameDF {
description: "Renames two dataframe columns",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr rename [a b] [a_new b_new]",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a_new".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b_new".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a_new".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b_new".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -88,8 +88,8 @@ fn command(
let seed: Option<u64> = call
.get_flag::<i64>(engine_state, stack, "seed")?
.map(|val| val as u64);
let replace: bool = call.has_flag("replace");
let shuffle: bool = call.has_flag("shuffle");
let replace: bool = call.has_flag(engine_state, stack, "replace")?;
let shuffle: bool = call.has_flag(engine_state, stack, "shuffle")?;
let df = NuDataFrame::try_from_pipeline(input, call.head)?;

View File

@ -0,0 +1,119 @@
use super::super::values::NuDataFrame;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
};
#[derive(Clone)]
pub struct SchemaDF;
impl Command for SchemaDF {
fn name(&self) -> &str {
"dfr schema"
}
fn usage(&self) -> &str {
"Show schema for a dataframe."
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.switch("datatype-list", "creates a lazy dataframe", Some('l'))
.input_output_type(
Type::Custom("dataframe".into()),
Type::Custom("dataframe".into()),
)
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Dataframe schema",
example: r#"[[a b]; [1 "foo"] [3 "bar"]] | dfr into-df | dfr schema"#,
result: Some(Value::record(
Record::from_raw_cols_vals_unchecked(
vec!["a".to_string(), "b".to_string()],
vec![
Value::string("i64", Span::test_data()),
Value::string("str", Span::test_data()),
],
),
Span::test_data(),
)),
}]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
if call.has_flag(engine_state, stack, "datatype-list")? {
Ok(PipelineData::Value(datatype_list(Span::unknown()), None))
} else {
command(engine_state, stack, call, input)
}
}
}
fn command(
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
let schema = df.schema();
let value: Value = schema.into();
Ok(PipelineData::Value(value, None))
}
fn datatype_list(span: Span) -> Value {
let types: Vec<Value> = [
("null", ""),
("bool", ""),
("u8", ""),
("u16", ""),
("u32", ""),
("u64", ""),
("i8", ""),
("i16", ""),
("i32", ""),
("i64", ""),
("f32", ""),
("f64", ""),
("str", ""),
("binary", ""),
("date", ""),
("datetime<time_unit: (ms, us, ns) timezone (optional)>", "Time Unit can be: milliseconds: ms, microseconds: us, nanoseconds: ns. Timezone wildcard is *. Other Timezone examples: UTC, America/Los_Angeles."),
("duration<time_unit: (ms, us, ns)>", "Time Unit can be: milliseconds: ms, microseconds: us, nanoseconds: ns."),
("time", ""),
("object", ""),
("unknown", ""),
("list<dtype>", ""),
]
.iter()
.map(|(dtype, note)| {
Value::record(Record::from_raw_cols_vals_unchecked(
vec!["dtype".to_string(), "note".to_string()],
vec![Value::string(*dtype, span), Value::string(*note, span)],
),span)
})
.collect();
Value::list(types, span)
}
#[cfg(test)]
mod test {
use super::super::super::test_dataframe::test_dataframe;
use super::*;
#[test]
fn test_examples() {
test_dataframe(vec![Box::new(SchemaDF {})])
}
}

View File

@ -34,10 +34,13 @@ impl Command for ShapeDF {
description: "Shows row and column shape",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr shape",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("rows".to_string(), vec![Value::test_int(2)]),
Column::new("columns".to_string(), vec![Value::test_int(2)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("rows".to_string(), vec![Value::test_int(2)]),
Column::new("columns".to_string(), vec![Value::test_int(2)]),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -70,7 +73,7 @@ fn command(
let rows_col = Column::new("rows".to_string(), vec![rows]);
let cols_col = Column::new("columns".to_string(), vec![cols]);
NuDataFrame::try_from_columns(vec![rows_col, cols_col])
NuDataFrame::try_from_columns(vec![rows_col, cols_col], None)
.map(|df| PipelineData::Value(df.into_value(call.head), None))
}

View File

@ -37,10 +37,13 @@ impl Command for SliceDF {
description: "Create new dataframe from a slice of the rows",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr slice 0 1",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
Column::new("b".to_string(), vec![Value::test_int(2)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
Column::new("b".to_string(), vec![Value::test_int(2)]),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -13,7 +13,7 @@ fn map_sql_polars_datatype(data_type: &SQLDataType) -> Result<DataType> {
| SQLDataType::Uuid
| SQLDataType::Clob(_)
| SQLDataType::Text
| SQLDataType::String(_) => DataType::Utf8,
| SQLDataType::String(_) => DataType::String,
SQLDataType::Float(_) => DataType::Float32,
SQLDataType::Real => DataType::Float32,
SQLDataType::Double => DataType::Float64,
@ -62,7 +62,9 @@ fn binary_op_(left: Expr, right: Expr, op: &SQLBinaryOperator) -> Result<Expr> {
SQLBinaryOperator::Multiply => left * right,
SQLBinaryOperator::Divide => left / right,
SQLBinaryOperator::Modulo => left % right,
SQLBinaryOperator::StringConcat => left.cast(DataType::Utf8) + right.cast(DataType::Utf8),
SQLBinaryOperator::StringConcat => {
left.cast(DataType::String) + right.cast(DataType::String)
}
SQLBinaryOperator::Gt => left.gt(right),
SQLBinaryOperator::Lt => left.lt(right),
SQLBinaryOperator::GtEq => left.gt_eq(right),

View File

@ -10,7 +10,7 @@ use polars::{
chunked_array::ChunkedArray,
prelude::{
AnyValue, DataFrame, DataType, Float64Type, IntoSeries, NewChunkedArray,
QuantileInterpolOptions, Series, Utf8Type,
QuantileInterpolOptions, Series, StringType,
},
};
@ -46,53 +46,56 @@ impl Command for Summary {
description: "list dataframe descriptives",
example: "[[a b]; [1 1] [1 1]] | dfr into-df | dfr summary",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"descriptor".to_string(),
vec![
Value::test_string("count"),
Value::test_string("sum"),
Value::test_string("mean"),
Value::test_string("median"),
Value::test_string("std"),
Value::test_string("min"),
Value::test_string("25%"),
Value::test_string("50%"),
Value::test_string("75%"),
Value::test_string("max"),
],
),
Column::new(
"a (i64)".to_string(),
vec![
Value::test_float(2.0),
Value::test_float(2.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(0.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
],
),
Column::new(
"b (i64)".to_string(),
vec![
Value::test_float(2.0),
Value::test_float(2.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(0.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"descriptor".to_string(),
vec![
Value::test_string("count"),
Value::test_string("sum"),
Value::test_string("mean"),
Value::test_string("median"),
Value::test_string("std"),
Value::test_string("min"),
Value::test_string("25%"),
Value::test_string("50%"),
Value::test_string("75%"),
Value::test_string("max"),
],
),
Column::new(
"a (i64)".to_string(),
vec![
Value::test_float(2.0),
Value::test_float(2.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(0.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
],
),
Column::new(
"b (i64)".to_string(),
vec![
Value::test_float(2.0),
Value::test_float(2.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(0.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
Value::test_float(1.0),
],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -171,7 +174,7 @@ fn command(
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
let names = ChunkedArray::<Utf8Type>::from_slice_options("descriptor", &labels).into_series();
let names = ChunkedArray::<StringType>::from_slice_options("descriptor", &labels).into_series();
let head = std::iter::once(names);
@ -179,42 +182,50 @@ fn command(
.as_ref()
.get_columns()
.iter()
.filter(|col| col.dtype() != &DataType::Object("object"))
.filter(|col| !matches!(col.dtype(), &DataType::Object("object", _)))
.map(|col| {
let count = col.len() as f64;
let sum = col
.sum_as_series()
.cast(&DataType::Float64)
.ok()
.and_then(|ca| match ca.get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
});
let sum = col.sum_as_series().ok().and_then(|series| {
series
.cast(&DataType::Float64)
.ok()
.and_then(|ca| match ca.get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
})
});
let mean = match col.mean_as_series().get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
};
let median = match col.median_as_series().get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
};
let std = match col.std_as_series(0).get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
};
let min = col
.min_as_series()
.cast(&DataType::Float64)
.ok()
.and_then(|ca| match ca.get(0) {
let median = match col.median_as_series() {
Ok(v) => match v.get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
});
},
_ => None,
};
let std = match col.std_as_series(0) {
Ok(v) => match v.get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
},
_ => None,
};
let min = col.min_as_series().ok().and_then(|series| {
series
.cast(&DataType::Float64)
.ok()
.and_then(|ca| match ca.get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
})
});
let mut quantiles = quantiles
.clone()
@ -230,14 +241,15 @@ fn command(
})
.collect::<Vec<Option<f64>>>();
let max = col
.max_as_series()
.cast(&DataType::Float64)
.ok()
.and_then(|ca| match ca.get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
});
let max = col.max_as_series().ok().and_then(|series| {
series
.cast(&DataType::Float64)
.ok()
.and_then(|ca| match ca.get(0) {
Ok(AnyValue::Float64(v)) => Some(v),
_ => None,
})
});
let mut descriptors = vec![Some(count), sum, mean, median, std, min];
descriptors.append(&mut quantiles);

View File

@ -44,16 +44,19 @@ impl Command for TakeDF {
let indices = ([0 2] | dfr into-df);
$df | dfr take $indices"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(4), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(4), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -64,10 +67,13 @@ impl Command for TakeDF {
let indices = ([0 2] | dfr into-df);
$series | dfr take $indices"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(4), Value::test_int(5)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(4), Value::test_int(5)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -70,7 +70,7 @@ fn command(
) -> Result<PipelineData, ShellError> {
let file_name: Spanned<PathBuf> = call.req(engine_state, stack, 0)?;
let delimiter: Option<Spanned<String>> = call.get_flag(engine_state, stack, "delimiter")?;
let no_header: bool = call.has_flag("no-header");
let no_header: bool = call.has_flag(engine_state, stack, "no-header")?;
let mut df = NuDataFrame::try_from_pipeline(input, call.head)?;

View File

@ -1,10 +1,14 @@
use crate::dataframe::values::NuSchema;
use super::super::values::{Column, NuDataFrame};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
use polars::prelude::*;
#[derive(Clone)]
pub struct ToDataFrame;
@ -20,6 +24,12 @@ impl Command for ToDataFrame {
fn signature(&self) -> Signature {
Signature::build(self.name())
.named(
"schema",
SyntaxShape::Record(vec![]),
r#"Polars Schema in format [{name: str}]. CSV, JSON, and JSONL files"#,
Some('s'),
)
.input_output_type(Type::Any, Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
@ -30,16 +40,19 @@ impl Command for ToDataFrame {
description: "Takes a dictionary and creates a dataframe",
example: "[[a b];[1 2] [3 4]] | dfr into-df",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -48,24 +61,27 @@ impl Command for ToDataFrame {
description: "Takes a list of tables and creates a dataframe",
example: "[[1 2 a] [3 4 b] [5 6 c]] | dfr into-df",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"0".to_string(),
vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
),
Column::new(
"1".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(6)],
),
Column::new(
"2".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"0".to_string(),
vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
),
Column::new(
"1".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(6)],
),
Column::new(
"2".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -74,14 +90,17 @@ impl Command for ToDataFrame {
description: "Takes a list and creates a dataframe",
example: "[a b c] | dfr into-df",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -90,14 +109,41 @@ impl Command for ToDataFrame {
description: "Takes a list of booleans and creates a dataframe",
example: "[true true false] | dfr into-df",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![
Value::test_bool(true),
Value::test_bool(true),
Value::test_bool(false),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![
Value::test_bool(true),
Value::test_bool(true),
Value::test_bool(false),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
},
Example {
description: "Convert to a dataframe and provide a schema",
example: "{a: 1, b: {a: [1 2 3]}, c: [a b c]}| dfr into-df -s {a: u8, b: {a: list<u64>}, c: list<str>}",
result: Some(
NuDataFrame::try_from_series(vec![
Series::new("a", &[1u8]),
{
let dtype = DataType::Struct(vec![Field::new("a", DataType::List(Box::new(DataType::UInt64)))]);
let vals = vec![AnyValue::StructOwned(
Box::new((vec![AnyValue::List(Series::new("a", &[1u64, 2, 3]))], vec![Field::new("a", DataType::String)]))); 1];
Series::from_any_values_and_dtype("b", &vals, &dtype, false)
.expect("Struct series should not fail")
},
{
let dtype = DataType::List(Box::new(DataType::String));
let vals = vec![AnyValue::List(Series::new("c", &["a", "b", "c"]))];
Series::from_any_values_and_dtype("c", &vals, &dtype, false)
.expect("List series should not fail")
}
], Span::test_data())
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -107,12 +153,17 @@ impl Command for ToDataFrame {
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
NuDataFrame::try_from_iter(input.into_iter())
let maybe_schema = call
.get_flag(engine_state, stack, "schema")?
.map(|schema| NuSchema::try_from(&schema))
.transpose()?;
NuDataFrame::try_from_iter(input.into_iter(), maybe_schema)
.map(|df| PipelineData::Value(NuDataFrame::into_value(df, call.head), None))
}
}

View File

@ -100,7 +100,7 @@ fn dataframe_command(
input: Value,
) -> Result<PipelineData, ShellError> {
let rows: Option<usize> = call.get_flag(engine_state, stack, "rows")?;
let tail: bool = call.has_flag("tail");
let tail: bool = call.has_flag(engine_state, stack, "tail")?;
let df = NuDataFrame::try_from_value(input)?;

View File

@ -42,20 +42,23 @@ impl Command for WithColumn {
| dfr into-df
| dfr with-column ([5 6] | dfr into-df) --name c"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(5), Value::test_int(6)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(5), Value::test_int(6)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -70,24 +73,27 @@ impl Command for WithColumn {
]
| dfr collect"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(2), Value::test_int(6)],
),
Column::new(
"d".to_string(),
vec![Value::test_int(3), Value::test_int(9)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(2), Value::test_int(6)],
),
Column::new(
"d".to_string(),
vec![Value::test_int(3), Value::test_int(9)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -32,10 +32,13 @@ impl Command for ExprArgWhere {
example: "let df = ([[a b]; [one 1] [two 2] [three 3]] | dfr into-df);
$df | dfr select (dfr arg-where ((dfr col b) >= 2) | dfr as b_arg)",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"b_arg".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"b_arg".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -41,27 +41,30 @@ impl Command for ExprConcatStr {
example: r#"let df = ([[a b c]; [one two 1] [three four 2]] | dfr into-df);
$df | dfr with-column ((dfr concat-str "-" [(dfr col a) (dfr col b) ((dfr col c) * 2)]) | dfr as concat)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("three")],
),
Column::new(
"b".to_string(),
vec![Value::test_string("two"), Value::test_string("four")],
),
Column::new(
"c".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"concat".to_string(),
vec![
Value::test_string("one-two-2"),
Value::test_string("three-four-4"),
],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("three")],
),
Column::new(
"b".to_string(),
vec![Value::test_string("two"), Value::test_string("four")],
),
Column::new(
"c".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"concat".to_string(),
vec![
Value::test_string("one-two-2"),
Value::test_string("three-four-4"),
],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -9,6 +9,11 @@ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
Value,
};
use polars::{
datatypes::{DataType, TimeUnit},
prelude::NamedFrom,
series::Series,
};
#[derive(Clone)]
pub struct ExprDatePart;
@ -47,10 +52,13 @@ impl Command for ExprDatePart {
description: "Creates an expression to capture the year date part",
example: r#"[["2021-12-30T01:02:03.123456789"]] | dfr into-df | dfr as-datetime "%Y-%m-%dT%H:%M:%S.%9f" | dfr with-column [(dfr col datetime | dfr datepart year | dfr as datetime_year )]"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("datetime".to_string(), vec![Value::test_date(dt)]),
Column::new("datetime_year".to_string(), vec![Value::test_int(2021)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("datetime".to_string(), vec![Value::test_date(dt)]),
Column::new("datetime_year".to_string(), vec![Value::test_int(2021)]),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -66,16 +74,21 @@ impl Command for ExprDatePart {
(dfr col datetime | dfr datepart second | dfr as datetime_second ),
(dfr col datetime | dfr datepart nanosecond | dfr as datetime_ns ) ]"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("datetime".to_string(), vec![Value::test_date(dt)]),
Column::new("datetime_year".to_string(), vec![Value::test_int(2021)]),
Column::new("datetime_month".to_string(), vec![Value::test_int(12)]),
Column::new("datetime_day".to_string(), vec![Value::test_int(30)]),
Column::new("datetime_hour".to_string(), vec![Value::test_int(1)]),
Column::new("datetime_minute".to_string(), vec![Value::test_int(2)]),
Column::new("datetime_second".to_string(), vec![Value::test_int(3)]),
Column::new("datetime_ns".to_string(), vec![Value::test_int(123456789)]),
])
NuDataFrame::try_from_series(
vec![
Series::new("datetime", &[dt.timestamp_nanos_opt()])
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
.expect("Error casting to datetime type"),
Series::new("datetime_year", &[2021_i64]), // i32 was coerced to i64
Series::new("datetime_month", &[12_i8]),
Series::new("datetime_day", &[30_i8]),
Series::new("datetime_hour", &[1_i8]),
Series::new("datetime_minute", &[2_i8]),
Series::new("datetime_second", &[3_i8]),
Series::new("datetime_ns", &[123456789_i64]), // i32 was coerced to i64
],
Span::test_data(),
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -179,7 +179,18 @@ macro_rules! lazy_expr_command {
let value = input.into_value(call.head);
if NuDataFrame::can_downcast(&value) {
let lazy = NuLazyFrame::try_from_value(value)?;
let lazy = NuLazyFrame::new(lazy.from_eager, lazy.into_polars().$func());
let lazy = NuLazyFrame::new(
lazy.from_eager,
lazy.into_polars()
.$func()
.map_err(|e| ShellError::GenericError {
error: "Dataframe Error".into(),
msg: e.to_string(),
help: None,
span: None,
inner: vec![],
})?,
);
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
} else {
@ -267,7 +278,18 @@ macro_rules! lazy_expr_command {
let value = input.into_value(call.head);
if NuDataFrame::can_downcast(&value) {
let lazy = NuLazyFrame::try_from_value(value)?;
let lazy = NuLazyFrame::new(lazy.from_eager, lazy.into_polars().$func($ddof));
let lazy = NuLazyFrame::new(
lazy.from_eager,
lazy.into_polars()
.$func($ddof)
.map_err(|e| ShellError::GenericError {
error: "Dataframe Error".into(),
msg: e.to_string(),
help: None,
span: None,
inner: vec![],
})?,
);
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
} else {
@ -385,10 +407,13 @@ lazy_expr_command!(
description: "Max value from columns in a dataframe",
example: "[[a b]; [6 2] [1 4] [4 1]] | dfr into-df | dfr max",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(6)],),
Column::new("b".to_string(), vec![Value::test_int(4)],),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(6)],),
Column::new("b".to_string(), vec![Value::test_int(4)],),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -400,16 +425,19 @@ lazy_expr_command!(
| dfr group-by a
| dfr agg (dfr col b | dfr max)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_int(4), Value::test_int(1)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_int(4), Value::test_int(1)],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -430,10 +458,13 @@ lazy_expr_command!(
description: "Min value from columns in a dataframe",
example: "[[a b]; [6 2] [1 4] [4 1]] | dfr into-df | dfr min",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(1)],),
Column::new("b".to_string(), vec![Value::test_int(1)],),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(1)],),
Column::new("b".to_string(), vec![Value::test_int(1)],),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -445,16 +476,19 @@ lazy_expr_command!(
| dfr group-by a
| dfr agg (dfr col b | dfr min)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(1)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(1)],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -475,10 +509,13 @@ lazy_expr_command!(
description: "Sums all columns in a dataframe",
example: "[[a b]; [6 2] [1 4] [4 1]] | dfr into-df | dfr sum",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(11)],),
Column::new("b".to_string(), vec![Value::test_int(7)],),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_int(11)],),
Column::new("b".to_string(), vec![Value::test_int(7)],),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -490,16 +527,19 @@ lazy_expr_command!(
| dfr group-by a
| dfr agg (dfr col b | dfr sum)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_int(6), Value::test_int(1)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_int(6), Value::test_int(1)],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -520,10 +560,13 @@ lazy_expr_command!(
description: "Mean value from columns in a dataframe",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr mean",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)],),
Column::new("b".to_string(), vec![Value::test_float(2.0)],),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)],),
Column::new("b".to_string(), vec![Value::test_float(2.0)],),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -535,16 +578,19 @@ lazy_expr_command!(
| dfr group-by a
| dfr agg (dfr col b | dfr mean)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(3.0), Value::test_float(1.0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(3.0), Value::test_float(1.0)],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -567,16 +613,19 @@ expr_command!(
| dfr group-by a
| dfr agg (dfr col b | dfr median)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(3.0), Value::test_float(1.0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(3.0), Value::test_float(1.0)],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -596,10 +645,13 @@ lazy_expr_command!(
description: "Std value from columns in a dataframe",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr std",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_float(2.0)],),
Column::new("b".to_string(), vec![Value::test_float(0.0)],),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_float(2.0)],),
Column::new("b".to_string(), vec![Value::test_float(0.0)],),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -611,16 +663,19 @@ lazy_expr_command!(
| dfr group-by a
| dfr agg (dfr col b | dfr std)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(0.0), Value::test_float(0.0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(0.0), Value::test_float(0.0)],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -643,10 +698,13 @@ lazy_expr_command!(
"Var value from columns in a dataframe or aggregates columns to their var value",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr var",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)],),
Column::new("b".to_string(), vec![Value::test_float(0.0)],),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)],),
Column::new("b".to_string(), vec![Value::test_float(0.0)],),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -658,16 +716,19 @@ lazy_expr_command!(
| dfr group-by a
| dfr agg (dfr col b | dfr var)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(0.0), Value::test_float(0.0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(0.0), Value::test_float(0.0)],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -39,28 +39,31 @@ impl Command for ExprIsIn {
example: r#"let df = ([[a b]; [one 1] [two 2] [three 3]] | dfr into-df);
$df | dfr with-column (dfr col a | dfr is-in [one two] | dfr as a_in)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![
Value::test_string("one"),
Value::test_string("two"),
Value::test_string("three"),
],
),
Column::new(
"b".to_string(),
vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
),
Column::new(
"a_in".to_string(),
vec![
Value::test_bool(true),
Value::test_bool(true),
Value::test_bool(false),
],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![
Value::test_string("one"),
Value::test_string("two"),
Value::test_string("three"),
],
),
Column::new(
"b".to_string(),
vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
),
Column::new(
"a_in".to_string(),
vec![
Value::test_bool(true),
Value::test_bool(true),
Value::test_bool(false),
],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -81,7 +84,8 @@ impl Command for ExprIsIn {
let list: Vec<Value> = call.req(engine_state, stack, 0)?;
let expr = NuExpression::try_from_pipeline(input, call.head)?;
let values = NuDataFrame::try_from_columns(vec![Column::new("list".to_string(), list)])?;
let values =
NuDataFrame::try_from_columns(vec![Column::new("list".to_string(), list)], None)?;
let list = values.as_series(call.head)?;
if matches!(list.dtype(), DataType::Object(..)) {

View File

@ -54,24 +54,27 @@ impl Command for ExprOtherwise {
)
| dfr collect"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(1), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(1)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(4), Value::test_int(5), Value::test_int(4)],
),
Column::new(
"d".to_string(),
vec![Value::test_int(10), Value::test_int(6), Value::test_int(0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(1), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(1)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(4), Value::test_int(5), Value::test_int(4)],
),
Column::new(
"d".to_string(),
vec![Value::test_int(10), Value::test_int(6), Value::test_int(0)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -41,16 +41,19 @@ impl Command for ExprQuantile {
| dfr group-by a
| dfr agg (dfr col b | dfr quantile 0.5)"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(4.0), Value::test_float(1.0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_string("one"), Value::test_string("two")],
),
Column::new(
"b".to_string(),
vec![Value::test_float(4.0), Value::test_float(1.0)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -62,24 +62,27 @@ impl Command for ExprWhen {
)
| dfr collect"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(1), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(1)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(4), Value::test_int(5), Value::test_int(4)],
),
Column::new(
"d".to_string(),
vec![Value::test_int(10), Value::test_int(6), Value::test_int(0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(1), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(1)],
),
Column::new(
"c".to_string(),
vec![Value::test_int(4), Value::test_int(5), Value::test_int(4)],
),
Column::new(
"d".to_string(),
vec![Value::test_int(10), Value::test_int(6), Value::test_int(0)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -47,24 +47,27 @@ impl Command for LazyAggregate {
(dfr col b | dfr sum | dfr as "b_sum")
]"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -81,24 +84,27 @@ impl Command for LazyAggregate {
]
| dfr collect"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -160,7 +166,7 @@ fn get_col_name(expr: &Expr) -> Option<String> {
| polars::prelude::AggExpr::Last(e)
| polars::prelude::AggExpr::Mean(e)
| polars::prelude::AggExpr::Implode(e)
| polars::prelude::AggExpr::Count(e)
| polars::prelude::AggExpr::Count(e, _)
| polars::prelude::AggExpr::Sum(e)
| polars::prelude::AggExpr::AggGroups(e)
| polars::prelude::AggExpr::Std(e, _)

View File

@ -33,16 +33,19 @@ impl Command for LazyCollect {
description: "drop duplicates",
example: "[[a b]; [1 2] [3 4]] | dfr into-lazy | dfr collect",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(3)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -69,7 +69,7 @@ impl Command for LazyExplode {
Value::test_string("Skiing"),
Value::test_string("Football"),
]),
]).expect("simple df for test should not fail")
], None).expect("simple df for test should not fail")
.into_value(Span::test_data()),
)
},
@ -86,7 +86,7 @@ impl Command for LazyExplode {
Value::test_string("Skiing"),
Value::test_string("Football"),
]),
]).expect("simple df for test should not fail")
], None).expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
},

View File

@ -38,16 +38,19 @@ impl Command for LazyFetch {
description: "Fetch a rows from the dataframe",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr fetch 2",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -38,16 +38,19 @@ impl Command for LazyFillNA {
description: "Fills the NaN values with 0",
example: "[1 2 NaN 3 NaN] | dfr into-df | dfr fill-nan 0",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(0),
Value::test_int(3),
Value::test_int(0),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(0),
Value::test_int(3),
Value::test_int(0),
],
)],
None,
)
.expect("Df for test should not fail")
.into_value(Span::test_data()),
),
@ -56,16 +59,19 @@ impl Command for LazyFillNA {
description: "Fills the NaN values of a whole dataframe",
example: "[[a b]; [0.2 1] [0.1 NaN]] | dfr into-df | dfr fill-nan 0",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_float(0.2), Value::test_float(0.1)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(1), Value::test_int(0)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_float(0.2), Value::test_float(0.1)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(1), Value::test_int(0)],
),
],
None,
)
.expect("Df for test should not fail")
.into_value(Span::test_data()),
),
@ -123,7 +129,7 @@ impl Command for LazyFillNA {
})
.collect::<Vec<Column>>();
Ok(PipelineData::Value(
NuDataFrame::try_from_columns(dataframe)?.into_value(call.head),
NuDataFrame::try_from_columns(dataframe, None)?.into_value(call.head),
None,
))
}

View File

@ -37,16 +37,19 @@ impl Command for LazyFillNull {
description: "Fills the null values by 0",
example: "[1 2 2 3 3] | dfr into-df | dfr shift 2 | dfr fill-null 0",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![
Value::test_int(0),
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(2),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![
Value::test_int(0),
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(2),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -38,16 +38,19 @@ impl Command for LazyFilter {
description: "Filter dataframe using an expression",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr filter ((dfr col a) >= 4)",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(4)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -71,7 +71,7 @@ Example {
Value::test_string("Skiing"),
Value::test_string("Football"),
]),
]).expect("simple df for test should not fail")
], None).expect("simple df for test should not fail")
.into_value(Span::test_data()),
)
},
@ -88,7 +88,7 @@ Example {
Value::test_string("Skiing"),
Value::test_string("Football"),
]),
]).expect("simple df for test should not fail")
], None).expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
},

View File

@ -46,24 +46,27 @@ impl Command for ToLazyGroupBy {
(dfr col b | dfr sum | dfr as "b_sum")
]"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -80,24 +83,27 @@ impl Command for ToLazyGroupBy {
]
| dfr collect"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(1), Value::test_int(2)],
),
Column::new(
"b_min".to_string(),
vec![Value::test_int(2), Value::test_int(4)],
),
Column::new(
"b_max".to_string(),
vec![Value::test_int(4), Value::test_int(6)],
),
Column::new(
"b_sum".to_string(),
vec![Value::test_int(6), Value::test_int(10)],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -53,53 +53,56 @@ impl Command for LazyJoin {
let df_b = ([["foo" "bar" "ham"];[1 "a" "let"] [2 "c" "var"] [3 "c" "const"]] | dfr into-lazy);
$df_a | dfr join $df_b a foo | dfr collect"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(1),
Value::test_int(1),
],
),
Column::new(
"b".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
Value::test_string("c"),
],
),
Column::new(
"c".to_string(),
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
],
),
Column::new(
"bar".to_string(),
vec![
Value::test_string("a"),
Value::test_string("c"),
Value::test_string("a"),
Value::test_string("a"),
],
),
Column::new(
"ham".to_string(),
vec![
Value::test_string("let"),
Value::test_string("var"),
Value::test_string("let"),
Value::test_string("let"),
],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(1),
Value::test_int(1),
],
),
Column::new(
"b".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
Value::test_string("c"),
],
),
Column::new(
"c".to_string(),
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
],
),
Column::new(
"bar".to_string(),
vec![
Value::test_string("a"),
Value::test_string("c"),
Value::test_string("a"),
Value::test_string("a"),
],
),
Column::new(
"ham".to_string(),
vec![
Value::test_string("let"),
Value::test_string("var"),
Value::test_string("let"),
Value::test_string("let"),
],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -110,53 +113,56 @@ impl Command for LazyJoin {
let df_b = ([["foo" "bar" "ham"];[1 "a" "let"] [2 "c" "var"] [3 "c" "const"]] | dfr into-lazy);
$df_a | dfr join $df_b a foo"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(1),
Value::test_int(1),
],
),
Column::new(
"b".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
Value::test_string("c"),
],
),
Column::new(
"c".to_string(),
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
],
),
Column::new(
"bar".to_string(),
vec![
Value::test_string("a"),
Value::test_string("c"),
Value::test_string("a"),
Value::test_string("a"),
],
),
Column::new(
"ham".to_string(),
vec![
Value::test_string("let"),
Value::test_string("var"),
Value::test_string("let"),
Value::test_string("let"),
],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(1),
Value::test_int(1),
],
),
Column::new(
"b".to_string(),
vec![
Value::test_string("a"),
Value::test_string("b"),
Value::test_string("c"),
Value::test_string("c"),
],
),
Column::new(
"c".to_string(),
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
],
),
Column::new(
"bar".to_string(),
vec![
Value::test_string("a"),
Value::test_string("c"),
Value::test_string("a"),
Value::test_string("a"),
],
),
Column::new(
"ham".to_string(),
vec![
Value::test_string("let"),
Value::test_string("var"),
Value::test_string("let"),
Value::test_string("let"),
],
),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -171,14 +177,14 @@ impl Command for LazyJoin {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let left = call.has_flag("left");
let outer = call.has_flag("outer");
let cross = call.has_flag("cross");
let left = call.has_flag(engine_state, stack, "left")?;
let outer = call.has_flag(engine_state, stack, "outer")?;
let cross = call.has_flag(engine_state, stack, "cross")?;
let how = if left {
JoinType::Left
} else if outer {
JoinType::Outer
JoinType::Outer { coalesce: true }
} else if cross {
JoinType::Cross
} else {

View File

@ -112,6 +112,70 @@ macro_rules! lazy_command {
}
}
};
($command: ident, $name: expr, $desc: expr, $examples: expr, $func: ident?, $test: ident) => {
#[derive(Clone)]
pub struct $command;
impl Command for $command {
fn name(&self) -> &str {
$name
}
fn usage(&self) -> &str {
$desc
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.input_output_type(
Type::Custom("dataframe".into()),
Type::Custom("dataframe".into()),
)
.category(Category::Custom("lazyframe".into()))
}
fn examples(&self) -> Vec<Example> {
$examples
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let lazy = NuLazyFrame::try_from_pipeline(input, call.head)?;
let lazy = NuLazyFrame::new(
lazy.from_eager,
lazy.into_polars()
.$func()
.map_err(|e| ShellError::GenericError {
error: "Dataframe Error".into(),
msg: e.to_string(),
help: None,
span: None,
inner: vec![],
})?,
);
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
}
}
#[cfg(test)]
mod $test {
use super::super::super::test_dataframe::test_dataframe;
use super::*;
#[test]
fn test_examples() {
test_dataframe(vec![Box::new($command {})])
}
}
};
}
// LazyReverse command
@ -124,16 +188,19 @@ lazy_command!(
description: "Reverses the dataframe.",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr reverse",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(6),],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2), Value::test_int(2),],
),
])
NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![Value::test_int(2), Value::test_int(4), Value::test_int(6),],
),
Column::new(
"b".to_string(),
vec![Value::test_int(2), Value::test_int(2), Value::test_int(2),],
),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -167,14 +234,17 @@ lazy_command!(
description: "Median value from columns in a dataframe",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr median",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)],),
Column::new("b".to_string(), vec![Value::test_float(2.0)],),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)],),
Column::new("b".to_string(), vec![Value::test_float(2.0)],),
],
None
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
},],
median,
median?,
test_median
);

View File

@ -38,10 +38,13 @@ impl Command for LazyQuantile {
description: "quantile value from columns in a dataframe",
example: "[[a b]; [6 2] [1 4] [4 1]] | dfr into-df | dfr quantile 0.5",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)]),
Column::new("b".to_string(), vec![Value::test_float(2.0)]),
])
NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![Value::test_float(4.0)]),
Column::new("b".to_string(), vec![Value::test_float(2.0)]),
],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -62,7 +65,14 @@ impl Command for LazyQuantile {
let lazy = NuLazyFrame::new(
lazy.from_eager,
lazy.into_polars()
.quantile(lit(quantile), QuantileInterpolOptions::default()),
.quantile(lit(quantile), QuantileInterpolOptions::default())
.map_err(|e| ShellError::GenericError {
error: "Dataframe Error".into(),
msg: e.to_string(),
help: None,
span: None,
inner: vec![],
})?,
);
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))

View File

@ -37,10 +37,13 @@ impl Command for LazySelect {
description: "Select a column from the dataframe",
example: "[[a b]; [6 2] [4 2] [2 2]] | dfr into-df | dfr select a",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(4), Value::test_int(2)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"a".to_string(),
vec![Value::test_int(6), Value::test_int(4), Value::test_int(2)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -60,7 +60,7 @@ impl Command for LazySortBy {
"b".to_string(),
vec![Value::test_int(4), Value::test_int(1), Value::test_int(2)],
),
])
], None)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -89,7 +89,7 @@ impl Command for LazySortBy {
Value::test_int(2),
],
),
])
], None)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -107,8 +107,8 @@ impl Command for LazySortBy {
let vals: Vec<Value> = call.rest(engine_state, stack, 0)?;
let value = Value::list(vals, call.head);
let expressions = NuExpression::extract_exprs(value)?;
let nulls_last = call.has_flag("nulls-last");
let maintain_order = call.has_flag("maintain-order");
let nulls_last = call.has_flag(engine_state, stack, "nulls-last")?;
let maintain_order = call.has_flag(engine_state, stack, "maintain-order")?;
let reverse: Option<Vec<bool>> = call.get_flag(engine_state, stack, "reverse")?;
let reverse = match reverse {

View File

@ -1,9 +1,12 @@
use crate::dataframe::values::NuSchema;
use super::super::values::{NuDataFrame, NuLazyFrame};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -20,6 +23,12 @@ impl Command for ToLazyFrame {
fn signature(&self) -> Signature {
Signature::build(self.name())
.named(
"schema",
SyntaxShape::Record(vec![]),
r#"Polars Schema in format [{name: str}]. CSV, JSON, and JSONL files"#,
Some('s'),
)
.input_output_type(Type::Any, Type::Custom("dataframe".into()))
.category(Category::Custom("lazyframe".into()))
}
@ -34,12 +43,17 @@ impl Command for ToLazyFrame {
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let df = NuDataFrame::try_from_iter(input.into_iter())?;
let maybe_schema = call
.get_flag(engine_state, stack, "schema")?
.map(|schema| NuSchema::try_from(&schema))
.transpose()?;
let df = NuDataFrame::try_from_iter(input.into_iter(), maybe_schema)?;
let lazy = NuLazyFrame::from_dataframe(df);
let value = Value::custom_value(Box::new(lazy), call.head);

View File

@ -33,10 +33,13 @@ impl Command for AllFalse {
description: "Returns true if all values are false",
example: "[false false false] | dfr into-df | dfr all-false",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"all_false".to_string(),
vec![Value::test_bool(true)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"all_false".to_string(),
vec![Value::test_bool(true)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -47,10 +50,13 @@ impl Command for AllFalse {
let res = ($s > 9);
$res | dfr all-false"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"all_false".to_string(),
vec![Value::test_bool(false)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"all_false".to_string(),
vec![Value::test_bool(false)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -88,8 +94,11 @@ fn command(
let value = Value::bool(!bool.any(), call.head);
NuDataFrame::try_from_columns(vec![Column::new("all_false".to_string(), vec![value])])
.map(|df| PipelineData::Value(NuDataFrame::into_value(df, call.head), None))
NuDataFrame::try_from_columns(
vec![Column::new("all_false".to_string(), vec![value])],
None,
)
.map(|df| PipelineData::Value(NuDataFrame::into_value(df, call.head), None))
}
#[cfg(test)]

View File

@ -33,10 +33,13 @@ impl Command for AllTrue {
description: "Returns true if all values are true",
example: "[true true true] | dfr into-df | dfr all-true",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"all_true".to_string(),
vec![Value::test_bool(true)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"all_true".to_string(),
vec![Value::test_bool(true)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -47,10 +50,13 @@ impl Command for AllTrue {
let res = ($s > 9);
$res | dfr all-true"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"all_true".to_string(),
vec![Value::test_bool(false)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"all_true".to_string(),
vec![Value::test_bool(false)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -88,7 +94,7 @@ fn command(
let value = Value::bool(bool.all(), call.head);
NuDataFrame::try_from_columns(vec![Column::new("all_true".to_string(), vec![value])])
NuDataFrame::try_from_columns(vec![Column::new("all_true".to_string(), vec![value])], None)
.map(|df| PipelineData::Value(NuDataFrame::into_value(df, call.head), None))
}

View File

@ -37,10 +37,10 @@ impl Command for ArgMax {
description: "Returns index for max value",
example: "[1 3 2] | dfr into-df | dfr arg-max",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"arg_max".to_string(),
vec![Value::test_int(1)],
)])
NuDataFrame::try_from_columns(
vec![Column::new("arg_max".to_string(), vec![Value::test_int(1)])],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -37,10 +37,10 @@ impl Command for ArgMin {
description: "Returns index for min value",
example: "[1 3 2] | dfr into-df | dfr arg-min",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"arg_min".to_string(),
vec![Value::test_int(0)],
)])
NuDataFrame::try_from_columns(
vec![Column::new("arg_min".to_string(), vec![Value::test_int(0)])],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -69,16 +69,19 @@ impl Command for Cumulative {
description: "Cumulative sum for a series",
example: "[1 2 3 4 5] | dfr into-df | dfr cumulative sum",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0_cumulative_sum".to_string(),
vec![
Value::test_int(1),
Value::test_int(3),
Value::test_int(6),
Value::test_int(10),
Value::test_int(15),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0_cumulative_sum".to_string(),
vec![
Value::test_int(1),
Value::test_int(3),
Value::test_int(6),
Value::test_int(10),
Value::test_int(15),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -103,12 +106,12 @@ fn command(
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cum_type: Spanned<String> = call.req(engine_state, stack, 0)?;
let reverse = call.has_flag("reverse");
let reverse = call.has_flag(engine_state, stack, "reverse")?;
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
let series = df.as_series(call.head)?;
if let DataType::Object(_) = series.dtype() {
if let DataType::Object(..) = series.dtype() {
return Err(ShellError::GenericError {
error: "Found object series".into(),
msg: "Series of type object cannot be used for cumulative operation".into(),

View File

@ -6,7 +6,7 @@ use nu_protocol::{
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type,
};
use polars::prelude::{IntoSeries, Utf8Methods};
use polars::prelude::{IntoSeries, StringMethods};
#[derive(Clone)]
pub struct AsDate;
@ -64,11 +64,11 @@ fn command(
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let format: String = call.req(engine_state, stack, 0)?;
let not_exact = call.has_flag("not-exact");
let not_exact = call.has_flag(engine_state, stack, "not-exact")?;
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
let series = df.as_series(call.head)?;
let casted = series.utf8().map_err(|e| ShellError::GenericError {
let casted = series.str().map_err(|e| ShellError::GenericError {
error: "Error casting to string".into(),
msg: e.to_string(),
span: Some(call.head),

View File

@ -7,7 +7,7 @@ use nu_protocol::{
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
use polars::prelude::{IntoSeries, TimeUnit, Utf8Methods};
use polars::prelude::{IntoSeries, StringMethods, TimeUnit};
#[derive(Clone)]
pub struct AsDateTime;
@ -53,27 +53,30 @@ impl Command for AsDateTime {
description: "Converts string to datetime",
example: r#"["2021-12-30 00:00:00" "2021-12-31 00:00:00"] | dfr into-df | dfr as-datetime "%Y-%m-%d %H:%M:%S""#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"datetime".to_string(),
vec![
Value::date(
DateTime::parse_from_str(
"2021-12-30 00:00:00 +0000",
"%Y-%m-%d %H:%M:%S %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
Value::date(
DateTime::parse_from_str(
"2021-12-31 00:00:00 +0000",
"%Y-%m-%d %H:%M:%S %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"datetime".to_string(),
vec![
Value::date(
DateTime::parse_from_str(
"2021-12-30 00:00:00 +0000",
"%Y-%m-%d %H:%M:%S %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
Value::date(
DateTime::parse_from_str(
"2021-12-31 00:00:00 +0000",
"%Y-%m-%d %H:%M:%S %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -82,27 +85,30 @@ impl Command for AsDateTime {
description: "Converts string to datetime with high resolutions",
example: r#"["2021-12-30 00:00:00.123456789" "2021-12-31 00:00:00.123456789"] | dfr into-df | dfr as-datetime "%Y-%m-%d %H:%M:%S.%9f""#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"datetime".to_string(),
vec![
Value::date(
DateTime::parse_from_str(
"2021-12-30 00:00:00.123456789 +0000",
"%Y-%m-%d %H:%M:%S.%9f %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
Value::date(
DateTime::parse_from_str(
"2021-12-31 00:00:00.123456789 +0000",
"%Y-%m-%d %H:%M:%S.%9f %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"datetime".to_string(),
vec![
Value::date(
DateTime::parse_from_str(
"2021-12-30 00:00:00.123456789 +0000",
"%Y-%m-%d %H:%M:%S.%9f %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
Value::date(
DateTime::parse_from_str(
"2021-12-31 00:00:00.123456789 +0000",
"%Y-%m-%d %H:%M:%S.%9f %z",
)
.expect("date calculation should not fail in test"),
Span::test_data(),
),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -128,11 +134,11 @@ fn command(
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let format: String = call.req(engine_state, stack, 0)?;
let not_exact = call.has_flag("not-exact");
let not_exact = call.has_flag(engine_state, stack, "not-exact")?;
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
let series = df.as_series(call.head)?;
let casted = series.utf8().map_err(|e| ShellError::GenericError {
let casted = series.str().map_err(|e| ShellError::GenericError {
error: "Error casting to string".into(),
msg: e.to_string(),
span: Some(call.head),

View File

@ -35,10 +35,13 @@ impl Command for GetDay {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-day"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(4), Value::test_int(4)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(4), Value::test_int(4)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetHour {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-hour"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(16), Value::test_int(16)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(16), Value::test_int(16)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetMinute {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-minute"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(39), Value::test_int(39)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(39), Value::test_int(39)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetMonth {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-month"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(8), Value::test_int(8)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(8), Value::test_int(8)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetNanosecond {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-nanosecond"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(0), Value::test_int(0)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(0), Value::test_int(0)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetOrdinal {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-ordinal"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(217), Value::test_int(217)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(217), Value::test_int(217)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetSecond {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-second"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(18), Value::test_int(18)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(18), Value::test_int(18)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetWeek {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-week"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(32), Value::test_int(32)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(32), Value::test_int(32)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetWeekDay {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-weekday"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(2), Value::test_int(2)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -35,10 +35,13 @@ impl Command for GetYear {
let df = ([$dt $dt] | dfr into-df);
$df | dfr get-year"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![Value::test_int(2020), Value::test_int(2020)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![Value::test_int(2020), Value::test_int(2020)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -1,5 +1,6 @@
use super::super::super::values::{Column, NuDataFrame};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
@ -45,16 +46,19 @@ impl Command for ArgSort {
description: "Returns indexes for a sorted series",
example: "[1 2 2 3 3] | dfr into-df | dfr arg-sort",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"arg_sort".to_string(),
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"arg_sort".to_string(),
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -63,16 +67,19 @@ impl Command for ArgSort {
description: "Returns indexes for a sorted series",
example: "[1 2 2 3 3] | dfr into-df | dfr arg-sort --reverse",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"arg_sort".to_string(),
vec![
Value::test_int(3),
Value::test_int(4),
Value::test_int(1),
Value::test_int(2),
Value::test_int(0),
],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"arg_sort".to_string(),
vec![
Value::test_int(3),
Value::test_int(4),
Value::test_int(1),
Value::test_int(2),
Value::test_int(0),
],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -92,18 +99,18 @@ impl Command for ArgSort {
}
fn command(
_engine_state: &EngineState,
_stack: &mut Stack,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
let sort_options = SortOptions {
descending: call.has_flag("reverse"),
nulls_last: call.has_flag("nulls-last"),
descending: call.has_flag(engine_state, stack, "reverse")?,
nulls_last: call.has_flag(engine_state, stack, "nulls-last")?,
multithreaded: true,
maintain_order: call.has_flag("maintain-order"),
maintain_order: call.has_flag(engine_state, stack, "maintain-order")?,
};
let mut res = df

View File

@ -37,10 +37,13 @@ impl Command for ArgTrue {
description: "Returns indexes where values are true",
example: "[false true false] | dfr into-df | dfr arg-true",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"arg_true".to_string(),
vec![Value::test_int(1)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"arg_true".to_string(),
vec![Value::test_int(1)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -37,10 +37,13 @@ impl Command for ArgUnique {
description: "Returns indexes for unique values",
example: "[1 2 2 3 3] | dfr into-df | dfr arg-unique",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"arg_unique".to_string(),
vec![Value::test_int(0), Value::test_int(1), Value::test_int(3)],
)])
NuDataFrame::try_from_columns(
vec![Column::new(
"arg_unique".to_string(),
vec![Value::test_int(0), Value::test_int(1), Value::test_int(3)],
)],
None,
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

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