2024-05-13 13:37:53 +00:00
|
|
|
use crate::repl::tests::{fail_test, run_test, TestResult};
|
Parameter defaults to $nu.scope.commands (#9152)
(*third* try at posting this PR, #9104, like #9084, got polluted with
unrelated commits. I'm never going to pull from the github feature
branch again!)
# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.
Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
Show parameter defaults in scope command signature, where they're
available for display by help.
per https://github.com/nushell/nushell/issues/8928.
I found unexpected ramifications in one completer (NuHelpCompleter) and
plugins, which both use the flag-formatting routine from builtin help.
For the moment I made the minimum necessary changes to get the mainline
scenario to pass tests and run. But we should circle back on what to do
with plugins and help completer..
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
1. New `parameter_default` column to `signatures` table in
`$nu.scope.commands`
It is populated with whatever parameters can be defaulted: currently
positional args and named flags.
2. Built in help (both `help <command>` and `<command> --help` will
display the defaults
3. Help completer will display defaults for flags, but not for
positionals.
Example:
A custom command with some default parameters:
```
〉cat ~/work/dflts.nu
# sample function to show defaults in help
export def main [
arg1: string # mandatory positional
arg2:string=abc # optional positional
--switch # no default here
--named:int # named flag, no default
--other:string=def # flag
--hard:record<foo:int bar:string, bas:bool> # default can be compound type
= {foo:22, bar:"other worlds", bas:false}
] { {arg1: $arg1,
arg2: $arg2,
switch: $switch,
named: $named,
other: $other,
hard: $hard, }
}
〉use ~/work/dflts.nu
〉$nu.scope.commands | where name == 'dflts' | get signatures.0.any | reject short_flag description custom_completion
╭───┬────────────────┬────────────────┬──────────────────────────────────────────┬─────────────┬───────────────────────────╮
│ # │ parameter_name │ parameter_type │ syntax_shape │ is_optional │ parameter_default │
├───┼────────────────┼────────────────┼──────────────────────────────────────────┼─────────────┼───────────────────────────┤
│ 0 │ │ input │ any │ false │ │
│ 1 │ arg1 │ positional │ string │ false │ │
│ 2 │ arg2 │ positional │ string │ true │ abc │
│ 3 │ switch │ switch │ │ true │ │
│ 4 │ named │ named │ int │ true │ │
│ 5 │ other │ named │ string │ true │ def │
│ 6 │ hard │ named │ record<foo: int, bar: string, bas: bool> │ true │ ╭───────┬───────────────╮ │
│ │ │ │ │ │ │ foo │ 22 │ │
│ │ │ │ │ │ │ bar │ other worlds │ │
│ │ │ │ │ │ │ bas │ false │ │
│ │ │ │ │ │ ╰───────┴───────────────╯ │
│ 7 │ │ output │ any │ false │ │
╰───┴────────────────┴────────────────┴──────────────────────────────────────────┴─────────────┴───────────────────────────╯
〉help dflts
sample function to show defaults in help
Usage:
> dflts {flags} <arg1> (arg2)
Flags:
--switch - switch -- no default here
--named <Int> - named flag, typed, but no default
--other <String> - flag with default (default: 'def')
--hard <Record([("foo", Int), ("bar", String), ("bas", Boolean)])> - default can be compound type (default: {foo: 22, bar: 'other worlds', bas: false})
-h, --help - Display the help message for this command
Parameters:
arg1 <string>: mandatory positional
arg2 <string>: optional positional (optional, default: 'abc')
```
Compared to (relevant bits of) help output previously:
```
Flags:
-h, --help - Display the help message for this command
-, --switch - no default here
-, --named <int> - named flag, no default
-, --other <string> - flag
-, --hard <record<foo: int, bar: string, bas: bool>> - default can be compound type
Signatures:
<any> | dflts <string> <string> -> <any>
Parameters:
arg1 <string>: mandatory positional
(optional) arg2 <string>: optional positional
```
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> [x] toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-11 18:59:56 +00:00
|
|
|
use rstest::rstest;
|
2021-12-25 19:39:42 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn concrete_variable_assignment() -> TestResult {
|
|
|
|
run_test(
|
2021-12-26 20:21:24 +00:00
|
|
|
"let x = (1..100 | each { |y| $y + 100 }); let y = ($x | length); $x | length",
|
2021-12-25 19:39:42 +00:00
|
|
|
"100",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn proper_shadow() -> TestResult {
|
|
|
|
run_test("let x = 10; let x = $x + 9; $x", "19")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_variable_1() -> TestResult {
|
|
|
|
run_test(r#"[3] | if $in.0 > 4 { "yay!" } else { "boo" }"#, "boo")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_variable_2() -> TestResult {
|
|
|
|
run_test(r#"3 | if $in > 2 { "yay!" } else { "boo" }"#, "yay!")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_variable_3() -> TestResult {
|
|
|
|
run_test(r#"3 | if $in > 4 { "yay!" } else { $in }"#, "3")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_variable_4() -> TestResult {
|
|
|
|
run_test(r#"3 | do { $in }"#, "3")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_variable_5() -> TestResult {
|
|
|
|
run_test(r#"3 | if $in > 2 { $in - 10 } else { $in * 10 }"#, "-7")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_variable_6() -> TestResult {
|
|
|
|
run_test(r#"3 | if $in > 6 { $in - 10 } else { $in * 10 }"#, "30")
|
|
|
|
}
|
|
|
|
|
2022-04-30 21:13:21 +00:00
|
|
|
#[test]
|
|
|
|
fn in_and_if_else() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"[1, 2, 3] | if false {} else if true { $in | length }"#,
|
|
|
|
"3",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
Overhaul `$in` expressions (#13357)
# Description
This grew quite a bit beyond its original scope, but I've tried to make
`$in` a bit more consistent and easier to work with.
Instead of the parser generating calls to `collect` and creating
closures, this adds `Expr::Collect` which just evaluates in the same
scope and doesn't require any closure.
When `$in` is detected in an expression, it is replaced with a new
variable (also called `$in`) and wrapped in `Expr::Collect`. During
eval, this expression is evaluated directly, with the input and with
that new variable set to the collected value.
Other than being faster and less prone to gotchas, it also makes it
possible to typecheck the output of an expression containing `$in`,
which is nice. This is a breaking change though, because of the lack of
the closure and because now typechecking will actually happen. Also, I
haven't attempted to typecheck the input yet.
The IR generated now just looks like this:
```gas
collect %in
clone %tmp, %in
store-variable $in, %tmp
# %out <- ...expression... <- %in
drop-variable $in
```
(where `$in` is the local variable created for this collection, and not
`IN_VARIABLE_ID`)
which is a lot better than having to create a closure and call `collect
--keep-env`, dealing with all of the capture gathering and allocation
that entails. Ideally we can also detect whether that input is actually
needed, so maybe we don't have to clone, but I haven't tried to do that
yet. Theoretically now that the variable is a unique one every time, it
should be possible to give it a type - I just don't know how to
determine that yet.
On top of that, I've also reworked how `$in` works in pipeline-initial
position. Previously, it was a little bit inconsistent. For example,
this worked:
```nushell
> 3 | do { let x = $in; let y = $in; print $x $y }
3
3
```
However, this causes a runtime variable not found error on the second
`$in`:
```nushell
> def foo [] { let x = $in; let y = $in; print $x $y }; 3 | foo
Error: nu::shell::variable_not_found
× Variable not found
╭─[entry #115:1:35]
1 │ def foo [] { let x = $in; let y = $in; print $x $y }; 3 | foo
· ─┬─
· ╰── variable not found
╰────
```
I've fixed this by making the first element `$in` detection *always*
happen at the block level, so if you use `$in` in pipeline-initial
position anywhere in a block, it will collect with an implicit
subexpression around the whole thing, and you can then use that `$in`
more than once. In doing this I also rewrote `parse_pipeline()` and
hopefully it's a bit more straightforward and possibly more efficient
too now.
Finally, I've tried to make `let` and `mut` a lot more straightforward
with how they handle the rest of the pipeline, and using a redirection
with `let`/`mut` now does what you'd expect if you assume that they
consume the whole pipeline - the redirection is just processed as
normal. These both work now:
```nushell
let x = ^foo err> err.txt
let y = ^foo out+err>| str length
```
It was previously possible to accomplish this with a subexpression, but
it just seemed like a weird gotcha that you couldn't do it. Intuitively,
`let` and `mut` just seem to take the whole line.
- closes #13137
# User-Facing Changes
- `$in` will behave more consistently with blocks and closures, since
the entire block is now just wrapped to handle it if it appears in the
first pipeline element
- `$in` no longer creates a closure, so what can be done within an
expression containing `$in` is less restrictive
- `$in` containing expressions are now type checked, rather than just
resulting in `any`. However, `$in` itself is still `any`, so this isn't
quite perfect yet
- Redirections are now allowed in `let` and `mut` and behave pretty much
how you'd expect
# Tests + Formatting
Added tests to cover the new behaviour.
# After Submitting
- [ ] release notes (definitely breaking change)
2024-07-17 21:02:42 +00:00
|
|
|
#[test]
|
|
|
|
fn in_with_closure() -> TestResult {
|
|
|
|
// Can use $in twice
|
|
|
|
run_test(r#"3 | do { let x = $in; let y = $in; $x + $y }"#, "6")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_with_custom_command() -> TestResult {
|
|
|
|
// Can use $in twice
|
|
|
|
run_test(
|
|
|
|
r#"def foo [] { let x = $in; let y = $in; $x + $y }; 3 | foo"#,
|
|
|
|
"6",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_used_twice_and_also_in_pipeline() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"3 | do { let x = $in; let y = $in; $x + $y | $in * 4 }"#,
|
|
|
|
"24",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-07-25 10:28:44 +00:00
|
|
|
// #13441
|
|
|
|
#[test]
|
|
|
|
fn in_used_in_range_from() -> TestResult {
|
|
|
|
run_test(r#"6 | $in..10 | math sum"#, "40")
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn in_used_in_range_to() -> TestResult {
|
|
|
|
run_test(r#"6 | 3..$in | math sum"#, "18")
|
|
|
|
}
|
|
|
|
|
2021-12-25 19:39:42 +00:00
|
|
|
#[test]
|
|
|
|
fn help_works_with_missing_requirements() -> TestResult {
|
2024-05-19 17:56:33 +00:00
|
|
|
run_test(r#"each --help | lines | length"#, "72")
|
2021-12-25 19:39:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn scope_variable() -> TestResult {
|
2022-01-24 14:19:38 +00:00
|
|
|
run_test(
|
2023-06-20 21:33:01 +00:00
|
|
|
r#"let x = 3; scope variables | where name == "$x" | get type.0"#,
|
2022-01-24 14:19:38 +00:00
|
|
|
"int",
|
|
|
|
)
|
2021-12-25 19:39:42 +00:00
|
|
|
}
|
2024-05-19 17:56:33 +00:00
|
|
|
|
Parameter defaults to $nu.scope.commands (#9152)
(*third* try at posting this PR, #9104, like #9084, got polluted with
unrelated commits. I'm never going to pull from the github feature
branch again!)
# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.
Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
Show parameter defaults in scope command signature, where they're
available for display by help.
per https://github.com/nushell/nushell/issues/8928.
I found unexpected ramifications in one completer (NuHelpCompleter) and
plugins, which both use the flag-formatting routine from builtin help.
For the moment I made the minimum necessary changes to get the mainline
scenario to pass tests and run. But we should circle back on what to do
with plugins and help completer..
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
1. New `parameter_default` column to `signatures` table in
`$nu.scope.commands`
It is populated with whatever parameters can be defaulted: currently
positional args and named flags.
2. Built in help (both `help <command>` and `<command> --help` will
display the defaults
3. Help completer will display defaults for flags, but not for
positionals.
Example:
A custom command with some default parameters:
```
〉cat ~/work/dflts.nu
# sample function to show defaults in help
export def main [
arg1: string # mandatory positional
arg2:string=abc # optional positional
--switch # no default here
--named:int # named flag, no default
--other:string=def # flag
--hard:record<foo:int bar:string, bas:bool> # default can be compound type
= {foo:22, bar:"other worlds", bas:false}
] { {arg1: $arg1,
arg2: $arg2,
switch: $switch,
named: $named,
other: $other,
hard: $hard, }
}
〉use ~/work/dflts.nu
〉$nu.scope.commands | where name == 'dflts' | get signatures.0.any | reject short_flag description custom_completion
╭───┬────────────────┬────────────────┬──────────────────────────────────────────┬─────────────┬───────────────────────────╮
│ # │ parameter_name │ parameter_type │ syntax_shape │ is_optional │ parameter_default │
├───┼────────────────┼────────────────┼──────────────────────────────────────────┼─────────────┼───────────────────────────┤
│ 0 │ │ input │ any │ false │ │
│ 1 │ arg1 │ positional │ string │ false │ │
│ 2 │ arg2 │ positional │ string │ true │ abc │
│ 3 │ switch │ switch │ │ true │ │
│ 4 │ named │ named │ int │ true │ │
│ 5 │ other │ named │ string │ true │ def │
│ 6 │ hard │ named │ record<foo: int, bar: string, bas: bool> │ true │ ╭───────┬───────────────╮ │
│ │ │ │ │ │ │ foo │ 22 │ │
│ │ │ │ │ │ │ bar │ other worlds │ │
│ │ │ │ │ │ │ bas │ false │ │
│ │ │ │ │ │ ╰───────┴───────────────╯ │
│ 7 │ │ output │ any │ false │ │
╰───┴────────────────┴────────────────┴──────────────────────────────────────────┴─────────────┴───────────────────────────╯
〉help dflts
sample function to show defaults in help
Usage:
> dflts {flags} <arg1> (arg2)
Flags:
--switch - switch -- no default here
--named <Int> - named flag, typed, but no default
--other <String> - flag with default (default: 'def')
--hard <Record([("foo", Int), ("bar", String), ("bas", Boolean)])> - default can be compound type (default: {foo: 22, bar: 'other worlds', bas: false})
-h, --help - Display the help message for this command
Parameters:
arg1 <string>: mandatory positional
arg2 <string>: optional positional (optional, default: 'abc')
```
Compared to (relevant bits of) help output previously:
```
Flags:
-h, --help - Display the help message for this command
-, --switch - no default here
-, --named <int> - named flag, no default
-, --other <string> - flag
-, --hard <record<foo: int, bar: string, bas: bool>> - default can be compound type
Signatures:
<any> | dflts <string> <string> -> <any>
Parameters:
arg1 <string>: mandatory positional
(optional) arg2 <string>: optional positional
```
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> [x] toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-11 18:59:56 +00:00
|
|
|
#[rstest]
|
|
|
|
#[case("a", "<> nothing")]
|
|
|
|
#[case("b", "<1.23> float")]
|
|
|
|
#[case("flag1", "<> nothing")]
|
|
|
|
#[case("flag2", "<4.56> float")]
|
|
|
|
fn scope_command_defaults(#[case] var: &str, #[case] exp_result: &str) -> TestResult {
|
|
|
|
run_test(
|
|
|
|
&format!(
|
2023-08-26 13:41:29 +00:00
|
|
|
r#"def t1 [a:int b?:float=1.23 --flag1:string --flag2:float=4.56] {{ true }};
|
2023-06-20 21:33:01 +00:00
|
|
|
let rslt = (scope commands | where name == 't1' | get signatures.0.any | where parameter_name == '{var}' | get parameter_default.0);
|
Parameter defaults to $nu.scope.commands (#9152)
(*third* try at posting this PR, #9104, like #9084, got polluted with
unrelated commits. I'm never going to pull from the github feature
branch again!)
# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.
Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
Show parameter defaults in scope command signature, where they're
available for display by help.
per https://github.com/nushell/nushell/issues/8928.
I found unexpected ramifications in one completer (NuHelpCompleter) and
plugins, which both use the flag-formatting routine from builtin help.
For the moment I made the minimum necessary changes to get the mainline
scenario to pass tests and run. But we should circle back on what to do
with plugins and help completer..
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
1. New `parameter_default` column to `signatures` table in
`$nu.scope.commands`
It is populated with whatever parameters can be defaulted: currently
positional args and named flags.
2. Built in help (both `help <command>` and `<command> --help` will
display the defaults
3. Help completer will display defaults for flags, but not for
positionals.
Example:
A custom command with some default parameters:
```
〉cat ~/work/dflts.nu
# sample function to show defaults in help
export def main [
arg1: string # mandatory positional
arg2:string=abc # optional positional
--switch # no default here
--named:int # named flag, no default
--other:string=def # flag
--hard:record<foo:int bar:string, bas:bool> # default can be compound type
= {foo:22, bar:"other worlds", bas:false}
] { {arg1: $arg1,
arg2: $arg2,
switch: $switch,
named: $named,
other: $other,
hard: $hard, }
}
〉use ~/work/dflts.nu
〉$nu.scope.commands | where name == 'dflts' | get signatures.0.any | reject short_flag description custom_completion
╭───┬────────────────┬────────────────┬──────────────────────────────────────────┬─────────────┬───────────────────────────╮
│ # │ parameter_name │ parameter_type │ syntax_shape │ is_optional │ parameter_default │
├───┼────────────────┼────────────────┼──────────────────────────────────────────┼─────────────┼───────────────────────────┤
│ 0 │ │ input │ any │ false │ │
│ 1 │ arg1 │ positional │ string │ false │ │
│ 2 │ arg2 │ positional │ string │ true │ abc │
│ 3 │ switch │ switch │ │ true │ │
│ 4 │ named │ named │ int │ true │ │
│ 5 │ other │ named │ string │ true │ def │
│ 6 │ hard │ named │ record<foo: int, bar: string, bas: bool> │ true │ ╭───────┬───────────────╮ │
│ │ │ │ │ │ │ foo │ 22 │ │
│ │ │ │ │ │ │ bar │ other worlds │ │
│ │ │ │ │ │ │ bas │ false │ │
│ │ │ │ │ │ ╰───────┴───────────────╯ │
│ 7 │ │ output │ any │ false │ │
╰───┴────────────────┴────────────────┴──────────────────────────────────────────┴─────────────┴───────────────────────────╯
〉help dflts
sample function to show defaults in help
Usage:
> dflts {flags} <arg1> (arg2)
Flags:
--switch - switch -- no default here
--named <Int> - named flag, typed, but no default
--other <String> - flag with default (default: 'def')
--hard <Record([("foo", Int), ("bar", String), ("bas", Boolean)])> - default can be compound type (default: {foo: 22, bar: 'other worlds', bas: false})
-h, --help - Display the help message for this command
Parameters:
arg1 <string>: mandatory positional
arg2 <string>: optional positional (optional, default: 'abc')
```
Compared to (relevant bits of) help output previously:
```
Flags:
-h, --help - Display the help message for this command
-, --switch - no default here
-, --named <int> - named flag, no default
-, --other <string> - flag
-, --hard <record<foo: int, bar: string, bas: bool>> - default can be compound type
Signatures:
<any> | dflts <string> <string> -> <any>
Parameters:
arg1 <string>: mandatory positional
(optional) arg2 <string>: optional positional
```
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> [x] toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-11 18:59:56 +00:00
|
|
|
$"<($rslt)> ($rslt | describe)""#
|
|
|
|
),
|
2023-05-24 22:58:18 +00:00
|
|
|
exp_result,
|
Parameter defaults to $nu.scope.commands (#9152)
(*third* try at posting this PR, #9104, like #9084, got polluted with
unrelated commits. I'm never going to pull from the github feature
branch again!)
# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.
Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
Show parameter defaults in scope command signature, where they're
available for display by help.
per https://github.com/nushell/nushell/issues/8928.
I found unexpected ramifications in one completer (NuHelpCompleter) and
plugins, which both use the flag-formatting routine from builtin help.
For the moment I made the minimum necessary changes to get the mainline
scenario to pass tests and run. But we should circle back on what to do
with plugins and help completer..
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
1. New `parameter_default` column to `signatures` table in
`$nu.scope.commands`
It is populated with whatever parameters can be defaulted: currently
positional args and named flags.
2. Built in help (both `help <command>` and `<command> --help` will
display the defaults
3. Help completer will display defaults for flags, but not for
positionals.
Example:
A custom command with some default parameters:
```
〉cat ~/work/dflts.nu
# sample function to show defaults in help
export def main [
arg1: string # mandatory positional
arg2:string=abc # optional positional
--switch # no default here
--named:int # named flag, no default
--other:string=def # flag
--hard:record<foo:int bar:string, bas:bool> # default can be compound type
= {foo:22, bar:"other worlds", bas:false}
] { {arg1: $arg1,
arg2: $arg2,
switch: $switch,
named: $named,
other: $other,
hard: $hard, }
}
〉use ~/work/dflts.nu
〉$nu.scope.commands | where name == 'dflts' | get signatures.0.any | reject short_flag description custom_completion
╭───┬────────────────┬────────────────┬──────────────────────────────────────────┬─────────────┬───────────────────────────╮
│ # │ parameter_name │ parameter_type │ syntax_shape │ is_optional │ parameter_default │
├───┼────────────────┼────────────────┼──────────────────────────────────────────┼─────────────┼───────────────────────────┤
│ 0 │ │ input │ any │ false │ │
│ 1 │ arg1 │ positional │ string │ false │ │
│ 2 │ arg2 │ positional │ string │ true │ abc │
│ 3 │ switch │ switch │ │ true │ │
│ 4 │ named │ named │ int │ true │ │
│ 5 │ other │ named │ string │ true │ def │
│ 6 │ hard │ named │ record<foo: int, bar: string, bas: bool> │ true │ ╭───────┬───────────────╮ │
│ │ │ │ │ │ │ foo │ 22 │ │
│ │ │ │ │ │ │ bar │ other worlds │ │
│ │ │ │ │ │ │ bas │ false │ │
│ │ │ │ │ │ ╰───────┴───────────────╯ │
│ 7 │ │ output │ any │ false │ │
╰───┴────────────────┴────────────────┴──────────────────────────────────────────┴─────────────┴───────────────────────────╯
〉help dflts
sample function to show defaults in help
Usage:
> dflts {flags} <arg1> (arg2)
Flags:
--switch - switch -- no default here
--named <Int> - named flag, typed, but no default
--other <String> - flag with default (default: 'def')
--hard <Record([("foo", Int), ("bar", String), ("bas", Boolean)])> - default can be compound type (default: {foo: 22, bar: 'other worlds', bas: false})
-h, --help - Display the help message for this command
Parameters:
arg1 <string>: mandatory positional
arg2 <string>: optional positional (optional, default: 'abc')
```
Compared to (relevant bits of) help output previously:
```
Flags:
-h, --help - Display the help message for this command
-, --switch - no default here
-, --named <int> - named flag, no default
-, --other <string> - flag
-, --hard <record<foo: int, bar: string, bas: bool>> - default can be compound type
Signatures:
<any> | dflts <string> <string> -> <any>
Parameters:
arg1 <string>: mandatory positional
(optional) arg2 <string>: optional positional
```
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> [x] toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-05-11 18:59:56 +00:00
|
|
|
)
|
|
|
|
}
|
2021-12-25 19:39:42 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn earlier_errors() -> TestResult {
|
|
|
|
fail_test(
|
2022-02-17 11:40:24 +00:00
|
|
|
r#"[1, "bob"] | each { |it| $it + 3 } | each { |it| $it / $it } | table"#,
|
2021-12-25 19:39:42 +00:00
|
|
|
"int",
|
|
|
|
)
|
|
|
|
}
|
2022-01-06 20:32:47 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_flags_are_nothing() -> TestResult {
|
|
|
|
run_test(
|
2022-12-22 20:30:10 +00:00
|
|
|
r#"def foo [--aaa(-a): int, --bbb(-b): int] { (if $aaa == null { 10 } else { $aaa }) + (if $bbb == null { 100 } else { $bbb }) }; foo"#,
|
2022-01-06 20:32:47 +00:00
|
|
|
"110",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_flags_are_nothing2() -> TestResult {
|
|
|
|
run_test(
|
2022-12-22 20:30:10 +00:00
|
|
|
r#"def foo [--aaa(-a): int, --bbb(-b): int] { (if $aaa == null { 10 } else { $aaa }) + (if $bbb == null { 100 } else { $bbb }) }; foo -a 90"#,
|
2022-01-06 20:32:47 +00:00
|
|
|
"190",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_flags_are_nothing3() -> TestResult {
|
|
|
|
run_test(
|
2022-12-22 20:30:10 +00:00
|
|
|
r#"def foo [--aaa(-a): int, --bbb(-b): int] { (if $aaa == null { 10 } else { $aaa }) + (if $bbb == null { 100 } else { $bbb }) }; foo -b 45"#,
|
2022-01-06 20:32:47 +00:00
|
|
|
"55",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_flags_are_nothing4() -> TestResult {
|
|
|
|
run_test(
|
2022-12-22 20:30:10 +00:00
|
|
|
r#"def foo [--aaa(-a): int, --bbb(-b): int] { (if $aaa == null { 10 } else { $aaa }) + (if $bbb == null { 100 } else { $bbb }) }; foo -a 3 -b 10000"#,
|
2022-01-06 20:32:47 +00:00
|
|
|
"10003",
|
|
|
|
)
|
|
|
|
}
|
2022-01-12 04:06:56 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn proper_variable_captures() -> TestResult {
|
|
|
|
run_test(
|
2022-11-10 08:21:49 +00:00
|
|
|
r#"def foo [x] { let y = 100; { || $y + $x } }; do (foo 23)"#,
|
2022-01-12 04:06:56 +00:00
|
|
|
"123",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn proper_variable_captures_with_calls() -> TestResult {
|
|
|
|
run_test(
|
Restrict closure expression to be something like `{|| ...}` (#8290)
# Description
As title, closes: #7921 closes: #8273
# User-Facing Changes
when define a closure without pipe, nushell will raise error for now:
```
❯ let x = {ss ss}
Error: nu::parser::closure_missing_pipe
× Missing || inside closure
╭─[entry #2:1:1]
1 │ let x = {ss ss}
· ───┬───
· ╰── Parsing as a closure, but || is missing
╰────
help: Try add || to the beginning of closure
```
`any`, `each`, `all`, `where` command accepts closure, it forces user
input closure like `{||`, or parse error will returned.
```
❯ {major:2, minor:1, patch:4} | values | each { into string }
Error: nu::parser::closure_missing_pipe
× Missing || inside closure
╭─[entry #4:1:1]
1 │ {major:2, minor:1, patch:4} | values | each { into string }
· ───────┬───────
· ╰── Parsing as a closure, but || is missing
╰────
help: Try add || to the beginning of closure
```
`with-env`, `do`, `def`, `try` are special, they still remain the same,
although it says that it accepts a closure, but they don't need to be
written like `{||`, it's more likely a block but can capture variable
outside of scope:
```
❯ def test [input] { echo [0 1 2] | do { do { echo $input } } }; test aaa
aaa
```
Just realize that It's a big breaking change, we need to update config
and scripts...
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-03-17 12:36:28 +00:00
|
|
|
r#"def foo [] { let y = 60; def bar [] { $y }; {|| bar } }; do (foo)"#,
|
2022-01-12 04:06:56 +00:00
|
|
|
"60",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn proper_variable_captures_with_nesting() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def foo [x] { let z = 100; def bar [y] { $y - $x + $z } ; { |z| bar $z } }; do (foo 11) 13"#,
|
|
|
|
"102",
|
|
|
|
)
|
|
|
|
}
|
2022-01-15 15:26:52 +00:00
|
|
|
|
2022-01-20 18:23:26 +00:00
|
|
|
#[test]
|
|
|
|
fn divide_duration() -> TestResult {
|
|
|
|
run_test(r#"4ms / 4ms"#, "1")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn divide_filesize() -> TestResult {
|
|
|
|
run_test(r#"4mb / 4mb"#, "1")
|
|
|
|
}
|
2022-01-24 21:55:45 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn date_comparison() -> TestResult {
|
|
|
|
run_test(r#"(date now) < ((date now) + 2min)"#, "true")
|
|
|
|
}
|
2022-01-26 19:00:25 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn let_sees_input() -> TestResult {
|
|
|
|
run_test(
|
2023-03-22 20:14:10 +00:00
|
|
|
r#"def c [] { let x = (str length); $x }; "hello world" | c"#,
|
2022-01-26 19:00:25 +00:00
|
|
|
"11",
|
|
|
|
)
|
|
|
|
}
|
2022-01-26 23:46:13 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn let_sees_in_variable() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def c [] { let x = $in.name; $x | str length }; {name: bob, size: 100 } | c"#,
|
|
|
|
"3",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn let_sees_in_variable2() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def c [] { let x = ($in | str length); $x }; 'bob' | c"#,
|
|
|
|
"3",
|
|
|
|
)
|
|
|
|
}
|
2022-01-29 20:45:46 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn def_env() -> TestResult {
|
|
|
|
run_test(
|
2023-10-02 18:13:31 +00:00
|
|
|
r#"def --env bob [] { $env.BAR = "BAZ" }; bob; $env.BAR"#,
|
2022-01-29 20:45:46 +00:00
|
|
|
"BAZ",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn not_def_env() -> TestResult {
|
2023-06-30 19:57:51 +00:00
|
|
|
fail_test(r#"def bob [] { $env.BAR = "BAZ" }; bob; $env.BAR"#, "")
|
2022-01-29 20:45:46 +00:00
|
|
|
}
|
|
|
|
|
2022-02-04 18:02:03 +00:00
|
|
|
#[test]
|
|
|
|
fn def_env_hiding_something() -> TestResult {
|
|
|
|
fail_test(
|
2023-10-02 18:13:31 +00:00
|
|
|
r#"$env.FOO = "foo"; def --env bob [] { hide-env FOO }; bob; $env.FOO"#,
|
2022-09-21 00:46:01 +00:00
|
|
|
"",
|
2022-02-04 18:02:03 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn def_env_then_hide() -> TestResult {
|
|
|
|
fail_test(
|
2023-10-02 18:13:31 +00:00
|
|
|
r#"def --env bob [] { $env.BOB = "bob" }; def --env un-bob [] { hide-env BOB }; bob; un-bob; $env.BOB"#,
|
2022-09-21 00:46:01 +00:00
|
|
|
"",
|
2022-02-04 18:02:03 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-29 20:45:46 +00:00
|
|
|
#[test]
|
|
|
|
fn export_def_env() -> TestResult {
|
|
|
|
run_test(
|
2023-10-02 18:13:31 +00:00
|
|
|
r#"module foo { export def --env bob [] { $env.BAR = "BAZ" } }; use foo bob; bob; $env.BAR"#,
|
2022-01-29 20:45:46 +00:00
|
|
|
"BAZ",
|
|
|
|
)
|
|
|
|
}
|
2022-02-04 21:19:13 +00:00
|
|
|
|
|
|
|
#[test]
|
2023-06-30 19:57:51 +00:00
|
|
|
fn dynamic_load_env() -> TestResult {
|
|
|
|
run_test(r#"let x = "FOO"; load-env {$x: "BAZ"}; $env.FOO"#, "BAZ")
|
2022-02-04 21:19:13 +00:00
|
|
|
}
|
2022-02-15 12:59:51 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reduce_spans() -> TestResult {
|
|
|
|
fail_test(
|
2023-10-08 11:12:46 +00:00
|
|
|
r#"let x = ([1, 2, 3] | reduce --fold 0 { $it.item + 2 * $it.acc }); error make {msg: "oh that hurts", label: {text: "right here", start: (metadata $x).span.start, end: (metadata $x).span.end } }"#,
|
2022-02-15 12:59:51 +00:00
|
|
|
"right here",
|
|
|
|
)
|
|
|
|
}
|
2022-02-16 09:59:44 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn with_env_shorthand_nested_quotes() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"FOO='-arg "hello world"' echo $env | get FOO"#,
|
|
|
|
"-arg \"hello world\"",
|
|
|
|
)
|
|
|
|
}
|
2022-02-21 22:22:21 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_redirection_stderr() -> TestResult {
|
|
|
|
// try a nonsense binary
|
|
|
|
run_test(r#"do -i { asdjw4j5cnaabw44rd }; echo done"#, "done")
|
|
|
|
}
|
2022-02-24 02:02:48 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn datetime_literal() -> TestResult {
|
|
|
|
run_test(r#"(date now) - 2019-08-23 > 1hr"#, "true")
|
|
|
|
}
|
2022-02-27 22:02:53 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn shortcircuiting_and() -> TestResult {
|
2022-12-07 23:02:11 +00:00
|
|
|
run_test(r#"false and (5 / 0; false)"#, "false")
|
2022-02-27 22:02:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn shortcircuiting_or() -> TestResult {
|
2022-12-07 23:02:11 +00:00
|
|
|
run_test(r#"true or (5 / 0; false)"#, "true")
|
2022-02-27 22:02:53 +00:00
|
|
|
}
|
2022-02-28 16:15:31 +00:00
|
|
|
|
2022-11-26 16:02:37 +00:00
|
|
|
#[test]
|
|
|
|
fn nonshortcircuiting_xor() -> TestResult {
|
|
|
|
run_test(r#"true xor (print "hello"; false) | ignore"#, "hello")
|
|
|
|
}
|
|
|
|
|
2022-02-28 16:15:31 +00:00
|
|
|
#[test]
|
|
|
|
fn open_ended_range() -> TestResult {
|
|
|
|
run_test(r#"1.. | first 100000 | length"#, "100000")
|
|
|
|
}
|
2022-03-03 00:55:03 +00:00
|
|
|
|
2022-03-07 20:08:56 +00:00
|
|
|
#[test]
|
|
|
|
fn default_value1() -> TestResult {
|
|
|
|
run_test(r#"def foo [x = 3] { $x }; foo"#, "3")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value2() -> TestResult {
|
|
|
|
run_test(r#"def foo [x: int = 3] { $x }; foo"#, "3")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value3() -> TestResult {
|
|
|
|
run_test(r#"def foo [--x = 3] { $x }; foo"#, "3")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value4() -> TestResult {
|
|
|
|
run_test(r#"def foo [--x: int = 3] { $x }; foo"#, "3")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value5() -> TestResult {
|
|
|
|
run_test(r#"def foo [x = 3] { $x }; foo 10"#, "10")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value6() -> TestResult {
|
|
|
|
run_test(r#"def foo [x: int = 3] { $x }; foo 10"#, "10")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value7() -> TestResult {
|
|
|
|
run_test(r#"def foo [--x = 3] { $x }; foo --x 10"#, "10")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value8() -> TestResult {
|
|
|
|
run_test(r#"def foo [--x: int = 3] { $x }; foo --x 10"#, "10")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value9() -> TestResult {
|
|
|
|
fail_test(r#"def foo [--x = 3] { $x }; foo --x a"#, "expected int")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value10() -> TestResult {
|
|
|
|
fail_test(r#"def foo [x = 3] { $x }; foo a"#, "expected int")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value11() -> TestResult {
|
|
|
|
fail_test(
|
|
|
|
r#"def foo [x = 3, y] { $x }; foo a"#,
|
|
|
|
"after optional parameter",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_value12() -> TestResult {
|
2022-12-31 11:18:53 +00:00
|
|
|
fail_test(
|
|
|
|
r#"def foo [--x:int = "a"] { $x }"#,
|
allow records to have type annotations (#8914)
# Description
follow up to #8529
cleaned up version of #8892
- the original syntax is okay
```nu
def okay [rec: record] {}
```
- you can now add type annotations for fields if you know
them before hand
```nu
def okay [rec: record<name: string>] {}
```
- you can specify multiple fields
```nu
def okay [person: record<name: string age: int>] {}
# an optional comma is allowed
def okay [person: record<name: string, age: int>] {}
```
- if annotations are specified, any use of the command will be type
checked against the specified type
```nu
def unwrap [result: record<ok: bool, value: any>] {}
unwrap {ok: 2, value: "value"}
# errors with
Error: nu::parser::type_mismatch
× Type mismatch.
╭─[entry #4:1:1]
1 │ unwrap {ok: 2, value: "value"}
· ───────┬─────
· ╰── expected record<ok: bool, value: any>, found record<ok: int, value: string>
╰────
```
> here the error is in the `ok` field, since `any` is coerced into any
type
> as a result `unwrap {ok: true, value: "value"}` is okay
- the key must be a string, either quoted or unquoted
```nu
def err [rec: record<{}: list>] {}
# errors with
Error:
× `record` type annotations key not string
╭─[entry #7:1:1]
1 │ def unwrap [result: record<{}: bool, value: any>] {}
· ─┬
· ╰── must be a string
╰────
```
- a key doesn't have to have a type in which case it is assumed to be
`any`
```nu
def okay [person: record<name age>] {}
def okay [person: record<name: string age>] {}
```
- however, if you put a colon, you have to specify a type
```nu
def err [person: record<name: >] {}
# errors with
Error: nu::parser::parse_mismatch
× Parse mismatch during operation.
╭─[entry #12:1:1]
1 │ def unwrap [res: record<name: >] { $res }
· ┬
· ╰── expected type after colon
╰────
```
# User-Facing Changes
**[BREAKING CHANGES]**
- this change adds a field to `SyntaxShape::Record` so any plugins that
used it will have to update and include the field. though if you are
unsure of the type the record expects, `SyntaxShape::Record(vec![])`
will suffice
2023-04-26 13:16:55 +00:00
|
|
|
"expected default value to be `int`",
|
2022-12-31 11:18:53 +00:00
|
|
|
)
|
2022-03-07 20:08:56 +00:00
|
|
|
}
|
2022-04-08 21:41:05 +00:00
|
|
|
|
2022-12-27 23:00:44 +00:00
|
|
|
#[test]
|
2023-05-20 13:23:25 +00:00
|
|
|
fn default_value_constant1() -> TestResult {
|
2023-04-26 14:14:02 +00:00
|
|
|
run_test(r#"def foo [x = "foo"] { $x }; foo"#, "foo")
|
|
|
|
}
|
|
|
|
|
2023-05-20 13:23:25 +00:00
|
|
|
#[test]
|
|
|
|
fn default_value_constant2() -> TestResult {
|
|
|
|
run_test(r#"def foo [secs = 1sec] { $secs }; foo"#, "1sec")
|
|
|
|
}
|
|
|
|
|
2023-04-26 14:14:02 +00:00
|
|
|
#[test]
|
2023-08-26 13:41:29 +00:00
|
|
|
fn default_value_constant3() -> TestResult {
|
|
|
|
run_test(r#"def foo [x = ("foo" | str length)] { $x }; foo"#, "3")
|
2022-12-27 23:00:44 +00:00
|
|
|
}
|
|
|
|
|
2023-05-03 21:09:36 +00:00
|
|
|
#[test]
|
|
|
|
fn default_value_not_constant2() -> TestResult {
|
|
|
|
fail_test(
|
2023-08-26 13:41:29 +00:00
|
|
|
r#"def foo [x = (loop { break })] { $x }; foo"#,
|
2023-05-03 21:09:36 +00:00
|
|
|
"expected a constant",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-04-08 21:41:05 +00:00
|
|
|
#[test]
|
|
|
|
fn loose_each() -> TestResult {
|
Restrict closure expression to be something like `{|| ...}` (#8290)
# Description
As title, closes: #7921 closes: #8273
# User-Facing Changes
when define a closure without pipe, nushell will raise error for now:
```
❯ let x = {ss ss}
Error: nu::parser::closure_missing_pipe
× Missing || inside closure
╭─[entry #2:1:1]
1 │ let x = {ss ss}
· ───┬───
· ╰── Parsing as a closure, but || is missing
╰────
help: Try add || to the beginning of closure
```
`any`, `each`, `all`, `where` command accepts closure, it forces user
input closure like `{||`, or parse error will returned.
```
❯ {major:2, minor:1, patch:4} | values | each { into string }
Error: nu::parser::closure_missing_pipe
× Missing || inside closure
╭─[entry #4:1:1]
1 │ {major:2, minor:1, patch:4} | values | each { into string }
· ───────┬───────
· ╰── Parsing as a closure, but || is missing
╰────
help: Try add || to the beginning of closure
```
`with-env`, `do`, `def`, `try` are special, they still remain the same,
although it says that it accepts a closure, but they don't need to be
written like `{||`, it's more likely a block but can capture variable
outside of scope:
```
❯ def test [input] { echo [0 1 2] | do { do { echo $input } } }; test aaa
aaa
```
Just realize that It's a big breaking change, we need to update config
and scripts...
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-03-17 12:36:28 +00:00
|
|
|
run_test(
|
|
|
|
r#"[[1, 2, 3], [4, 5, 6]] | each {|| $in.1 } | math sum"#,
|
|
|
|
"7",
|
|
|
|
)
|
2022-04-08 21:41:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_means_input() -> TestResult {
|
|
|
|
run_test(r#"def shl [] { $in * 2 }; 2 | shl"#, "4")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn in_iteration() -> TestResult {
|
|
|
|
run_test(
|
Restrict closure expression to be something like `{|| ...}` (#8290)
# Description
As title, closes: #7921 closes: #8273
# User-Facing Changes
when define a closure without pipe, nushell will raise error for now:
```
❯ let x = {ss ss}
Error: nu::parser::closure_missing_pipe
× Missing || inside closure
╭─[entry #2:1:1]
1 │ let x = {ss ss}
· ───┬───
· ╰── Parsing as a closure, but || is missing
╰────
help: Try add || to the beginning of closure
```
`any`, `each`, `all`, `where` command accepts closure, it forces user
input closure like `{||`, or parse error will returned.
```
❯ {major:2, minor:1, patch:4} | values | each { into string }
Error: nu::parser::closure_missing_pipe
× Missing || inside closure
╭─[entry #4:1:1]
1 │ {major:2, minor:1, patch:4} | values | each { into string }
· ───────┬───────
· ╰── Parsing as a closure, but || is missing
╰────
help: Try add || to the beginning of closure
```
`with-env`, `do`, `def`, `try` are special, they still remain the same,
although it says that it accepts a closure, but they don't need to be
written like `{||`, it's more likely a block but can capture variable
outside of scope:
```
❯ def test [input] { echo [0 1 2] | do { do { echo $input } } }; test aaa
aaa
```
Just realize that It's a big breaking change, we need to update config
and scripts...
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-03-17 12:36:28 +00:00
|
|
|
r#"[3, 4, 5] | each {|| echo $"hi ($in)" } | str join"#,
|
2022-04-08 21:41:05 +00:00
|
|
|
"hi 3hi 4hi 5",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-06-09 22:14:18 +00:00
|
|
|
fn reusable_in() -> TestResult {
|
2022-04-08 21:41:05 +00:00
|
|
|
run_test(
|
|
|
|
r#"[1, 2, 3, 4] | take (($in | length) - 1) | math sum"#,
|
|
|
|
"6",
|
|
|
|
)
|
|
|
|
}
|
2022-04-09 05:17:48 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn better_operator_spans() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"metadata ({foo: 10} | (20 - $in.foo)) | get span | $in.start < $in.end"#,
|
|
|
|
"true",
|
|
|
|
)
|
|
|
|
}
|
2022-04-26 18:39:38 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn range_right_exclusive() -> TestResult {
|
|
|
|
run_test(r#"[1, 4, 5, 8, 9] | range 1..<3 | math sum"#, "9")
|
|
|
|
}
|
2023-01-28 17:55:29 +00:00
|
|
|
|
|
|
|
/// Issue #7872
|
|
|
|
#[test]
|
|
|
|
fn assignment_to_in_var_no_panic() -> TestResult {
|
|
|
|
fail_test(r#"$in = 3"#, "needs to be a mutable variable")
|
|
|
|
}
|
2023-01-28 19:17:32 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn assignment_to_env_no_panic() -> TestResult {
|
|
|
|
fail_test(r#"$env = 3"#, "cannot_replace_env")
|
|
|
|
}
|
2023-04-26 13:16:32 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn short_flags() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def foobar [-a: int, -b: string, -c: string] { echo $'($a) ($c) ($b)' }; foobar -b "balh balh" -a 1543 -c "FALSE123""#,
|
|
|
|
"1543 FALSE123 balh balh",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn short_flags_1() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def foobar [-a: string, -b: string, -s: int] { if ( $s == 0 ) { echo $'($b)($a)' }}; foobar -a test -b case -s 0 "#,
|
|
|
|
"casetest",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn short_flags_2() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def foobar [-a: int, -b: string, -c: int] { $a + $c };foobar -b "balh balh" -a 10 -c 1 "#,
|
|
|
|
"11",
|
|
|
|
)
|
|
|
|
}
|