Compare commits

..

197 Commits

Author SHA1 Message Date
JT
8fa965118c Update release.yml 2022-11-30 06:35:16 +13:00
JT
9c800bcb2c bump to 0.72 (#7272)
# Description

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-29 20:17:23 +13:00
ea4d8c5f49 update release-pkg.nu to include more recent less version (#7265)
# Description

This PR upgrades the Windows less version from v590 to v608

# User-Facing Changes


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-29 15:21:00 +13:00
5d2abdd1c3 add a more verbose description of operators (#7263)
# Description

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

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


# User-Facing Changes

Adds description column

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-29 15:20:17 +13:00
7d5333db3b Clean up .sh scripts with shellcheck (#7261)
Ran [`shellcheck`](https://github.com/koalaman/shellcheck) on our 3
shell scripts and fixed the warnings. This caught that the scripts were
[broken because of their
shebang](https://www.shellcheck.net/wiki/SC3030):

```
〉./uninstall-all.sh

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

```
〉shellcheck *.sh

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


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


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

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


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


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


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

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


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


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


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

Did you mean:
    cargo uninstall "$plugin"

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

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

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

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

# User-Facing Changes



# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-27 21:03:17 +13:00
c4d2b787aa Add did-you-mean suggestions for operators (#7251)
# Description

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


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


# Tests + Formatting

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

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



# User-Facing Changes

N/A

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-27 09:26:54 +13:00
9850fbd77d chore: chrono_update (#7132)
chrono version update

# Description

upgrade chrono to 0.4.23

# Major Changes

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

# Tests + Formatting

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

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

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

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

# After Submitting

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

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

Operator precedence

`and` > `xor` > `or`

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

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

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



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

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


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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

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

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

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

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

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


# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-26 20:16:39 +13:00
3f960012cd Revert "remove zstd warning message" (#7235)
This reverts commit ee81030600.


# Description

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-24 20:01:22 -08:00
0b094e2bf2 remove zstd warning message (#7232)
# Description

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

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

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

a slight modification of this PR

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-24 18:39:32 -08:00
2388e1e80b Reorder export-env eval and allow reloading an overlay (#7231)
# Description

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-24 23:45:24 +01:00
JT
62e34b69b3 New commands: break, continue, return, and loop (#7230)
# Description

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

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

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

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

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

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

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

# User-Facing Changes

Adds the above commands.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-25 09:39:16 +13:00
fd68767216 pin to a version of zstd that doesn't break dataframe compilation (#7227)
# Description

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

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-25 08:43:23 +13:00
JT
93202d4529 Remove And and Or pipeline elements (#7229)
# Description

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

# User-Facing Changes

Nothing user facing. These were not yet implemented.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-25 07:06:12 +13:00
ed1f0eb231 Make catch block a closure w/ access to error (#7228)
A small follow-up to #7221. This changes the `catch` block from a block
to a closure, so that it can access the error returned from the `try`
block. This helps with a common scenario: "the `try` block failed, and I
want to log why it failed."

### Example


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

### Future Work

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

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

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

# User-Facing Changes

This adds the `try` command.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-24 17:52:11 +13:00
JT
8cca447e8c A set of fixes for stderr redirect (#7219)
# Description

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

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-24 16:58:15 +13:00
651e86a3c0 uniq -i does not convert to lowercase (#7192) (#7209)
# Description
`uniq -i` does not convert output strings to lowercase.

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

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


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

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

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

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

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

# User-Facing Changes

None

# Tests + Formatting

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

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

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

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

Fixes #7201.

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

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

Closes #6803.

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 08:43:12 -08:00
f46c45343a uniq code refactoring (#7188)
# Description

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

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

# Tests + Formatting

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

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

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

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

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

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

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

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

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

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

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

```

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

# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 19:22:23 +13:00
cf96677c78 Error on negative argument of first (#7186)
Fixes a two's complement underflow/overflow when given a negative arg.

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

Same behavior as #7184


# Tests + Formatting

+ 1 failure test

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 17:04:28 +13:00
ce03d8eb12 Error on negative argument to last (#7184)
# Description

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

Follow-up for #7178

# User-Facing Changes

Breaking change:

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

Note:
In #7136 we are considering supporting negative indexing

# Tests + Formatting

+ 1 failure test

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 17:04:04 +13:00
21dedef7f6 remove block input support in merge (#7177)
# Description

Closes: #6937

# User-Facing Changes

N/A

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 17:01:27 +13:00
da7f77867a Fixed json parsing (#7175)
# Description

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

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

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

# User-Facing Changes

It should give fewer errors.

# Tests + Formatting

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

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

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

# After Submitting

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

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


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

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

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

---

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

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

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

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-23 16:57:55 +13:00
0c38729735 Apply clippy fix (#7193)
# Description

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

# User-Facing Changes

N/A

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 16:57:27 +13:00
a0b3a48e8b Fix mv error message issues (arrows, Windows paths) (#7197)
# Description

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

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

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

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

```
# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 16:55:13 +13:00
b662c2eb96 Make external command substitution works friendly(like fish shell, trailing ending newlines) (#7156)
# Description

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

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

Fixes: #6816
Fixes: #3980


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

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

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

After:
<img width="505" alt="img"
src="https://user-images.githubusercontent.com/22256154/202468599-7b537488-3d6b-458e-9d75-d85780826db0.png">


# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 16:51:57 +13:00
JT
8cda641350 Don't redirect stdout when only redirecting stderr (#7206)
# Description

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

# User-Facing Changes

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

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-11-23 15:18:34 +13:00
efdfeac55e Feature cleanup (#7182)
Following up on #7180 with some feature cleanup:

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

cc @WindSoilder @fdncred

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

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

Note that the `-i` short flag is not affected.
 
# User-Facing Changes

See above.

# Tests + Formatting

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

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

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

# After Submitting

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

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

Aside from that pipeline metadata wasn't passed aroud.

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

* Make other formats require string

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

* `IntoPipelineData` cleanup

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

* tell user that one command support operated on cell paths

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

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

* simplify code

* don't show signatures for parser keyword

* update comment

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

* fix string when no positional args

* update signature body

* update

* add help signature test

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

* fix clippy

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

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

*added username, password, and fragment

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

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

* Tweaked test slightly

* Fix merge conflict(?)

* Remove recently-added test case

* Revert rm.always_trash default

* Untweak rm help messages

* Formatting

* Remove example

* Add deprecation warning

* Remove deprecation timeline

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

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

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

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

* Clippy

* Add missing quote

* Remove two unnecessary characters

* Add short datetime tests

* Make regex simplificatified

* Alphabetise 'use' statements

* Improve perf by putting case-insensitive cases in regex

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

* Add LiteElement to represent different pipeline elem types

* Add PipelineElement to Pipelines

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

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

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

* Improve fix

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

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

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

* Fix width issue

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

* Remove debug println!

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

* Update logic

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

* Improve the logic via a wrapping

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

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

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

* fix CI

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

* Fixing tests

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

* Fix coloring issues

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

* Don't expand when can

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

* Fix tests

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

* Change the logic

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

* Fix cargo fmt

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

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

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

* Add test

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

* Fix `first` behaviour for unexpected input types

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

This commit changes the schema: instead of

command.signature: table

we now have

command.signatures: list<table>

with one signature for every input-output type pair.

* Represent signatures as a map from input_type to signature

* Sort signature entries

* Drop command name from signature tables

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

* Bug fix: was creating records with repeated keys

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

* Adds back multithreading feature to sysinfo.

* Adds back alloc for percent-encoding

* Adds updated lock file.

* Missed one sysinfo.

* `indexmap` just defaults

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

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

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

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

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

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

  × Capture of mutable variable.

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

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

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

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

* Tests mostly working

* finish last fixes, passes all tests

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

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

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

tests

format

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

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

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

* fix Value::List match

* fix invalid rows

* add tests

* fix ListStream match

* add one more test for ListStream

* add more tests

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

* Start defining subtype relation

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

- Enforce them in tests

* Declare input and output types of commands

* Add formatted signatures to `help commands` table

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

* Revert unnecessary derive(Hash) on SyntaxShape

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

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

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

* fix test case

* simplify code

* reverse back for first command

* fix example

* make arg required

* add test

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

* Refactor pull request template

* Reword PR template a bit

* Update CONTRIBUTING.md

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

* Reformulate

* Make "Before Submitting" a top-level header

* Add review requirement to After Submitting

* Reformulate

* Update .github/pull_request_template.md

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

* Reformulate contributing guide

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

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

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

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

* removed the redundant `sleep` search-term

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

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

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

* Enhance eval_hook to return PipelineData.

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

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

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

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

try to fix

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

* change wording

* fat

* fmt

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

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

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

* Fix help message typos

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

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

* finish refactor on strings command

* simplify code

* rename from ArgumentsCp to CellPathOnlyArgs

* fmt code

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

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

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

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

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

* make code simpler

* finish refactor on bytes commands

* finish refactor on str commands

* fimplify code

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

fixes #6747

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

* fix: 📝 edit the comment

* fix:  updated the tests

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

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

* Make windows-only dependency conditional in toml

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

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

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

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

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

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

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

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

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

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

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

Builds fine, no observable loss in diagnostics quality of life

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

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

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

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

* Deal with `needless_collect` with annotations

* Change hack for needless_collect in `from json`

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

Probably not really worth it.

* Revert "Deal with `needless_collect` with annotations"

This reverts commit 05aca98445.

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

Minimal fix for #6843

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

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

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

* reset reedline to main branch

* update reedline to fix right prompt to be rendered correctly

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

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

* [4325] - hightlight only matched symbol

* [4325] - cleanup

* [4325] - match bracket while typing

* [4325] - fix clippy

* [4325] - add bracket highlight configuration

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

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

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

* accord to suggestions

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

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

Closes #6676

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

close #6840

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

* Add test for pagging with row_overlapping

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

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

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

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

* fix test

* fix test

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

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

* Bump tabled version

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

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

* Use reedline feature branch

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

* fmt

* fix for database
2022-10-20 23:28:18 +13:00
50e53e788a Simplify and reduce allocations in pipeline data loop (#6790) 2022-10-20 23:22:07 +13:00
7336e1df1a rm: fix error span when targets doesn't exists (#6815)
Closes #6810
2022-10-20 10:00:25 +02:00
a724a8fe7d bump to dev version 0.70.1 (#6814) 2022-10-20 18:04:10 +13:00
JT
c731a4e275 Update Cargo.toml 2022-10-19 11:44:04 +13:00
569 changed files with 17030 additions and 14076 deletions

View File

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

View File

@ -1,21 +1,24 @@
# Description
(description of your pull request here)
_(Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.)_
# Tests
_(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)_
Make sure you've done the following:
# User-Facing Changes
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.
_(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 --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
- [ ] `cargo test --workspace --features=extra` to check that all the tests pass
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass
# Documentation
# After Submitting
- [ ] If your PR touches a user-facing nushell feature then make sure that there is an entry in the documentation (https://github.com/nushell/nushell.github.io) for the feature, and update it if necessary.
If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date.

View File

@ -20,32 +20,22 @@ jobs:
NUSHELL_CARGO_TARGET: ci
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
# components: rustfmt, clippy
- uses: Swatinem/rust-cache@v1
with:
key: "v2" # increment this to bust the cache if needed
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Rustfmt
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: fmt
args: --all -- --check
- name: Clippy
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: clippy
args: --features=extra --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
args: --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
nu-tests:
env:
@ -55,39 +45,31 @@ jobs:
fail-fast: true
matrix:
platform: [windows-latest, macos-latest, ubuntu-latest]
style: [extra, default]
style: [default, dataframe]
rust:
- stable
include:
- style: extra
flags: "--features=extra"
- style: default
flags: ""
- style: dataframe
flags: "--features=dataframe"
exclude:
# only test dataframes on Ubuntu (the fastest platform)
- platform: windows-latest
style: default
style: dataframe
- platform: macos-latest
style: default
style: dataframe
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
- uses: Swatinem/rust-cache@v1
with:
key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Tests
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: test
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
@ -108,28 +90,19 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
- uses: Swatinem/rust-cache@v1
with:
key: "2" # increment this to bust the cache if needed
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Install Nushell
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: install
args: --path=. --profile ci --no-default-features
args: --locked --path=. --profile ci --no-default-features
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.10"
@ -159,24 +132,19 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Clippy
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: clippy
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
- name: Tests
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: test
args: --profile ci --package nu_plugin_*

View File

@ -51,9 +51,9 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
# ----------------------------------------------------------------------------
if $os in ['windows-latest'] {
if ($flags | str trim | is-empty) {
cargo build --release --all --target $target --features=extra
cargo build --release --all --target $target
} else {
cargo build --release --all --target $target --features=extra $flags
cargo build --release --all --target $target $flags
}
}
@ -90,10 +90,16 @@ if ($ver | str trim | is-empty) {
cd $dist; $'(char nl)Creating release archive...'; hr-line
if $os in ['ubuntu-latest', 'macos-latest'] {
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
let files = (ls | get name)
let dest = $'($bin)-($version)-($target)'
let archive = $'($dist)/($dest).tar.gz'
let archive = $'($dist)/($bin)-($version)-($target).tar.gz'
tar czf $archive *
mkdir $dest
$files | each {|it| mv $it $dest } | ignore
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
tar -czf $archive $dest
print $'archive: ---> ($archive)'; ls $archive
echo $'::set-output name=archive::($archive)'
@ -102,7 +108,7 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
let releaseStem = $'($bin)-($version)-($target)'
$'(char nl)Download less related stuffs...'; hr-line
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o less.exe
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
# Create Windows msi release package
@ -132,9 +138,9 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
def 'cargo-build-nu' [ options: string ] {
if ($options | str trim | is-empty) {
cargo build --release --all --target $target --features=extra,static-link-openssl
cargo build --release --all --target $target --features=static-link-openssl
} else {
cargo build --release --all --target $target --features=extra,static-link-openssl $options
cargo build --release --all --target $target --features=static-link-openssl $options
}
}

View File

@ -88,7 +88,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release
- name: Publish Archive
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v0.1.13
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
draft: true

4
.gitignore vendored
View File

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

View File

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

665
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,17 @@ license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.70.0"
version = "0.72.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/{ version }/{ name }-{ version }-{ target }.{ archive-format }"
pkg-fmt = "tgz"
[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
pkg-fmt = "zip"
[workspace]
members = [
"crates/nu-cli",
@ -32,27 +39,27 @@ members = [
]
[dependencies]
chrono = { version = "0.4.21", features = ["serde"] }
chrono = { version = "0.4.23", features = ["serde"] }
crossterm = "0.24.0"
ctrlc = "3.2.1"
log = "0.4"
miette = "5.1.0"
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
nu-ansi-term = "0.46.0"
nu-cli = { path="./crates/nu-cli", version = "0.70.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.70.0" }
nu-command = { path="./crates/nu-command", version = "0.70.0" }
nu-engine = { path="./crates/nu-engine", version = "0.70.0" }
nu-json = { path="./crates/nu-json", version = "0.70.0" }
nu-parser = { path="./crates/nu-parser", version = "0.70.0" }
nu-path = { path="./crates/nu-path", version = "0.70.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.70.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.70.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.70.0" }
nu-system = { path = "./crates/nu-system", version = "0.70.0" }
nu-table = { path = "./crates/nu-table", version = "0.70.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.70.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.70.0" }
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
nu-cli = { path="./crates/nu-cli", version = "0.72.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.72.0" }
nu-command = { path="./crates/nu-command", version = "0.72.0" }
nu-engine = { path="./crates/nu-engine", version = "0.72.0" }
nu-json = { path="./crates/nu-json", version = "0.72.0" }
nu-parser = { path="./crates/nu-parser", version = "0.72.0" }
nu-path = { path="./crates/nu-path", version = "0.72.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.72.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.72.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.72.0" }
nu-system = { path = "./crates/nu-system", version = "0.72.0" }
nu-table = { path = "./crates/nu-table", version = "0.72.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.72.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.72.0" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
rayon = "1.5.1"
is_executable = "1.0.1"
@ -69,11 +76,11 @@ signal-hook = { version = "0.3.14", default-features = false }
winres = "0.1"
[target.'cfg(target_family = "unix")'.dependencies]
nix = "0.24"
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]}
atty = "0.2"
[dev-dependencies]
nu-test-support = { path="./crates/nu-test-support", version = "0.70.0" }
nu-test-support = { path="./crates/nu-test-support", version = "0.72.0" }
tempfile = "3.2.0"
assert_cmd = "2.0.2"
pretty_assertions = "1.0.0"
@ -84,10 +91,12 @@ itertools = "0.10.3"
[features]
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
extra = ["default", "dataframe", "database"]
default = ["plugin", "which-support", "trash-support"]
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
extra = ["default"]
default = ["plugin", "which-support", "trash-support", "sqlite"]
stable = ["default"]
wasi = []
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
static-link-openssl = ["dep:openssl"]
@ -100,8 +109,8 @@ trash-support = ["nu-command/trash-support"]
# Dataframe feature for nushell
dataframe = ["nu-command/dataframe"]
# Database commands for nushell
database = ["nu-command/database"]
# SQLite commands for nushell
sqlite = ["nu-command/sqlite"]
[profile.release]
opt-level = "s" # Optimize for size

View File

@ -206,7 +206,7 @@ Nu is under heavy development and will naturally change as it matures. The chart
| Functions | | | | X | | Functions and aliases are supported |
| Variables | | | | X | | Nu supports variables and environment variables |
| Completions | | | | X | | Completions for filepaths |
| Type-checking | | | X | | | Commands check basic types, but input/output isn't checked |
| Type-checking | | | | x | | Commands check basic types, and input/output types |
## Officially Supported By

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,7 +60,7 @@ impl CommandCompletion {
.matches_str(&x.to_string_lossy(), prefix)),
Some(true)
)
&& is_executable::is_executable(&item.path())
&& is_executable::is_executable(item.path())
{
if let Ok(name) = item.file_name().into_string() {
executables.push(name);

View File

@ -5,6 +5,7 @@ use crate::completions::{
use nu_engine::eval_block;
use nu_parser::{flatten_expression, parse, FlatShape};
use nu_protocol::{
ast::PipelineElement,
engine::{EngineState, Stack, StateWorkingSet},
BlockId, PipelineData, Span, Value,
};
@ -131,139 +132,62 @@ impl NuCompleter {
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
for pipeline in output.pipelines.into_iter() {
for expr in pipeline.expressions {
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
let span_offset: usize = alias_offset.iter().sum();
let mut spans: Vec<String> = vec![];
for pipeline_element in pipeline.elements {
match pipeline_element {
PipelineElement::Expression(_, expr)
| PipelineElement::Redirection(_, _, expr)
| PipelineElement::And(_, expr)
| PipelineElement::Or(_, expr) => {
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
let span_offset: usize = alias_offset.iter().sum();
let mut spans: Vec<String> = vec![];
for (flat_idx, flat) in flattened.iter().enumerate() {
// Read the current spam to string
let current_span = working_set.get_span_contents(flat.0).to_vec();
let current_span_str = String::from_utf8_lossy(&current_span);
for (flat_idx, flat) in flattened.iter().enumerate() {
// Read the current spam to string
let current_span = working_set.get_span_contents(flat.0).to_vec();
let current_span_str = String::from_utf8_lossy(&current_span);
// Skip the last 'a' as span item
if flat_idx == flattened.len() - 1 {
let mut chars = current_span_str.chars();
chars.next_back();
let current_span_str = chars.as_str().to_owned();
spans.push(current_span_str.to_string());
} else {
spans.push(current_span_str.to_string());
}
// Complete based on the last span
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
// Context variables
let most_left_var =
most_left_variable(flat_idx, &working_set, flattened.clone());
// Create a new span
let new_span = if flat_idx == 0 {
Span {
start: flat.0.start,
end: flat.0.end - 1 - span_offset,
}
} else {
Span {
start: flat.0.start - span_offset,
end: flat.0.end - 1 - span_offset,
}
};
// Parses the prefix. Completion should look up to the cursor position, not after.
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
let index = pos - (flat.0.start - span_offset);
prefix.drain(index..);
// Variables completion
if prefix.starts_with(b"$") || most_left_var.is_some() {
let mut completer = VariableCompletion::new(
self.engine_state.clone(),
self.stack.clone(),
most_left_var.unwrap_or((vec![], vec![])),
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
// Flags completion
if prefix.starts_with(b"-") {
// Try to complete flag internally
let mut completer = FlagCompletion::new(expr.clone());
let result = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !result.is_empty() {
return result;
// Skip the last 'a' as span item
if flat_idx == flattened.len() - 1 {
let mut chars = current_span_str.chars();
chars.next_back();
let current_span_str = chars.as_str().to_owned();
spans.push(current_span_str.to_string());
} else {
spans.push(current_span_str.to_string());
}
// We got no results for internal completion
// now we can check if external completer is set and use it
if let Some(block_id) = config.external_completer {
if let Some(external_result) =
self.external_completion(block_id, &spans, offset, new_span)
{
return external_result;
}
}
}
// Complete based on the last span
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
// Context variables
let most_left_var =
most_left_variable(flat_idx, &working_set, flattened.clone());
// specially check if it is currently empty - always complete commands
if flat_idx == 0 && working_set.get_span_contents(new_span).is_empty() {
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
FlatShape::String,
true,
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
// Create a new span
let new_span = if flat_idx == 0 {
Span {
start: flat.0.start,
end: flat.0.end - 1 - span_offset,
}
} else {
Span {
start: flat.0.start - span_offset,
end: flat.0.end - 1 - span_offset,
}
};
// Completions that depends on the previous expression (e.g: use, source-env)
if flat_idx > 0 {
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
// Read the content for the previous expression
let prev_expr_str =
working_set.get_span_contents(previous_expr.0).to_vec();
// Parses the prefix. Completion should look up to the cursor position, not after.
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
let index = pos - (flat.0.start - span_offset);
prefix.drain(index..);
// Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source-env" {
let mut completer =
DotNuCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
// Variables completion
if prefix.starts_with(b"$") || most_left_var.is_some() {
let mut completer = VariableCompletion::new(
self.engine_state.clone(),
self.stack.clone(),
most_left_var.unwrap_or((vec![], vec![])),
);
} else if prev_expr_str == b"ls" {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
@ -274,101 +198,190 @@ impl NuCompleter {
pos,
);
}
}
}
// Match other types
match &flat.1 {
FlatShape::Custom(decl_id) => {
let mut completer = CustomCompletion::new(
self.engine_state.clone(),
self.stack.clone(),
*decl_id,
initial_line,
);
// Flags completion
if prefix.starts_with(b"-") {
// Try to complete flag internally
let mut completer = FlagCompletion::new(expr.clone());
let result = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Directory => {
let mut completer =
DirectoryCompletion::new(self.engine_state.clone());
if !result.is_empty() {
return result;
}
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Filepath | FlatShape::GlobPattern => {
let mut completer = FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
flat_shape => {
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
flat_shape.clone(),
false,
);
let mut out: Vec<_> = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
// Try to complete using an external completer (if set)
if let Some(block_id) = config.external_completer {
if let Some(external_result) =
self.external_completion(block_id, &spans, offset, new_span)
{
return external_result;
// We got no results for internal completion
// now we can check if external completer is set and use it
if let Some(block_id) = config.external_completer {
if let Some(external_result) = self
.external_completion(block_id, &spans, offset, new_span)
{
return external_result;
}
}
}
// Check for file completion
let mut completer = FileCompletion::new(self.engine_state.clone());
out = self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
// specially check if it is currently empty - always complete commands
if flat_idx == 0
&& working_set.get_span_contents(new_span).is_empty()
{
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
FlatShape::String,
true,
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
// Completions that depends on the previous expression (e.g: use, source-env)
if flat_idx > 0 {
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
// Read the content for the previous expression
let prev_expr_str =
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source-env"
{
let mut completer =
DotNuCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
} else if prev_expr_str == b"ls" {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
}
}
// Match other types
match &flat.1 {
FlatShape::Custom(decl_id) => {
let mut completer = CustomCompletion::new(
self.engine_state.clone(),
self.stack.clone(),
*decl_id,
initial_line,
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Directory => {
let mut completer =
DirectoryCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Filepath | FlatShape::GlobPattern => {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
flat_shape => {
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
flat_shape.clone(),
false,
);
let mut out: Vec<_> = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
// Try to complete using an external completer (if set)
if let Some(block_id) = config.external_completer {
if let Some(external_result) = self.external_completion(
block_id, &spans, offset, new_span,
) {
return external_result;
}
}
// Check for file completion
let mut completer =
FileCompletion::new(self.engine_state.clone());
out = self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
}
};
}
};
}
}
}
}

View File

@ -23,7 +23,6 @@ pub fn read_plugin_file(
stack: &mut Stack,
plugin_file: Option<Spanned<String>>,
storage_path: &str,
is_perf_true: bool,
) {
// Reading signatures from signature file
// The plugin.nu file stores the parsed signature collected from each registered plugin
@ -31,7 +30,7 @@ pub fn read_plugin_file(
let plugin_path = engine_state.plugin_signatures.clone();
if let Some(plugin_path) = plugin_path {
let plugin_filename = plugin_path.to_string_lossy().to_owned();
let plugin_filename = plugin_path.to_string_lossy();
if let Ok(contents) = std::fs::read(&plugin_path) {
eval_source(
@ -44,9 +43,7 @@ pub fn read_plugin_file(
}
}
if is_perf_true {
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
}
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
}
#[cfg(feature = "plugin")]
@ -80,7 +77,7 @@ pub fn eval_config_contents(
stack: &mut Stack,
) {
if config_path.exists() & config_path.is_file() {
let config_filename = config_path.to_string_lossy().to_owned();
let config_filename = config_path.to_string_lossy();
if let Ok(contents) = std::fs::read(&config_path) {
eval_source(

View File

@ -19,7 +19,6 @@ pub fn evaluate_file(
engine_state: &mut EngineState,
stack: &mut Stack,
input: PipelineData,
is_perf_true: bool,
) -> Result<()> {
// Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) {
@ -30,6 +29,8 @@ pub fn evaluate_file(
let file = std::fs::read(&path).into_diagnostic()?;
engine_state.start_in_file(Some(&path));
let mut working_set = StateWorkingSet::new(engine_state);
trace!("parsing file: {}", path);
@ -54,9 +55,7 @@ pub fn evaluate_file(
std::process::exit(1);
}
if is_perf_true {
info!("evaluate {}:{}:{}", file!(), line!(), column!());
}
info!("evaluate {}:{}:{}", file!(), line!(), column!());
Ok(())
}
@ -75,6 +74,14 @@ pub fn print_table_or_error(
// Change the engine_state config to use the passed in configuration
engine_state.set_config(config);
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, error);
std::process::exit(1);
}
match engine_state.find_decl("table".as_bytes(), &[]) {
Some(decl_id) => {
let command = engine_state.get_decl(decl_id);

View File

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

View File

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

View File

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

View File

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

View File

@ -114,7 +114,7 @@ pub(crate) fn add_menus(
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
if let PipelineData::Value(value, None) = res {
for menu in create_menus(&value, config)? {
for menu in create_menus(&value)? {
line_editor =
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
}
@ -251,7 +251,7 @@ pub(crate) fn add_columnar_menu(
Value::Nothing { .. } => {
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
}
Value::Block {
Value::Closure {
val,
captures,
span,
@ -337,7 +337,7 @@ pub(crate) fn add_list_menu(
Value::Nothing { .. } => {
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
}
Value::Block {
Value::Closure {
val,
captures,
span,
@ -459,7 +459,7 @@ pub(crate) fn add_description_menu(
completer,
}))
}
Value::Block {
Value::Closure {
val,
captures,
span,
@ -477,7 +477,7 @@ pub(crate) fn add_description_menu(
}))
}
_ => Err(ShellError::UnsupportedConfigValue(
"block or omitted value".to_string(),
"closure or omitted value".to_string(),
menu.source.into_abbreviated_string(config),
menu.source.span()?,
)),
@ -491,7 +491,7 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) {
KeyCode::Tab,
ReedlineEvent::UntilFound(vec![
ReedlineEvent::Menu("completion_menu".to_string()),
ReedlineEvent::MenuNext,
ReedlineEvent::Edit(vec![EditCommand::Complete]),
]),
);
@ -964,6 +964,7 @@ fn edit_from_record(
let char = extract_char(value, config)?;
EditCommand::MoveLeftBefore(char)
}
"complete" => EditCommand::Complete,
e => {
return Err(ShellError::UnsupportedConfigValue(
"reedline EditCommand".to_string(),

View File

@ -10,7 +10,7 @@ use lazy_static::lazy_static;
use log::{info, trace, warn};
use miette::{IntoDiagnostic, Result};
use nu_color_config::get_color_config;
use nu_engine::{convert_env_values, eval_block};
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::{
ast::PathMember,
@ -24,7 +24,6 @@ use std::{
sync::atomic::Ordering,
time::Instant,
};
use strip_ansi_escapes::strip;
use sysinfo::SystemExt;
// According to Daniel Imms @Tyriar, we need to do these this way:
@ -41,7 +40,6 @@ pub fn evaluate_repl(
engine_state: &mut EngineState,
stack: &mut Stack,
nushell_path: &str,
is_perf_true: bool,
prerun_command: Option<Spanned<String>>,
) -> Result<()> {
use reedline::{FileBackedHistory, Reedline, Signal};
@ -51,7 +49,7 @@ pub fn evaluate_repl(
if !atty::is(atty::Stream::Stdin) {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Nushell launched as interactive REPL but STDIN is not a TTY, either launch in a valid terminal or provide arguments to invoke a script!",
"Nushell launched as a REPL, but STDIN is not a TTY; either launch in a valid terminal or provide arguments to invoke a script!",
))
.into_diagnostic();
}
@ -60,14 +58,12 @@ pub fn evaluate_repl(
let mut nu_prompt = NushellPrompt::new();
if is_perf_true {
info!(
"translate environment vars {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"translate environment vars {}:{}:{}",
file!(),
line!(),
column!()
);
// Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) {
@ -92,18 +88,14 @@ pub fn evaluate_repl(
},
);
if is_perf_true {
info!(
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
if is_perf_true {
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
}
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
let mut line_editor = Reedline::create();
@ -121,9 +113,7 @@ pub fn evaluate_repl(
engine_state.config.history_file_format,
);
if let Some(history_path) = history_path.as_deref() {
if is_perf_true {
info!("setup history {}:{}:{}", file!(), line!(), column!());
}
info!("setup history {}:{}:{}", file!(), line!(), column!());
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
HistoryFileFormat::PlainText => Box::new(
@ -149,15 +139,7 @@ pub fn evaluate_repl(
if use_ansi {
println!("{}", banner);
} else {
let stripped_string = {
if let Ok(bytes) = strip(&banner) {
String::from_utf8_lossy(&bytes).to_string()
} else {
banner
}
};
println!("{}", stripped_string);
println!("{}", nu_utils::strip_ansi_string_likely(banner));
}
}
@ -173,14 +155,12 @@ pub fn evaluate_repl(
}
loop {
if is_perf_true {
info!(
"load config each loop {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"load config each loop {}:{}:{}",
file!(),
line!(),
column!()
);
let cwd = get_guaranteed_cwd(engine_state, stack);
@ -201,15 +181,11 @@ pub fn evaluate_repl(
let config = engine_state.get_config();
if is_perf_true {
info!("setup colors {}:{}:{}", file!(), line!(), column!());
}
info!("setup colors {}:{}:{}", file!(), line!(), column!());
let color_hm = get_color_config(config);
if is_perf_true {
info!("update reedline {}:{}:{}", file!(), line!(), column!());
}
info!("update reedline {}:{}:{}", file!(), line!(), column!());
let engine_reference = std::sync::Arc::new(engine_state.clone());
line_editor = line_editor
.with_highlighter(Box::new(NuHighlighter {
@ -266,18 +242,14 @@ pub fn evaluate_repl(
};
if config.sync_history_on_enter {
if is_perf_true {
info!("sync history {}:{}:{}", file!(), line!(), column!());
}
info!("sync history {}:{}:{}", file!(), line!(), column!());
if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {}", e);
}
}
if is_perf_true {
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
}
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
// Changing the line editor based on the found keybindings
line_editor = match create_keybindings(config) {
@ -301,14 +273,12 @@ pub fn evaluate_repl(
}
};
if is_perf_true {
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
}
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
// Right before we start our prompt and take input from the user,
// fire the "pre_prompt" hook
if let Some(hook) = config.hooks.pre_prompt.clone() {
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
report_error_new(engine_state, &err);
}
}
@ -323,19 +293,16 @@ pub fn evaluate_repl(
}
let config = engine_state.get_config();
let prompt =
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt, is_perf_true);
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
entry_num += 1;
if is_perf_true {
info!(
"finished setup, starting repl {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"finished setup, starting repl {}:{}:{}",
file!(),
line!(),
column!()
);
let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration;
@ -367,7 +334,7 @@ pub fn evaluate_repl(
// Right before we start running the code the user gave us,
// fire the "pre_execution" hook
if let Some(hook) = config.hooks.pre_execution.clone() {
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
report_error_new(engine_state, &err);
}
}
@ -561,7 +528,7 @@ pub fn evaluate_repl(
Err(err) => {
let message = err.to_string();
if !message.contains("duration") {
println!("Error: {:?}", err);
eprintln!("Error: {:?}", err);
// TODO: Identify possible error cases where a hard failure is preferable
// Ignoring and reporting could hide bigger problems
// e.g. https://github.com/nushell/nushell/issues/6452
@ -719,6 +686,7 @@ pub fn eval_env_change_hook(
eval_hook(
engine_state,
stack,
None,
vec![("$before".into(), before), ("$after".into(), after.clone())],
hook_value,
)?;
@ -744,15 +712,17 @@ pub fn eval_env_change_hook(
pub fn eval_hook(
engine_state: &mut EngineState,
stack: &mut Stack,
input: Option<PipelineData>,
arguments: Vec<(String, Value)>,
value: &Value,
) -> Result<(), ShellError> {
) -> Result<PipelineData, ShellError> {
let value_span = value.span()?;
let condition_path = PathMember::String {
val: "condition".to_string(),
span: value_span,
};
let mut output = PipelineData::new(Span::new(0, 0));
let code_path = PathMember::String {
val: "code".to_string(),
@ -762,7 +732,7 @@ pub fn eval_hook(
match value {
Value::List { vals, .. } => {
for val in vals {
eval_hook(engine_state, stack, arguments.clone(), val)?
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
}
}
Value::Record { .. } => {
@ -773,11 +743,17 @@ pub fn eval_hook(
val: block_id,
span: block_span,
..
}
| Value::Closure {
val: block_id,
span: block_span,
..
} => {
match run_hook_block(
engine_state,
stack,
block_id,
None,
arguments.clone(),
block_span,
) {
@ -825,6 +801,7 @@ pub fn eval_hook(
name.as_bytes().to_vec(),
val.span()?,
Type::Any,
false,
);
vars.push((var_id, val));
@ -857,7 +834,9 @@ pub fn eval_hook(
.collect();
match eval_block(engine_state, stack, &block, input, false, false) {
Ok(_) => {}
Ok(pipeline_data) => {
output = pipeline_data;
}
Err(err) => {
report_error_new(engine_state, &err);
}
@ -872,7 +851,28 @@ pub fn eval_hook(
span: block_span,
..
} => {
run_hook_block(engine_state, stack, block_id, arguments, block_span)?;
run_hook_block(
engine_state,
stack,
block_id,
input,
arguments,
block_span,
)?;
}
Value::Closure {
val: block_id,
span: block_span,
..
} => {
run_hook_block(
engine_state,
stack,
block_id,
input,
arguments,
block_span,
)?;
}
other => {
return Err(ShellError::UnsupportedConfigValue(
@ -889,7 +889,34 @@ pub fn eval_hook(
span: block_span,
..
} => {
run_hook_block(engine_state, stack, *block_id, arguments, *block_span)?;
output = PipelineData::Value(
run_hook_block(
engine_state,
stack,
*block_id,
input,
arguments,
*block_span,
)?,
None,
);
}
Value::Closure {
val: block_id,
span: block_span,
..
} => {
output = PipelineData::Value(
run_hook_block(
engine_state,
stack,
*block_id,
input,
arguments,
*block_span,
)?,
None,
);
}
other => {
return Err(ShellError::UnsupportedConfigValue(
@ -903,19 +930,20 @@ pub fn eval_hook(
let cwd = get_guaranteed_cwd(engine_state, stack);
engine_state.merge_env(stack, cwd)?;
Ok(())
Ok(output)
}
pub fn run_hook_block(
engine_state: &EngineState,
stack: &mut Stack,
block_id: BlockId,
optional_input: Option<PipelineData>,
arguments: Vec<(String, Value)>,
span: Span,
) -> Result<Value, ShellError> {
let block = engine_state.get_block(block_id);
let input = PipelineData::new(span);
let input = optional_input.unwrap_or_else(|| PipelineData::new(span));
let mut callee_stack = stack.gather_captures(&block.captures);
@ -934,7 +962,8 @@ pub fn run_hook_block(
}
}
match eval_block(engine_state, &mut callee_stack, block, input, false, false) {
match eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)
{
Ok(pipeline_data) => match pipeline_data.into_value(span) {
Value::Error { error } => Err(error),
val => {

View File

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

View File

@ -1,11 +1,11 @@
use log::trace;
use crate::repl::eval_hook;
use nu_engine::eval_block;
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
use nu_protocol::engine::StateWorkingSet;
use nu_protocol::CliError;
use nu_protocol::{
engine::{EngineState, Stack},
PipelineData, ShellError, Span, Value,
print_if_stream, PipelineData, ShellError, Span, Value,
};
#[cfg(windows)]
use nu_utils::enable_vt_processing;
@ -204,8 +204,6 @@ pub fn eval_source(
fname: &str,
input: PipelineData,
) -> bool {
trace!("eval_source");
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse(
@ -232,7 +230,30 @@ pub fn eval_source(
match eval_block(engine_state, stack, &block, input, false, false) {
Ok(pipeline_data) => {
match pipeline_data.print(engine_state, stack, false, false) {
let config = engine_state.get_config();
let result;
if let PipelineData::ExternalStream {
stdout: stream,
stderr: stderr_stream,
exit_code,
..
} = pipeline_data
{
result = print_if_stream(stream, stderr_stream, false, exit_code);
} else if let Some(hook) = config.hooks.display_output.clone() {
match eval_hook(engine_state, stack, Some(pipeline_data), vec![], &hook) {
Err(err) => {
result = Err(err);
}
Ok(val) => {
result = val.print(engine_state, stack, false, false);
}
}
} else {
result = pipeline_data.print(engine_state, stack, false, false);
}
match result {
Err(err) => {
let working_set = StateWorkingSet::new(engine_state);

View File

@ -5,11 +5,11 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.70.0"
version = "0.72.0"
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
nu-protocol = { path = "../nu-protocol", version = "0.72.0" }
nu-ansi-term = "0.46.0"
nu-json = { path = "../nu-json", version = "0.70.0" }
nu-table = { path = "../nu-table", version = "0.70.0" }
nu-json = { path = "../nu-json", version = "0.72.0" }
nu-table = { path = "../nu-table", version = "0.72.0" }
serde = { version="1.0.123", features=["derive"] }

View File

@ -217,15 +217,10 @@ pub fn get_color_config(config: &Config) -> HashMap<String, Style> {
hm.insert("hints".to_string(), Color::DarkGray.normal());
for (key, value) in &config.color_config {
let value = value
.as_string()
.expect("the only values for config color must be strings");
update_hashmap(key, &value, &mut hm);
// eprintln!(
// "config: {}:{}\t\t\thashmap: {}:{:?}",
// &key, &value, &key, &hm[key]
// );
match value.as_string() {
Ok(value) => update_hashmap(key, &value, &mut hm),
Err(_) => continue,
}
}
hm

View File

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

View File

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

View File

@ -34,6 +34,10 @@ pub fn get_shape_color(shape: String, conf: &Config) -> Style {
"shape_variable" => Style::new().fg(Color::Purple),
"shape_flag" => Style::new().fg(Color::Blue).bold(),
"shape_custom" => Style::new().fg(Color::Green),
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
"shape_and" => Style::new().fg(Color::Purple).bold(),
"shape_or" => Style::new().fg(Color::Purple).bold(),
"shape_nothing" => Style::new().fg(Color::LightCyan),
_ => Style::default(),
},

View File

@ -5,25 +5,24 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
edition = "2021"
license = "MIT"
name = "nu-command"
version = "0.70.0"
version = "0.72.0"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-color-config = { path = "../nu-color-config", version = "0.70.0" }
nu-engine = { path = "../nu-engine", version = "0.70.0" }
nu-glob = { path = "../nu-glob", version = "0.70.0" }
nu-json = { path = "../nu-json", version = "0.70.0" }
nu-parser = { path = "../nu-parser", version = "0.70.0" }
nu-path = { path = "../nu-path", version = "0.70.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.70.0" }
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
nu-system = { path = "../nu-system", version = "0.70.0" }
nu-table = { path = "../nu-table", version = "0.70.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.70.0" }
nu-test-support = { path = "../nu-test-support", version = "0.70.0" }
nu-utils = { path = "../nu-utils", version = "0.70.0" }
nu-color-config = { path = "../nu-color-config", version = "0.72.0" }
nu-engine = { path = "../nu-engine", version = "0.72.0" }
nu-glob = { path = "../nu-glob", version = "0.72.0" }
nu-json = { path = "../nu-json", version = "0.72.0" }
nu-parser = { path = "../nu-parser", version = "0.72.0" }
nu-path = { path = "../nu-path", version = "0.72.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.72.0" }
nu-protocol = { path = "../nu-protocol", version = "0.72.0" }
nu-system = { path = "../nu-system", version = "0.72.0" }
nu-table = { path = "../nu-table", version = "0.72.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.72.0" }
nu-utils = { path = "../nu-utils", version = "0.72.0" }
nu-ansi-term = "0.46.0"
num-format = { version = "0.4.3" }
@ -33,13 +32,13 @@ base64 = "0.13.0"
byteorder = "1.4.3"
bytesize = "1.1.0"
calamine = "0.18.0"
chrono = { version = "0.4.21", features = ["serde", "unstable-locales"] }
chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false }
chrono-humanize = "0.2.1"
chrono-tz = "0.6.3"
crossterm = "0.24.0"
csv = "1.1.6"
dialoguer = "0.9.0"
digest = "0.10.0"
dialoguer = { default-features = false, version = "0.9.0" }
digest = { default-features = false, version = "0.10.0" }
dtparse = "1.2.0"
eml-parser = "0.1.0"
encoding_rs = "0.8.30"
@ -55,13 +54,14 @@ is-root = "0.1.2"
itertools = "0.10.0"
lazy_static = "1.4.0"
log = "0.4.14"
lscolors = { version = "0.12.0", features = ["crossterm"]}
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
md5 = { package = "md-5", version = "0.10.0" }
meval = "0.2.0"
mime = "0.3.16"
notify = "4.0.17"
num = { version = "0.4.0", optional = true }
num-traits = "0.2.14"
once_cell = "1.0"
pathdiff = "0.2.1"
powierza-coefficient = "1.0.1"
quick-xml = "0.23.0"
@ -78,7 +78,6 @@ serde_yaml = "0.9.4"
sha2 = "0.10.0"
# Disable default features b/c the default features build Git (very slow to compile)
shadow-rs = { version = "0.16.1", default-features = false }
strip-ansi-escapes = "0.1.1"
sysinfo = "0.26.2"
terminal_size = "0.2.1"
thiserror = "1.0.31"
@ -88,8 +87,8 @@ unicode-segmentation = "1.8.0"
url = "2.2.1"
uuid = { version = "1.1.2", features = ["v4"] }
which = { version = "4.3.0", optional = true }
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
wax = { version = "0.5.0", features = ["diagnostics"] }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
wax = { version = "0.5.0" }
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
@ -99,13 +98,14 @@ winreg = "0.10.1"
[target.'cfg(unix)'.dependencies]
umask = "2.0.0"
users = "0.11.0"
libc = "0.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
version = "2.1.3"
optional = true
[dependencies.polars]
version = "0.23.2"
version = "0.25.0"
optional = true
features = [
"arg_where",
@ -136,9 +136,8 @@ features = [
]
[target.'cfg(windows)'.dependencies.windows]
version = "0.37.0"
version = "0.42.0"
features = [
"alloc",
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_SystemServices",
@ -148,13 +147,15 @@ features = [
trash-support = ["trash"]
which-support = ["which"]
plugin = ["nu-parser/plugin"]
dataframe = ["polars", "num"]
database = ["sqlparser", "rusqlite"]
dataframe = ["polars", "num", "sqlparser"]
sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
[build-dependencies]
shadow-rs = { version = "0.16.1", default-features = false }
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.72.0" }
hamcrest2 = "0.3.0"
dirs-next = "2.0.0"
proptest = "1.0.0"

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -15,6 +15,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("bits and")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required(
"target",
SyntaxShape::Int,

View File

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

View File

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

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -15,6 +15,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("bits or")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required(
"target",
SyntaxShape::Int,

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use num_traits::int::PrimInt;
use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("bits rol")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate left")
.switch(
"signed",

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use num_traits::int::PrimInt;
use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("bits ror")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
.switch(
"signed",

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use num_traits::CheckedShl;
use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("bits shl")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to shift left")
.switch(
"signed",

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use num_traits::CheckedShr;
use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("bits shr")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to shift right")
.switch(
"signed",

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -15,6 +15,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("bits xor")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required(
"target",
SyntaxShape::Int,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher};
/// ```text
/// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}})
/// ```
#[derive(Eq, Debug)]
#[derive(Eq, Debug, Ord, PartialOrd)]
pub enum HashableValue {
Bool {
val: bool,
@ -228,7 +228,7 @@ mod test {
vals: vec![Value::Bool { val: true, span }],
span,
},
Value::Block {
Value::Closure {
val: 0,
captures: HashMap::new(),
span,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ use nu_parser::parse_duration_bytes;
use nu_protocol::{
ast::{Call, CellPath, Expr},
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Unit,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Unit,
Value,
};
@ -17,6 +17,13 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("into duration")
.input_output_types(vec![
(Type::String, Type::Duration),
(Type::Duration, Type::Duration),
// TODO: --convert option should be implemented as `format duration`
(Type::String, Type::String),
(Type::Duration, Type::String),
])
.named(
"convert",
SyntaxShape::String,
@ -26,7 +33,7 @@ impl Command for SubCommand {
.rest(
"rest",
SyntaxShape::CellPath,
"column paths to convert to duration (for table input)",
"for a data structure input, convert data at the given cell paths",
)
.category(Category::Conversions)
}
@ -36,7 +43,7 @@ impl Command for SubCommand {
}
fn extra_usage(&self) -> &str {
"into duration does not take leap years into account and every month is calculated with 30 days"
"This command does not take leap years into account, and every month is assumed to have 30 days."
}
fn search_terms(&self) -> Vec<&str> {
@ -121,11 +128,19 @@ impl Command for SubCommand {
span,
}),
},
Example {
description: "Convert duration to duration",
example: "420sec | into duration",
result: Some(Value::Duration {
val: 7 * 60 * 1000 * 1000 * 1000,
span,
}),
},
Example {
description: "Convert duration to the requested duration as a string",
example: "420sec | into duration --convert min",
example: "420sec | into duration --convert ms",
result: Some(Value::String {
val: "7 min".to_string(),
val: "420000 ms".to_string(),
span,
}),
},

View File

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

View File

@ -1,16 +1,23 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
struct Arguments {
radix: Option<Value>,
column_paths: Vec<CellPath>,
radix: u32,
cell_paths: Option<Vec<CellPath>>,
little_endian: bool,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)]
pub struct SubCommand;
@ -21,12 +28,22 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("into int")
.input_output_types(vec![
(Type::String, Type::Int),
(Type::Number, Type::Int),
(Type::Bool, Type::Int),
// Unix timestamp in seconds
(Type::Date, Type::Int),
// TODO: Users should do this by dividing a Filesize by a Filesize explicitly
(Type::Filesize, Type::Int),
])
.vectorizes_over_list(true)
.named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
.switch("little-endian", "use little-endian byte decoding", None)
.rest(
"rest",
SyntaxShape::CellPath,
"column paths to convert to int (for table input)",
"for a data structure input, convert data at the given cell paths",
)
.category(Category::Conversions)
}
@ -46,7 +63,29 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_int(engine_state, stack, call, input)
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let radix = call.get_flag::<Value>(engine_state, stack, "radix")?;
let radix: u32 = match radix {
Some(Value::Int { val, span }) => {
if !(2..=36).contains(&val) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
span,
));
}
val as u32
}
Some(_) => 10,
None => 10,
};
let args = Arguments {
radix,
little_endian: call.has_flag("little-endian"),
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
@ -121,59 +160,9 @@ impl Command for SubCommand {
}
}
fn into_int(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let options = Arguments {
radix: call.get_flag(engine_state, stack, "radix")?,
little_endian: call.has_flag("little-endian"),
column_paths: call.rest(engine_state, stack, 0)?,
};
let radix: u32 = match options.radix {
Some(Value::Int { val, .. }) => val as u32,
Some(_) => 10,
None => 10,
};
if let Some(val) = &options.radix {
if !(2..=36).contains(&radix) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
val.span()?,
));
}
}
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, head, radix, options.little_endian)
} else {
let mut ret = v;
for path in &options.column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, head, radix, options.little_endian)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let radix = args.radix;
let little_endian = args.little_endian;
match input {
Value::Int { val: _, .. } => {
if radix == 10 {
@ -401,21 +390,45 @@ mod test {
let word = Value::test_string("10");
let expected = Value::test_int(10);
let actual = action(&word, Span::test_data(), 10, false);
let actual = action(
&word,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
#[test]
fn turns_binary_to_integer() {
let s = Value::test_string("0b101");
let actual = action(&s, Span::test_data(), 10, false);
let actual = action(
&s,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(5));
}
#[test]
fn turns_hex_to_integer() {
let s = Value::test_string("0xFF");
let actual = action(&s, Span::test_data(), 16, false);
let actual = action(
&s,
&Arguments {
radix: 16,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(255));
}
@ -423,7 +436,15 @@ mod test {
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
let integer_str = Value::test_string("36anra");
let actual = action(&integer_str, Span::test_data(), 10, false);
let actual = action(
&integer_str,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual.get_type(), Error)
}

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,8 @@ use nu_parser::parse;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack, StateWorkingSet},
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
Value,
};
#[derive(Clone)]
@ -20,6 +21,7 @@ impl Command for Ast {
fn signature(&self) -> Signature {
Signature::build("ast")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"pipeline",
SyntaxShape::String,
@ -50,17 +52,17 @@ impl Command for Ast {
Example {
description: "Print the ast of a string",
example: "ast 'hello'",
result: None,
result: Some(Value::nothing(Span::test_data())),
},
Example {
description: "Print the ast of a pipeline",
example: "ast 'ls | where name =~ README'",
result: None,
result: Some(Value::nothing(Span::test_data())),
},
Example {
description: "Print the ast of a pipeline with an error",
example: "ast 'for x in 1..10 { echo $x '",
result: None,
result: Some(Value::nothing(Span::test_data())),
},
]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct DefEnv;
@ -16,13 +16,10 @@ impl Command for DefEnv {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("def-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required(
"block",
SyntaxShape::Block(Some(vec![])),
"body of the definition",
)
.required("block", SyntaxShape::Block, "body of the definition")
.category(Category::Core)
}

View File

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

View File

@ -1,9 +1,8 @@
use nu_engine::{eval_block, CallExt};
use nu_engine::{eval_block_with_early_return, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape,
Value,
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -18,14 +17,25 @@ impl Command for Do {
"Run a block"
}
fn signature(&self) -> nu_protocol::Signature {
fn signature(&self) -> Signature {
Signature::build("do")
.required("block", SyntaxShape::Any, "the block to run")
.required("closure", SyntaxShape::Any, "the closure to run")
.input_output_types(vec![(Type::Any, Type::Any)])
.switch(
"ignore-errors",
"ignore errors as the block runs",
Some('i'),
)
.switch(
"ignore-shell-errors",
"ignore shell errors as the block runs",
Some('s'),
)
.switch(
"ignore-program-errors",
"ignore program errors as the block runs",
Some('p'),
)
.switch(
"capture-errors",
"capture errors as the block runs and return it",
@ -41,10 +51,12 @@ impl Command for Do {
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
) -> Result<PipelineData, ShellError> {
let block: Closure = call.req(engine_state, stack, 0)?;
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
let ignore_errors = call.has_flag("ignore-errors");
let ignore_all_errors = call.has_flag("ignore-errors");
let ignore_shell_errors = ignore_all_errors || call.has_flag("ignore-shell-errors");
let ignore_program_errors = ignore_all_errors || call.has_flag("ignore-program-errors");
let capture_errors = call.has_flag("capture-errors");
let mut stack = stack.captures_to_stack(&block.captures);
@ -88,93 +100,79 @@ impl Command for Do {
)
}
}
let result = eval_block(
let result = eval_block_with_early_return(
engine_state,
&mut stack,
block,
input,
call.redirect_stdout,
ignore_errors || capture_errors,
capture_errors || ignore_shell_errors || ignore_program_errors,
);
if ignore_errors {
match result {
Ok(x) => Ok(x),
Err(_) => Ok(PipelineData::new(call.head)),
}
} else if capture_errors {
// collect stdout and stderr and check exit code.
// if exit code is not 0, return back ShellError.
match result {
match result {
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code,
span,
metadata,
trim_end_newline,
}) if capture_errors => {
let mut exit_code_ctrlc = None;
let exit_code: Vec<Value> = match exit_code {
None => vec![],
Some(exit_code_stream) => {
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
exit_code_stream.into_iter().collect()
}
};
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
if *code != 0 {
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => stderr_stream.into_string().map(|s| s.item)?,
};
return Err(ShellError::ExternalCommand(
"External command failed".to_string(),
stderr_msg,
span,
));
}
}
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code,
exit_code: Some(ListStream::from_stream(
exit_code.into_iter(),
exit_code_ctrlc,
)),
span,
metadata,
}) => {
// collect all output first.
let mut stderr_ctrlc = None;
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => {
stderr_ctrlc = stderr_stream.ctrlc.clone();
stderr_stream.into_string().map(|s| s.item)?
}
};
let mut stdout_ctrlc = None;
let stdout_msg = match stdout {
None => "".to_string(),
Some(stdout_stream) => {
stdout_ctrlc = stdout_stream.ctrlc.clone();
stdout_stream.into_string().map(|s| s.item)?
}
};
let mut exit_code_ctrlc = None;
let exit_code: Vec<Value> = match exit_code {
None => vec![],
Some(exit_code_stream) => {
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
exit_code_stream.into_iter().collect()
}
};
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
// if exit_code is not 0, it indicates error occured, return back Err.
if *code != 0 {
return Err(ShellError::ExternalCommand(
"External command runs to failed".to_string(),
stderr_msg,
span,
));
}
}
// construct pipeline data to our caller
Ok(PipelineData::ExternalStream {
stdout: Some(RawStream::new(
Box::new(vec![Ok(stdout_msg.into_bytes())].into_iter()),
stdout_ctrlc,
span,
)),
stderr: Some(RawStream::new(
Box::new(vec![Ok(stderr_msg.into_bytes())].into_iter()),
stderr_ctrlc,
span,
)),
exit_code: Some(ListStream::from_stream(
exit_code.into_iter(),
exit_code_ctrlc,
)),
span,
metadata,
})
}
Ok(other) => Ok(other),
Err(e) => Err(e),
trim_end_newline,
})
}
} else {
result
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code: _,
span,
metadata,
trim_end_newline,
}) if ignore_program_errors => Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code: None,
span,
metadata,
trim_end_newline,
}),
Ok(PipelineData::Value(Value::Error { .. }, ..)) if ignore_shell_errors => {
Ok(PipelineData::new(call.head))
}
Err(_) if ignore_shell_errors => Ok(PipelineData::new(call.head)),
r => r,
}
}
@ -186,14 +184,34 @@ impl Command for Do {
result: Some(Value::test_string("hello")),
},
Example {
description: "Run the block and ignore errors",
description: "Run the block and ignore both shell and program errors",
example: r#"do -i { thisisnotarealcommand }"#,
result: None,
},
Example {
description: "Run the block and ignore shell errors",
example: r#"do -s { thisisnotarealcommand }"#,
result: None,
},
Example {
description: "Run the block and ignore program errors",
example: r#"do -p { nu -c 'exit 1' }; echo "I'll still run""#,
result: None,
},
Example {
description: "Abort the pipeline if a program returns a non-zero exit code",
example: r#"do -c { nu -c 'exit 1' } | myscarycommand"#,
result: None,
},
Example {
description: "Run the block, with a positional parameter",
example: r#"do {|x| 100 + $x } 50"#,
result: Some(Value::test_int(150)),
example: r#"do {|x| 100 + $x } 77"#,
result: Some(Value::test_int(177)),
},
Example {
description: "Run the block, with input",
example: r#"77 | do {|x| 100 + $in }"#,
result: None, // TODO: returns 177
},
]
}

View File

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

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, Signature, Span, Value,
Category, Example, IntoPipelineData, PipelineData, Signature, Span, Type, Value,
};
#[derive(Clone)]
@ -14,7 +14,9 @@ impl Command for ExportCommand {
}
fn signature(&self) -> Signature {
Signature::build("export").category(Category::Core)
Signature::build("export")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.category(Category::Core)
}
fn usage(&self) -> &str {
@ -43,6 +45,7 @@ impl Command for ExportCommand {
&ExportCommand.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head,
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct ExportAlias;
@ -16,6 +16,7 @@ impl Command for ExportAlias {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export alias")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct ExportDef;
@ -16,13 +16,10 @@ impl Command for ExportDef {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export def")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required(
"block",
SyntaxShape::Block(Some(vec![])),
"body of the definition",
)
.required("block", SyntaxShape::Block, "body of the definition")
.category(Category::Core)
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct ExportDefEnv;
@ -16,13 +16,10 @@ impl Command for ExportDefEnv {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export def-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required(
"block",
SyntaxShape::Block(Some(vec![])),
"body of the definition",
)
.required("block", SyntaxShape::Block, "body of the definition")
.category(Category::Core)
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct ExportExtern;
@ -16,6 +16,7 @@ impl Command for ExportExtern {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export extern")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.category(Category::Core)

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct ExportUse;
@ -16,6 +16,7 @@ impl Command for ExportUse {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
.category(Category::Core)
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Extern;
@ -16,6 +16,7 @@ impl Command for Extern {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("extern")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.category(Category::Core)

View File

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

View File

@ -9,7 +9,7 @@ use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Value,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use std::borrow::Borrow;
#[derive(Clone)]
@ -22,6 +22,7 @@ impl Command for Help {
fn signature(&self) -> Signature {
Signature::build("help")
.input_output_types(vec![(Type::Nothing, Type::String)])
.rest(
"rest",
SyntaxShape::String,
@ -103,6 +104,7 @@ fn help(
let mut vals = vec![];
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(decl.borrow());
let signatures = sig.to_string();
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
@ -134,21 +136,9 @@ fn help(
span: head,
});
cols.push("is_plugin".into());
vals.push(Value::Bool {
val: decl.is_plugin().is_some(),
span: head,
});
cols.push("is_custom".into());
vals.push(Value::Bool {
val: decl.is_custom_command(),
span: head,
});
cols.push("is_keyword".into());
vals.push(Value::Bool {
val: decl.is_parser_keyword(),
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span: head,
});
@ -162,6 +152,16 @@ fn help(
span: head,
});
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span: head,
});
cols.push("search_terms".into());
vals.push(if search_terms.is_empty() {
Value::nothing(head)
@ -219,6 +219,7 @@ fn help(
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(decl.borrow());
let signatures = sig.to_string();
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
@ -235,21 +236,9 @@ fn help(
span: head,
});
cols.push("is_plugin".into());
vals.push(Value::Bool {
val: decl.is_plugin().is_some(),
span: head,
});
cols.push("is_custom".into());
vals.push(Value::Bool {
val: decl.is_custom_command(),
span: head,
});
cols.push("is_keyword".into());
vals.push(Value::Bool {
val: decl.is_parser_keyword(),
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span: head,
});
@ -259,6 +248,16 @@ fn help(
span: head,
});
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span: head,
});
cols.push("search_terms".into());
vals.push(if search_terms.is_empty() {
Value::nothing(head)
@ -292,9 +291,9 @@ fn help(
let output = engine_state
.get_signatures_with_examples(false)
.iter()
.filter(|(signature, _, _, _)| signature.name == name)
.map(|(signature, examples, _, _)| {
get_full_help(signature, examples, engine_state, stack)
.filter(|(signature, _, _, _, _)| signature.name == name)
.map(|(signature, examples, _, _, is_parser_keyword)| {
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
})
.collect::<Vec<String>>();
@ -363,15 +362,12 @@ pub fn highlight_search_string(
}
};
// strip haystack to remove existing ansi style
let stripped_haystack: String = match strip_ansi_escapes::strip(haystack) {
Ok(i) => String::from_utf8(i).unwrap_or_else(|_| String::from(haystack)),
Err(_) => String::from(haystack),
};
let stripped_haystack = nu_utils::strip_ansi_likely(haystack);
let mut last_match_end = 0;
let style = Style::new().fg(White).on(Red);
let mut highlighted = String::new();
for cap in regex.captures_iter(stripped_haystack.as_str()) {
for cap in regex.captures_iter(stripped_haystack.as_ref()) {
match cap {
Ok(capture) => {
let start = match capture.get(0) {

View File

@ -0,0 +1,333 @@
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value,
};
#[derive(Clone)]
pub struct HelpOperators;
impl Command for HelpOperators {
fn name(&self) -> &str {
"help operators"
}
fn usage(&self) -> &str {
"Show help on nushell operators."
}
fn signature(&self) -> Signature {
Signature::build("help operators").category(Category::Core)
}
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let op_info = generate_operator_info();
let mut recs = vec![];
for op in op_info {
let mut cols = vec![];
let mut vals = vec![];
cols.push("type".into());
vals.push(Value::string(op.op_type, head));
cols.push("operator".into());
vals.push(Value::string(op.operator, head));
cols.push("name".into());
vals.push(Value::string(op.name, head));
cols.push("description".into());
vals.push(Value::string(op.description, head));
cols.push("precedence".into());
vals.push(Value::int(op.precedence, head));
recs.push(Value::Record {
cols,
vals,
span: head,
})
}
Ok(recs
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
}
}
struct OperatorInfo {
op_type: String,
operator: String,
name: String,
description: String,
precedence: i64,
}
fn generate_operator_info() -> Vec<OperatorInfo> {
vec![
OperatorInfo {
op_type: "Assignment".into(),
operator: "=".into(),
name: "Assign".into(),
description: "Assigns a value to a variable.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "+=".into(),
name: "PlusAssign".into(),
description: "Adds a value to a variable.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "-=".into(),
name: "MinusAssign".into(),
description: "Subtracts a value from a variable.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "*=".into(),
name: "MultiplyAssign".into(),
description: "Multiplies a variable by a value.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "/=".into(),
name: "DivideAssign".into(),
description: "Divides a variable by a value.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "==".into(),
name: "Equal".into(),
description: "Checks if two values are equal.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "!=".into(),
name: "NotEqual".into(),
description: "Checks if two values are not equal.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "<".into(),
name: "LessThan".into(),
description: "Checks if a value is less than another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "<=".into(),
name: "LessThanOrEqual".into(),
description: "Checks if a value is less than or equal to another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: ">".into(),
name: "GreaterThan".into(),
description: "Checks if a value is greater than another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: ">=".into(),
name: "GreaterThanOrEqual".into(),
description: "Checks if a value is greater than or equal to another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "=~".into(),
name: "RegexMatch".into(),
description: "Checks if a value matches a regular expression.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "!~".into(),
name: "NotRegexMatch".into(),
description: "Checks if a value does not match a regular expression.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "in".into(),
name: "In".into(),
description: "Checks if a value is in a list or string.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "not-in".into(),
name: "NotIn".into(),
description: "Checks if a value is not in a list or string.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "starts-with".into(),
name: "StartsWith".into(),
description: "Checks if a string starts with another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "ends-with".into(),
name: "EndsWith".into(),
description: "Checks if a string ends with another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "not".into(),
name: "UnaryNot".into(),
description: "Negates a value or expression.".into(),
precedence: 0,
},
OperatorInfo {
op_type: "Math".into(),
operator: "+".into(),
name: "Plus".into(),
description: "Adds two values.".into(),
precedence: 90,
},
OperatorInfo {
op_type: "Math".into(),
operator: "++".into(),
name: "Append".into(),
description: "Appends two lists or a list and a value.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Math".into(),
operator: "-".into(),
name: "Minus".into(),
description: "Subtracts two values.".into(),
precedence: 90,
},
OperatorInfo {
op_type: "Math".into(),
operator: "*".into(),
name: "Multiply".into(),
description: "Multiplies two values.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "/".into(),
name: "Divide".into(),
description: "Divides two values.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "//".into(),
name: "FloorDivision".into(),
description: "Divides two values and floors the result.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "mod".into(),
name: "Modulo".into(),
description: "Divides two values and returns the remainder.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "**".into(),
name: "Pow ".into(),
description: "Raises one value to the power of another.".into(),
precedence: 100,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-or".into(),
name: "BitOr".into(),
description: "Performs a bitwise OR on two values.".into(),
precedence: 60,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-xor".into(),
name: "BitXor".into(),
description: "Performs a bitwise XOR on two values.".into(),
precedence: 70,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-and".into(),
name: "BitAnd".into(),
description: "Performs a bitwise AND on two values.".into(),
precedence: 75,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-shl".into(),
name: "ShiftLeft".into(),
description: "Shifts a value left by another.".into(),
precedence: 85,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-shr".into(),
name: "ShiftRight".into(),
description: "Shifts a value right by another.".into(),
precedence: 85,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "&&".into(),
name: "And".into(),
description: "Deprecated. Checks if two values are true.".into(),
precedence: 50,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "and".into(),
name: "And".into(),
description: "Checks if two values are true.".into(),
precedence: 50,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "||".into(),
name: "Or".into(),
description: "Deprecated. Checks if either value is true.".into(),
precedence: 40,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "or".into(),
name: "Or".into(),
description: "Checks if either value is true.".into(),
precedence: 40,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "xor".into(),
name: "Xor".into(),
description: "Checks if one value is true and the other is false.".into(),
precedence: 45,
},
]
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpOperators;
use crate::test_examples;
test_examples(HelpOperators {})
}
}

View File

@ -1,7 +1,7 @@
use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -14,6 +14,7 @@ impl Command for Hide {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("hide")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
.category(Category::Core)
}

View File

@ -3,7 +3,7 @@ use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
did_you_mean, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Value,
SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -16,6 +16,7 @@ impl Command for HideEnv {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("hide-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.rest(
"name",
SyntaxShape::String,

View File

@ -1,8 +1,8 @@
use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, FromValue, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -19,10 +19,11 @@ impl Command for If {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("if")
.input_output_types(vec![(Type::Any, Type::Any)])
.required("cond", SyntaxShape::Expression, "condition to check")
.required(
"then_block",
SyntaxShape::Block(Some(vec![])),
SyntaxShape::Block,
"block to run if check succeeds",
)
.optional(
@ -50,7 +51,7 @@ impl Command for If {
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let cond = call.positional_nth(0).expect("checked through parser");
let then_block: CaptureBlock = call.req(engine_state, stack, 1)?;
let then_block: Block = call.req(engine_state, stack, 1)?;
let else_case = call.positional_nth(2);
let result = eval_expression(engine_state, stack, cond)?;
@ -58,10 +59,9 @@ impl Command for If {
Value::Bool { val, .. } => {
if *val {
let block = engine_state.get_block(then_block.block_id);
let mut stack = stack.captures_to_stack(&then_block.captures);
eval_block(
engine_state,
&mut stack,
stack,
block,
input,
call.redirect_stdout,
@ -70,14 +70,10 @@ impl Command for If {
} else if let Some(else_case) = else_case {
if let Some(else_expr) = else_case.as_keyword() {
if let Some(block_id) = else_expr.as_block() {
let result = eval_expression(engine_state, stack, else_expr)?;
let else_block: CaptureBlock = FromValue::from_value(&result)?;
let mut stack = stack.captures_to_stack(&else_block.captures);
let block = engine_state.get_block(block_id);
eval_block(
engine_state,
&mut stack,
stack,
block,
input,
call.redirect_stdout,

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, Type, Value};
#[derive(Clone)]
pub struct Ignore;
@ -15,7 +15,9 @@ impl Command for Ignore {
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("ignore").category(Category::Core)
Signature::build("ignore")
.input_output_types(vec![(Type::Any, Type::Nothing)])
.category(Category::Core)
}
fn search_terms(&self) -> Vec<&str> {
@ -37,7 +39,7 @@ impl Command for Ignore {
vec![Example {
description: "Ignore the output of an echo command",
example: "echo done | ignore",
result: None,
result: Some(Value::nothing(Span::test_data())),
}]
}
}

View File

@ -1,7 +1,7 @@
use nu_engine::eval_expression_with_input;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Let;
@ -17,6 +17,8 @@ impl Command for Let {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("let")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
@ -97,6 +99,8 @@ impl Command for Let {
#[cfg(test)]
mod test {
use nu_protocol::engine::CommandType;
use super::*;
#[test]
@ -105,4 +109,9 @@ mod test {
test_examples(Let {})
}
#[test]
fn test_command_type() {
assert!(matches!(Let.command_type(), CommandType::Keyword));
}
}

View File

@ -0,0 +1,101 @@
use std::sync::atomic::Ordering;
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct Loop;
impl Command for Loop {
fn name(&self) -> &str {
"loop"
}
fn usage(&self) -> &str {
"Run a block in a loop."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("loop")
.required("block", SyntaxShape::Block, "block to loop")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let block: Block = call.req(engine_state, stack, 0)?;
loop {
if let Some(ctrlc) = &engine_state.ctrlc {
if ctrlc.load(Ordering::SeqCst) {
break;
}
}
let block = engine_state.get_block(block.block_id);
match eval_block(
engine_state,
stack,
block,
PipelineData::new(call.head),
call.redirect_stdout,
call.redirect_stderr,
) {
Err(ShellError::Break(_)) => {
break;
}
Err(ShellError::Continue(_)) => {
continue;
}
Err(err) => {
return Err(err);
}
Ok(pipeline) => {
pipeline.into_value(call.head);
}
}
}
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Loop while a condition is true",
example: "mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $x",
result: Some(Value::Int {
val: 11,
span: Span::test_data(),
}),
}]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Loop {})
}
}

View File

@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata, Signature,
Span, SyntaxShape, Value,
Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -20,6 +20,8 @@ impl Command for Metadata {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("metadata")
.input_output_types(vec![(Type::Nothing, Type::Record(vec![]))])
.allow_variants_without_examples(true)
.optional(
"expression",
SyntaxShape::Any,
@ -84,6 +86,15 @@ impl Command for Metadata {
span: head,
})
}
PipelineMetadata {
data_source: DataSource::HtmlThemes,
} => {
cols.push("source".into());
vals.push(Value::String {
val: "into html --list".into(),
span: head,
})
}
}
}
@ -146,6 +157,15 @@ fn build_metadata_record(arg: &Value, metadata: &Option<PipelineMetadata>, head:
span: head,
})
}
PipelineMetadata {
data_source: DataSource::HtmlThemes,
} => {
cols.push("source".into());
vals.push(Value::String {
val: "into html --list".into(),
span: head,
})
}
}
}

View File

@ -1,6 +1,8 @@
mod alias;
mod ast;
mod break_;
mod commandline;
mod continue_;
mod debug;
mod def;
mod def_env;
@ -17,20 +19,28 @@ mod export_use;
mod extern_;
mod for_;
pub mod help;
mod help_operators;
mod hide;
mod hide_env;
mod if_;
mod ignore;
mod let_;
mod loop_;
mod metadata;
mod module;
mod mut_;
pub(crate) mod overlay;
mod return_;
mod try_;
mod use_;
mod version;
mod while_;
pub use alias::Alias;
pub use ast::Ast;
pub use break_::Break;
pub use commandline::Commandline;
pub use continue_::Continue;
pub use debug::Debug;
pub use def::Def;
pub use def_env::DefEnv;
@ -47,16 +57,22 @@ pub use export_use::ExportUse;
pub use extern_::Extern;
pub use for_::For;
pub use help::Help;
pub use help_operators::HelpOperators;
pub use hide::Hide;
pub use hide_env::HideEnv;
pub use if_::If;
pub use ignore::Ignore;
pub use let_::Let;
pub use loop_::Loop;
pub use metadata::Metadata;
pub use module::Module;
pub use mut_::Mut;
pub use overlay::*;
pub use return_::Return;
pub use try_::Try;
pub use use_::Use;
pub use version::Version;
pub use while_::While;
#[cfg(feature = "plugin")]
mod register;

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct Module;
@ -16,12 +16,9 @@ impl Command for Module {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("module")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("module_name", SyntaxShape::String, "module name")
.required(
"block",
SyntaxShape::Block(Some(vec![])),
"body of the module",
)
.required("block", SyntaxShape::Block, "body of the module")
.category(Category::Core)
}

View File

@ -0,0 +1,117 @@
use nu_engine::eval_expression_with_input;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Mut;
impl Command for Mut {
fn name(&self) -> &str {
"mut"
}
fn usage(&self) -> &str {
"Create a mutable variable and give it a value."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("mut")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn search_terms(&self) -> Vec<&str> {
vec!["set", "mutable"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let var_id = call
.positional_nth(0)
.expect("checked through parser")
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call
.positional_nth(1)
.expect("checked through parser")
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression_with_input(
engine_state,
stack,
keyword_expr,
input,
call.redirect_stdout,
call.redirect_stderr,
)?
.0;
//println!("Adding: {:?} to {}", rhs, var_id);
stack.add_var(var_id, rhs.into_value(call.head));
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Set a mutable variable to a value, then update it",
example: "mut x = 10; $x = 12",
result: None,
},
Example {
description: "Set a mutable variable to the result of an expression",
example: "mut x = 10 + 100",
result: None,
},
Example {
description: "Set a mutable variable based on the condition",
example: "mut x = if false { -1 } else { 1 }",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use nu_protocol::engine::CommandType;
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Mut {})
}
#[test]
fn test_command_type() {
assert!(matches!(Mut.command_type(), CommandType::Keyword));
}
}

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