2023-06-20 23:33:01 +02:00
|
|
|
use crate::{current_dir_str, get_full_help};
|
2022-01-16 14:55:56 +01:00
|
|
|
use nu_path::expand_path_with;
|
2021-10-25 06:01:02 +02:00
|
|
|
use nu_protocol::{
|
2022-11-11 08:16:07 +01:00
|
|
|
ast::{
|
2023-09-05 16:35:58 +02:00
|
|
|
eval_operator, Argument, Assignment, Bits, Block, Boolean, Call, Comparison, Expr,
|
|
|
|
Expression, Math, Operator, PathMember, PipelineElement, Redirection,
|
2022-11-11 08:16:07 +01:00
|
|
|
},
|
2023-09-11 20:50:03 +02:00
|
|
|
engine::{EngineState, Stack},
|
2023-10-19 18:41:38 +02:00
|
|
|
IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Range, Record, ShellError, Span,
|
|
|
|
Spanned, Unit, Value, VarId, ENV_VARIABLE_ID,
|
2021-10-25 06:01:02 +02:00
|
|
|
};
|
2023-09-01 08:18:55 +02:00
|
|
|
use std::collections::HashMap;
|
2021-10-13 19:53:27 +02:00
|
|
|
|
2022-03-10 08:49:02 +01:00
|
|
|
pub fn eval_call(
|
2021-10-25 08:31:39 +02:00
|
|
|
engine_state: &EngineState,
|
2021-12-25 20:39:42 +01:00
|
|
|
caller_stack: &mut Stack,
|
2021-10-25 06:01:02 +02:00
|
|
|
call: &Call,
|
|
|
|
input: PipelineData,
|
|
|
|
) -> Result<PipelineData, ShellError> {
|
2022-12-15 18:39:24 +01:00
|
|
|
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
2023-09-03 16:27:29 +02:00
|
|
|
return Ok(Value::nothing(call.head).into_pipeline_data());
|
2022-05-02 10:18:25 +02:00
|
|
|
}
|
2021-12-06 05:09:49 +01:00
|
|
|
let decl = engine_state.get_decl(call.decl_id);
|
2021-11-28 20:35:02 +01:00
|
|
|
|
2022-04-09 07:17:48 +02:00
|
|
|
if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
|
2023-08-11 19:58:49 +02:00
|
|
|
let mut signature = engine_state.get_signature(decl);
|
2022-02-10 03:24:29 +01:00
|
|
|
signature.usage = decl.usage().to_string();
|
|
|
|
signature.extra_usage = decl.extra_usage().to_string();
|
|
|
|
|
2022-11-20 14:22:42 +01:00
|
|
|
let full_help = get_full_help(
|
|
|
|
&signature,
|
|
|
|
&decl.examples(),
|
|
|
|
engine_state,
|
|
|
|
caller_stack,
|
|
|
|
decl.is_parser_keyword(),
|
|
|
|
);
|
2023-09-03 16:27:29 +02:00
|
|
|
Ok(Value::string(full_help, call.head).into_pipeline_data())
|
2021-10-13 19:53:27 +02:00
|
|
|
} else if let Some(block_id) = decl.get_block_id() {
|
2021-10-25 22:04:23 +02:00
|
|
|
let block = engine_state.get_block(block_id);
|
|
|
|
|
Recursively export constants from modules (#10049)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx
you can also mention related issues, PRs or discussions!
-->
# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.
Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
https://github.com/nushell/nushell/pull/9773 introduced constants to
modules and allowed to export them, but only within one level. This PR:
* allows recursive exporting of constants from all submodules
* fixes submodule imports in a list import pattern
* makes sure exported constants are actual constants
Should unblock https://github.com/nushell/nushell/pull/9678
### Example:
```nushell
module spam {
export module eggs {
export module bacon {
export const viking = 'eats'
}
}
}
use spam
print $spam.eggs.bacon.viking # prints 'eats'
use spam [eggs]
print $eggs.bacon.viking # prints 'eats'
use spam eggs bacon viking
print $viking # prints 'eats'
```
### Limitation 1:
Considering the above `spam` module, attempting to get `eggs bacon` from
`spam` module doesn't work directly:
```nushell
use spam [ eggs bacon ] # attempts to load `eggs`, then `bacon`
use spam [ "eggs bacon" ] # obviously wrong name for a constant, but doesn't work also for commands
```
Workaround (for example):
```nushell
use spam eggs
use eggs [ bacon ]
print $bacon.viking # prints 'eats'
```
I'm thinking I'll just leave it in, as you can easily work around this.
It is also a limitation of the import pattern in general, not just
constants.
### Limitation 2:
`overlay use` successfully imports the constants, but `overlay hide`
does not hide them, even though it seems to hide normal variables
successfully. This needs more investigation.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Allows recursive constant exports from submodules.
# 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 -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-08-20 14:51:35 +02:00
|
|
|
let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
|
2021-12-20 07:58:09 +01:00
|
|
|
|
|
|
|
for (param_idx, param) in decl
|
|
|
|
.signature()
|
|
|
|
.required_positional
|
|
|
|
.iter()
|
|
|
|
.chain(decl.signature().optional_positional.iter())
|
|
|
|
.enumerate()
|
|
|
|
{
|
2021-07-23 23:19:30 +02:00
|
|
|
let var_id = param
|
|
|
|
.var_id
|
|
|
|
.expect("internal error: all custom parameters must have var_ids");
|
|
|
|
|
2022-04-09 04:55:02 +02:00
|
|
|
if let Some(arg) = call.positional_nth(param_idx) {
|
2021-12-25 20:39:42 +01:00
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
|
|
|
callee_stack.add_var(var_id, result);
|
2023-04-26 16:14:02 +02:00
|
|
|
} else if let Some(value) = ¶m.default_value {
|
|
|
|
callee_stack.add_var(var_id, value.to_owned());
|
2021-12-20 07:58:09 +01:00
|
|
|
} else {
|
2021-12-25 20:39:42 +01:00
|
|
|
callee_stack.add_var(var_id, Value::nothing(call.head));
|
2021-12-20 07:58:09 +01:00
|
|
|
}
|
2021-07-23 23:19:30 +02:00
|
|
|
}
|
2021-09-07 05:37:02 +02:00
|
|
|
|
|
|
|
if let Some(rest_positional) = decl.signature().rest_positional {
|
|
|
|
let mut rest_items = vec![];
|
|
|
|
|
2022-04-09 04:55:02 +02:00
|
|
|
for arg in call.positional_iter().skip(
|
2021-09-07 05:37:02 +02:00
|
|
|
decl.signature().required_positional.len()
|
|
|
|
+ decl.signature().optional_positional.len(),
|
|
|
|
) {
|
2021-12-25 20:39:42 +01:00
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
2021-09-07 05:37:02 +02:00
|
|
|
rest_items.push(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
let span = if let Some(rest_item) = rest_items.first() {
|
2023-08-24 22:48:05 +02:00
|
|
|
rest_item.span()
|
2021-09-07 05:37:02 +02:00
|
|
|
} else {
|
2021-12-19 08:46:13 +01:00
|
|
|
call.head
|
2021-09-07 05:37:02 +02:00
|
|
|
};
|
|
|
|
|
2021-12-25 20:39:42 +01:00
|
|
|
callee_stack.add_var(
|
2021-09-07 05:37:02 +02:00
|
|
|
rest_positional
|
|
|
|
.var_id
|
|
|
|
.expect("Internal error: rest positional parameter lacks var_id"),
|
2023-09-03 16:27:29 +02:00
|
|
|
Value::list(rest_items, span),
|
2021-09-07 05:37:02 +02:00
|
|
|
)
|
|
|
|
}
|
2021-10-11 23:17:45 +02:00
|
|
|
|
|
|
|
for named in decl.signature().named {
|
2021-10-13 19:53:27 +02:00
|
|
|
if let Some(var_id) = named.var_id {
|
|
|
|
let mut found = false;
|
2022-04-09 04:55:02 +02:00
|
|
|
for call_named in call.named_iter() {
|
2023-04-26 15:16:32 +02:00
|
|
|
if let (Some(spanned), Some(short)) = (&call_named.1, named.short) {
|
|
|
|
if spanned.item == short.to_string() {
|
|
|
|
if let Some(arg) = &call_named.2 {
|
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
|
|
|
|
|
|
|
callee_stack.add_var(var_id, result);
|
2023-05-03 23:09:36 +02:00
|
|
|
} else if let Some(value) = &named.default_value {
|
|
|
|
callee_stack.add_var(var_id, value.to_owned());
|
2023-04-26 15:16:32 +02:00
|
|
|
} else {
|
2023-07-21 15:20:33 +02:00
|
|
|
callee_stack.add_var(var_id, Value::bool(true, call.head))
|
2023-04-26 15:16:32 +02:00
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
} else if call_named.0.item == named.long {
|
2022-04-09 07:17:48 +02:00
|
|
|
if let Some(arg) = &call_named.2 {
|
2021-12-25 20:39:42 +01:00
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
2021-10-12 06:49:17 +02:00
|
|
|
|
2022-03-07 21:08:56 +01:00
|
|
|
callee_stack.add_var(var_id, result);
|
2023-05-03 23:09:36 +02:00
|
|
|
} else if let Some(value) = &named.default_value {
|
|
|
|
callee_stack.add_var(var_id, value.to_owned());
|
2021-10-13 19:53:27 +02:00
|
|
|
} else {
|
2023-07-21 15:20:33 +02:00
|
|
|
callee_stack.add_var(var_id, Value::bool(true, call.head))
|
2021-10-13 19:53:27 +02:00
|
|
|
}
|
|
|
|
found = true;
|
2021-10-11 23:17:45 +02:00
|
|
|
}
|
|
|
|
}
|
2021-10-12 06:49:17 +02:00
|
|
|
|
2022-01-06 21:32:47 +01:00
|
|
|
if !found {
|
|
|
|
if named.arg.is_none() {
|
2023-07-21 15:20:33 +02:00
|
|
|
callee_stack.add_var(var_id, Value::bool(false, call.head))
|
2023-05-03 23:09:36 +02:00
|
|
|
} else if let Some(value) = named.default_value {
|
|
|
|
callee_stack.add_var(var_id, value);
|
2022-01-06 21:32:47 +01:00
|
|
|
} else {
|
2023-09-03 16:27:29 +02:00
|
|
|
callee_stack.add_var(var_id, Value::nothing(call.head))
|
2022-01-06 21:32:47 +01:00
|
|
|
}
|
2021-10-13 19:53:27 +02:00
|
|
|
}
|
2021-10-12 06:49:17 +02:00
|
|
|
}
|
2021-10-11 23:17:45 +02:00
|
|
|
}
|
2022-02-04 19:02:03 +01:00
|
|
|
|
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-24 21:39:16 +01:00
|
|
|
let result = eval_block_with_early_return(
|
2022-03-02 13:52:24 +01:00
|
|
|
engine_state,
|
|
|
|
&mut callee_stack,
|
|
|
|
block,
|
|
|
|
input,
|
|
|
|
call.redirect_stdout,
|
|
|
|
call.redirect_stderr,
|
|
|
|
);
|
2022-02-04 19:02:03 +01:00
|
|
|
|
2022-01-29 21:45:46 +01:00
|
|
|
if block.redirect_env {
|
2022-08-18 22:24:39 +02:00
|
|
|
redirect_env(engine_state, caller_stack, &callee_stack);
|
2022-01-29 21:45:46 +01:00
|
|
|
}
|
2022-02-04 19:02:03 +01:00
|
|
|
|
2022-01-29 21:45:46 +01:00
|
|
|
result
|
2021-07-30 00:56:51 +02:00
|
|
|
} else {
|
2021-12-25 20:39:42 +01:00
|
|
|
// We pass caller_stack here with the knowledge that internal commands
|
|
|
|
// are going to be specifically looking for global state in the stack
|
|
|
|
// rather than any local state.
|
|
|
|
decl.run(engine_state, caller_stack, call, input)
|
2021-07-16 03:10:22 +02:00
|
|
|
}
|
2021-07-23 07:14:49 +02:00
|
|
|
}
|
2021-07-16 03:10:22 +02:00
|
|
|
|
2022-08-18 22:24:39 +02:00
|
|
|
/// Redirect the environment from callee to the caller.
|
|
|
|
pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
|
2022-08-31 22:32:56 +02:00
|
|
|
// Grab all environment variables from the callee
|
2022-08-18 22:24:39 +02:00
|
|
|
let caller_env_vars = caller_stack.get_env_var_names(engine_state);
|
|
|
|
|
|
|
|
// remove env vars that are present in the caller but not in the callee
|
|
|
|
// (the callee hid them)
|
|
|
|
for var in caller_env_vars.iter() {
|
|
|
|
if !callee_stack.has_env_var(engine_state, var) {
|
|
|
|
caller_stack.remove_env_var(engine_state, var);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add new env vars from callee to caller
|
|
|
|
for (var, value) in callee_stack.get_stack_env_vars() {
|
|
|
|
caller_stack.add_env_var(var, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
enum RedirectTarget {
|
|
|
|
Piped(bool, bool),
|
|
|
|
CombinedPipe,
|
|
|
|
}
|
|
|
|
|
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 04:51:57 +01:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-09-19 23:48:33 +02:00
|
|
|
fn eval_external(
|
2021-10-25 08:31:39 +02:00
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
2022-01-13 09:17:45 +01:00
|
|
|
head: &Expression,
|
2021-10-08 23:51:47 +02:00
|
|
|
args: &[Expression],
|
2021-10-25 06:01:02 +02:00
|
|
|
input: PipelineData,
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
redirect_target: RedirectTarget,
|
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 04:51:57 +01:00
|
|
|
is_subexpression: bool,
|
2022-09-30 14:13:46 +02:00
|
|
|
) -> Result<PipelineData, ShellError> {
|
2021-10-25 08:31:39 +02:00
|
|
|
let decl_id = engine_state
|
2022-05-07 21:39:22 +02:00
|
|
|
.find_decl("run-external".as_bytes(), &[])
|
2023-03-06 18:33:09 +01:00
|
|
|
.ok_or(ShellError::ExternalNotSupported { span: head.span })?;
|
2021-09-19 23:48:33 +02:00
|
|
|
|
2021-10-25 08:31:39 +02:00
|
|
|
let command = engine_state.get_decl(decl_id);
|
2021-09-19 23:48:33 +02:00
|
|
|
|
2022-02-04 03:01:45 +01:00
|
|
|
let mut call = Call::new(head.span);
|
2021-10-08 23:51:47 +02:00
|
|
|
|
2022-04-09 04:55:02 +02:00
|
|
|
call.add_positional(head.clone());
|
2021-10-08 23:51:47 +02:00
|
|
|
|
|
|
|
for arg in args {
|
2022-04-09 04:55:02 +02:00
|
|
|
call.add_positional(arg.clone())
|
2021-10-08 23:51:47 +02:00
|
|
|
}
|
2021-09-19 23:48:33 +02:00
|
|
|
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
match redirect_target {
|
|
|
|
RedirectTarget::Piped(redirect_stdout, redirect_stderr) => {
|
|
|
|
if redirect_stdout {
|
|
|
|
call.add_named((
|
|
|
|
Spanned {
|
|
|
|
item: "redirect-stdout".into(),
|
|
|
|
span: head.span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
2022-02-21 23:22:21 +01:00
|
|
|
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
if redirect_stderr {
|
|
|
|
call.add_named((
|
|
|
|
Spanned {
|
|
|
|
item: "redirect-stderr".into(),
|
|
|
|
span: head.span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RedirectTarget::CombinedPipe => call.add_named((
|
2022-02-21 23:22:21 +01:00
|
|
|
Spanned {
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
item: "redirect-combine".into(),
|
2022-01-13 09:17:45 +01:00
|
|
|
span: head.span,
|
2021-10-11 23:17:45 +02:00
|
|
|
},
|
|
|
|
None,
|
2022-04-09 07:17:48 +02:00
|
|
|
None,
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
)),
|
2021-09-23 18:42:03 +02:00
|
|
|
}
|
|
|
|
|
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 04:51:57 +01:00
|
|
|
if is_subexpression {
|
|
|
|
call.add_named((
|
|
|
|
Spanned {
|
|
|
|
item: "trim-end-newline".into(),
|
|
|
|
span: head.span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-09-30 14:13:46 +02:00
|
|
|
command.run(engine_state, stack, &call, input)
|
2021-09-19 23:48:33 +02:00
|
|
|
}
|
|
|
|
|
2021-09-03 00:58:15 +02:00
|
|
|
pub fn eval_expression(
|
2021-10-25 08:31:39 +02:00
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
2021-09-03 00:58:15 +02:00
|
|
|
expr: &Expression,
|
|
|
|
) -> Result<Value, ShellError> {
|
2021-07-23 07:14:49 +02:00
|
|
|
match &expr.expr {
|
2023-07-21 15:20:33 +02:00
|
|
|
Expr::Bool(b) => Ok(Value::bool(*b, expr.span)),
|
Reduced LOC by replacing several instances of `Value::Int {}`, `Value::Float{}`, `Value::Bool {}`, and `Value::String {}` with `Value::int()`, `Value::float()`, `Value::boolean()` and `Value::string()` (#7412)
# Description
While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
val: a,
span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.
# User-Facing Changes
None.
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 17:37:51 +01:00
|
|
|
Expr::Int(i) => Ok(Value::int(*i, expr.span)),
|
|
|
|
Expr::Float(f) => Ok(Value::float(*f, expr.span)),
|
2023-09-03 16:27:29 +02:00
|
|
|
Expr::Binary(b) => Ok(Value::binary(b.clone(), expr.span)),
|
2021-10-25 08:31:39 +02:00
|
|
|
Expr::ValueWithUnit(e, unit) => match eval_expression(engine_state, stack, e)? {
|
2023-08-24 22:48:05 +02:00
|
|
|
Value::Int { val, .. } => compute(val, unit.item, unit.span),
|
2023-03-06 18:33:09 +01:00
|
|
|
x => Err(ShellError::CantConvert {
|
|
|
|
to_type: "unit value".into(),
|
|
|
|
from_type: x.get_type().to_string(),
|
|
|
|
span: e.span,
|
|
|
|
help: None,
|
|
|
|
}),
|
2021-10-05 04:27:39 +02:00
|
|
|
},
|
2021-09-11 13:13:04 +02:00
|
|
|
Expr::Range(from, next, to, operator) => {
|
2021-09-04 23:52:57 +02:00
|
|
|
let from = if let Some(f) = from {
|
2021-10-25 08:31:39 +02:00
|
|
|
eval_expression(engine_state, stack, f)?
|
2021-09-04 23:52:57 +02:00
|
|
|
} else {
|
2023-09-03 16:27:29 +02:00
|
|
|
Value::nothing(expr.span)
|
2021-09-04 23:52:57 +02:00
|
|
|
};
|
|
|
|
|
2021-09-11 13:13:04 +02:00
|
|
|
let next = if let Some(s) = next {
|
2021-10-25 08:31:39 +02:00
|
|
|
eval_expression(engine_state, stack, s)?
|
2021-09-04 23:52:57 +02:00
|
|
|
} else {
|
2023-09-03 16:27:29 +02:00
|
|
|
Value::nothing(expr.span)
|
2021-09-04 23:52:57 +02:00
|
|
|
};
|
|
|
|
|
2021-09-11 13:13:04 +02:00
|
|
|
let to = if let Some(t) = to {
|
2021-10-25 08:31:39 +02:00
|
|
|
eval_expression(engine_state, stack, t)?
|
2021-09-11 13:13:04 +02:00
|
|
|
} else {
|
2023-09-03 16:27:29 +02:00
|
|
|
Value::nothing(expr.span)
|
2021-09-04 23:52:57 +02:00
|
|
|
};
|
|
|
|
|
2023-09-03 16:27:29 +02:00
|
|
|
Ok(Value::range(
|
|
|
|
Range::new(expr.span, from, next, to, operator)?,
|
|
|
|
expr.span,
|
|
|
|
))
|
2021-09-04 23:52:57 +02:00
|
|
|
}
|
2021-10-29 20:15:17 +02:00
|
|
|
Expr::Var(var_id) => eval_variable(engine_state, stack, *var_id, expr.span),
|
2023-09-03 16:27:29 +02:00
|
|
|
Expr::VarDecl(_) => Ok(Value::nothing(expr.span)),
|
|
|
|
Expr::CellPath(cell_path) => Ok(Value::cell_path(cell_path.clone(), expr.span)),
|
2021-09-26 20:39:19 +02:00
|
|
|
Expr::FullCellPath(cell_path) => {
|
2021-10-25 08:31:39 +02:00
|
|
|
let value = eval_expression(engine_state, stack, &cell_path.head)?;
|
2021-09-07 00:02:24 +02:00
|
|
|
|
2023-03-16 04:50:58 +01:00
|
|
|
value.follow_cell_path(&cell_path.tail, false)
|
2021-09-07 00:02:24 +02:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Expr::ImportPattern(_) => Ok(Value::nothing(expr.span)),
|
2022-09-04 17:36:42 +02:00
|
|
|
Expr::Overlay(_) => {
|
|
|
|
let name =
|
2023-07-31 21:47:46 +02:00
|
|
|
String::from_utf8_lossy(engine_state.get_span_contents(expr.span)).to_string();
|
2022-09-04 17:36:42 +02:00
|
|
|
|
2023-09-03 16:27:29 +02:00
|
|
|
Ok(Value::string(name, expr.span))
|
2022-09-04 17:36:42 +02:00
|
|
|
}
|
2021-10-25 06:01:02 +02:00
|
|
|
Expr::Call(call) => {
|
|
|
|
// FIXME: protect this collect with ctrl-c
|
2022-12-07 19:31:57 +01:00
|
|
|
Ok(eval_call(engine_state, stack, call, PipelineData::empty())?.into_value(call.head))
|
2021-10-25 06:01:02 +02:00
|
|
|
}
|
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 04:51:57 +01:00
|
|
|
Expr::ExternalCall(head, args, is_subexpression) => {
|
2022-01-13 09:17:45 +01:00
|
|
|
let span = head.span;
|
2021-10-25 06:01:02 +02:00
|
|
|
// FIXME: protect this collect with ctrl-c
|
2021-10-25 23:14:21 +02:00
|
|
|
Ok(eval_external(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
2022-01-13 09:17:45 +01:00
|
|
|
head,
|
2021-10-25 23:14:21 +02:00
|
|
|
args,
|
2022-12-07 19:31:57 +01:00
|
|
|
PipelineData::empty(),
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
RedirectTarget::Piped(false, false),
|
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 04:51:57 +01:00
|
|
|
*is_subexpression,
|
2021-10-25 23:14:21 +02:00
|
|
|
)?
|
2022-01-13 09:17:45 +01:00
|
|
|
.into_value(span))
|
2021-09-23 18:42:03 +02:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Expr::DateTime(dt) => Ok(Value::date(*dt, expr.span)),
|
|
|
|
Expr::Operator(_) => Ok(Value::nothing(expr.span)),
|
|
|
|
Expr::MatchPattern(pattern) => Ok(Value::match_pattern(*pattern.clone(), expr.span)),
|
|
|
|
Expr::MatchBlock(_) => Ok(Value::nothing(expr.span)), // match blocks are handled by `match`
|
2022-04-06 21:10:25 +02:00
|
|
|
Expr::UnaryNot(expr) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, expr)?;
|
|
|
|
match lhs {
|
2023-07-21 15:20:33 +02:00
|
|
|
Value::Bool { val, .. } => Ok(Value::bool(!val, expr.span)),
|
2023-09-26 14:53:59 +02:00
|
|
|
other => Err(ShellError::TypeMismatch {
|
|
|
|
err_message: format!("expected bool, found {}", other.get_type()),
|
2023-03-06 11:31:07 +01:00
|
|
|
span: expr.span,
|
|
|
|
}),
|
2022-04-06 21:10:25 +02:00
|
|
|
}
|
|
|
|
}
|
2021-07-23 07:14:49 +02:00
|
|
|
Expr::BinaryOp(lhs, op, rhs) => {
|
2021-08-10 08:31:34 +02:00
|
|
|
let op_span = op.span;
|
2021-08-16 00:33:34 +02:00
|
|
|
let op = eval_operator(op)?;
|
2021-07-23 07:14:49 +02:00
|
|
|
|
|
|
|
match op {
|
2022-11-11 07:51:08 +01:00
|
|
|
Operator::Boolean(boolean) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
match boolean {
|
|
|
|
Boolean::And => {
|
|
|
|
if lhs.is_false() {
|
2023-07-21 15:20:33 +02:00
|
|
|
Ok(Value::bool(false, expr.span))
|
2022-11-11 07:51:08 +01:00
|
|
|
} else {
|
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
|
|
|
lhs.and(op_span, &rhs, expr.span)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Boolean::Or => {
|
|
|
|
if lhs.is_true() {
|
2023-07-21 15:20:33 +02:00
|
|
|
Ok(Value::bool(true, expr.span))
|
2022-11-11 07:51:08 +01:00
|
|
|
} else {
|
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
|
|
|
lhs.or(op_span, &rhs, expr.span)
|
|
|
|
}
|
|
|
|
}
|
2022-11-26 17:02:37 +01:00
|
|
|
Boolean::Xor => {
|
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
|
|
|
lhs.xor(op_span, &rhs, expr.span)
|
|
|
|
}
|
2022-02-27 23:02:53 +01:00
|
|
|
}
|
|
|
|
}
|
2022-11-11 07:51:08 +01:00
|
|
|
Operator::Math(math) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
2022-02-27 23:02:53 +01:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 07:51:08 +01:00
|
|
|
|
|
|
|
match math {
|
|
|
|
Math::Plus => lhs.add(op_span, &rhs, expr.span),
|
|
|
|
Math::Minus => lhs.sub(op_span, &rhs, expr.span),
|
|
|
|
Math::Multiply => lhs.mul(op_span, &rhs, expr.span),
|
|
|
|
Math::Divide => lhs.div(op_span, &rhs, expr.span),
|
|
|
|
Math::Append => lhs.append(op_span, &rhs, expr.span),
|
|
|
|
Math::Modulo => lhs.modulo(op_span, &rhs, expr.span),
|
|
|
|
Math::FloorDivision => lhs.floor_div(op_span, &rhs, expr.span),
|
|
|
|
Math::Pow => lhs.pow(op_span, &rhs, expr.span),
|
|
|
|
}
|
2022-07-03 13:45:20 +02:00
|
|
|
}
|
2022-11-11 07:51:08 +01:00
|
|
|
Operator::Comparison(comparison) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
2022-07-02 20:03:36 +02:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 07:51:08 +01:00
|
|
|
match comparison {
|
|
|
|
Comparison::LessThan => lhs.lt(op_span, &rhs, expr.span),
|
|
|
|
Comparison::LessThanOrEqual => lhs.lte(op_span, &rhs, expr.span),
|
|
|
|
Comparison::GreaterThan => lhs.gt(op_span, &rhs, expr.span),
|
|
|
|
Comparison::GreaterThanOrEqual => lhs.gte(op_span, &rhs, expr.span),
|
|
|
|
Comparison::Equal => lhs.eq(op_span, &rhs, expr.span),
|
|
|
|
Comparison::NotEqual => lhs.ne(op_span, &rhs, expr.span),
|
|
|
|
Comparison::In => lhs.r#in(op_span, &rhs, expr.span),
|
|
|
|
Comparison::NotIn => lhs.not_in(op_span, &rhs, expr.span),
|
Add LRU regex cache (#7587)
Closes #7572 by adding a cache for compiled regexes of type
`Arc<Mutex<LruCache<String, Regex>>>` to `EngineState` .
The cache is limited to 100 entries (limit chosen arbitrarily) and
evicts least-recently-used items first.
This PR makes a noticeable difference when using regexes for
`color_config`, e.g.:
```bash
#first set string formatting in config.nu like:
string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }`
# then try displaying and exploring a table with many strings
# this is instant after the PR, but takes hundreds of milliseconds before
['#ff0033', '#0025ee', '#0087aa', 'string', '#4101ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff']
```
## New dependency (`lru`)
This uses [the popular `lru` crate](https://lib.rs/crates/lru). The new
dependency adds 19.8KB to a Linux release build of Nushell. I think this
is OK, especially since the crate can be useful elsewhere in Nu.
2022-12-23 23:30:04 +01:00
|
|
|
Comparison::RegexMatch => {
|
|
|
|
lhs.regex_match(engine_state, op_span, &rhs, false, expr.span)
|
|
|
|
}
|
2022-11-11 07:51:08 +01:00
|
|
|
Comparison::NotRegexMatch => {
|
Add LRU regex cache (#7587)
Closes #7572 by adding a cache for compiled regexes of type
`Arc<Mutex<LruCache<String, Regex>>>` to `EngineState` .
The cache is limited to 100 entries (limit chosen arbitrarily) and
evicts least-recently-used items first.
This PR makes a noticeable difference when using regexes for
`color_config`, e.g.:
```bash
#first set string formatting in config.nu like:
string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }`
# then try displaying and exploring a table with many strings
# this is instant after the PR, but takes hundreds of milliseconds before
['#ff0033', '#0025ee', '#0087aa', 'string', '#4101ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff']
```
## New dependency (`lru`)
This uses [the popular `lru` crate](https://lib.rs/crates/lru). The new
dependency adds 19.8KB to a Linux release build of Nushell. I think this
is OK, especially since the crate can be useful elsewhere in Nu.
2022-12-23 23:30:04 +01:00
|
|
|
lhs.regex_match(engine_state, op_span, &rhs, true, expr.span)
|
2022-11-11 07:51:08 +01:00
|
|
|
}
|
|
|
|
Comparison::StartsWith => lhs.starts_with(op_span, &rhs, expr.span),
|
|
|
|
Comparison::EndsWith => lhs.ends_with(op_span, &rhs, expr.span),
|
|
|
|
}
|
2022-07-02 20:03:36 +02:00
|
|
|
}
|
2022-11-11 07:51:08 +01:00
|
|
|
Operator::Bits(bits) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
2022-07-02 13:48:43 +02:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 07:51:08 +01:00
|
|
|
match bits {
|
|
|
|
Bits::BitAnd => lhs.bit_and(op_span, &rhs, expr.span),
|
|
|
|
Bits::BitOr => lhs.bit_or(op_span, &rhs, expr.span),
|
|
|
|
Bits::BitXor => lhs.bit_xor(op_span, &rhs, expr.span),
|
|
|
|
Bits::ShiftLeft => lhs.bit_shl(op_span, &rhs, expr.span),
|
|
|
|
Bits::ShiftRight => lhs.bit_shr(op_span, &rhs, expr.span),
|
|
|
|
}
|
2022-07-02 13:48:43 +02:00
|
|
|
}
|
2022-11-11 19:50:43 +01:00
|
|
|
Operator::Assignment(assignment) => {
|
2022-07-02 13:48:43 +02:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 07:51:08 +01:00
|
|
|
|
2022-11-11 19:50:43 +01:00
|
|
|
let rhs = match assignment {
|
|
|
|
Assignment::Assign => rhs,
|
|
|
|
Assignment::PlusAssign => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
lhs.add(op_span, &rhs, op_span)?
|
|
|
|
}
|
|
|
|
Assignment::MinusAssign => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
lhs.sub(op_span, &rhs, op_span)?
|
|
|
|
}
|
|
|
|
Assignment::MultiplyAssign => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
lhs.mul(op_span, &rhs, op_span)?
|
|
|
|
}
|
|
|
|
Assignment::DivideAssign => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
lhs.div(op_span, &rhs, op_span)?
|
|
|
|
}
|
2022-12-09 17:20:58 +01:00
|
|
|
Assignment::AppendAssign => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
lhs.append(op_span, &rhs, op_span)?
|
|
|
|
}
|
2022-11-11 19:50:43 +01:00
|
|
|
};
|
|
|
|
|
2022-11-11 07:51:08 +01:00
|
|
|
match &lhs.expr {
|
|
|
|
Expr::Var(var_id) | Expr::VarDecl(var_id) => {
|
|
|
|
let var_info = engine_state.get_var(*var_id);
|
|
|
|
if var_info.mutable {
|
Move variables to var stack (#8604)
# Description
This moves the representation of variables on the stack to a Vec, which
more closely resembles a stack. For small numbers of variables live at
any one point, this tends to be more efficient than a HashMap. Having a
stack-like vector also allows us to remember a stack position,
temporarily push variables on, then quickly drop the stack back to the
original size when we're done. We'll need this capability to allow
matching inside of conditions.
On this mac, a simple run of:
`timeit { mut x = 1; while $x < 1000000 { $x += 1 } }`
Went from 1 sec 86 ms, down to 1 sec 2 ms. Clearly, we have a lot more
ground we can make up in looping speed 😅 but it's nice that for fixing
this to make matching easier, we also get a win in terms of lookup speed
for small numbers of variables.
# User-Facing Changes
Likely users won't (hopefully) see any negative impact and may even see
a small positive impact.
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-03-25 00:56:45 +01:00
|
|
|
stack.add_var(*var_id, rhs);
|
2022-11-11 07:51:08 +01:00
|
|
|
Ok(Value::nothing(lhs.span))
|
|
|
|
} else {
|
2023-03-06 11:31:07 +01:00
|
|
|
Err(ShellError::AssignmentRequiresMutableVar { lhs_span: lhs.span })
|
2022-11-11 07:51:08 +01:00
|
|
|
}
|
|
|
|
}
|
2023-06-30 21:57:51 +02:00
|
|
|
Expr::FullCellPath(cell_path) => {
|
|
|
|
match &cell_path.head.expr {
|
|
|
|
Expr::Var(var_id) | Expr::VarDecl(var_id) => {
|
|
|
|
// The $env variable is considered "mutable" in Nushell.
|
|
|
|
// As such, give it special treatment here.
|
|
|
|
let is_env = var_id == &ENV_VARIABLE_ID;
|
|
|
|
if is_env || engine_state.get_var(*var_id).mutable {
|
|
|
|
let mut lhs =
|
|
|
|
eval_expression(engine_state, stack, &cell_path.head)?;
|
|
|
|
|
|
|
|
lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
|
|
|
|
if is_env {
|
|
|
|
if cell_path.tail.is_empty() {
|
|
|
|
return Err(ShellError::CannotReplaceEnv {
|
|
|
|
span: cell_path.head.span,
|
|
|
|
});
|
2022-12-06 18:51:55 +01:00
|
|
|
}
|
2023-06-30 21:57:51 +02:00
|
|
|
|
|
|
|
// The special $env treatment: for something like $env.config.history.max_size = 2000,
|
|
|
|
// get $env.config (or whichever one it is) AFTER the above mutation, and set it
|
|
|
|
// as the "config" environment variable.
|
|
|
|
let vardata = lhs.follow_cell_path(
|
|
|
|
&[cell_path.tail[0].clone()],
|
|
|
|
false,
|
|
|
|
)?;
|
|
|
|
match &cell_path.tail[0] {
|
|
|
|
PathMember::String { val, span, .. } => {
|
|
|
|
if val == "FILE_PWD"
|
|
|
|
|| val == "CURRENT_FILE"
|
|
|
|
|| val == "PWD"
|
|
|
|
{
|
|
|
|
return Err(ShellError::AutomaticEnvVarSetManually {
|
|
|
|
envvar_name: val.to_string(),
|
|
|
|
span: *span,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
stack.add_env_var(val.to_string(), vardata);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// In case someone really wants an integer env-var
|
|
|
|
PathMember::Int { val, .. } => {
|
|
|
|
stack.add_env_var(val.to_string(), vardata);
|
|
|
|
}
|
2022-12-06 18:51:55 +01:00
|
|
|
}
|
2023-06-30 21:57:51 +02:00
|
|
|
} else {
|
|
|
|
stack.add_var(*var_id, lhs);
|
2022-11-11 08:16:07 +01:00
|
|
|
}
|
2023-06-30 21:57:51 +02:00
|
|
|
Ok(Value::nothing(cell_path.head.span))
|
2022-12-06 18:51:55 +01:00
|
|
|
} else {
|
2023-06-30 21:57:51 +02:00
|
|
|
Err(ShellError::AssignmentRequiresMutableVar {
|
|
|
|
lhs_span: lhs.span,
|
|
|
|
})
|
2022-11-11 08:16:07 +01:00
|
|
|
}
|
2022-11-11 07:51:08 +01:00
|
|
|
}
|
2023-06-30 21:57:51 +02:00
|
|
|
_ => Err(ShellError::AssignmentRequiresVar { lhs_span: lhs.span }),
|
2022-11-11 07:51:08 +01:00
|
|
|
}
|
2023-06-30 21:57:51 +02:00
|
|
|
}
|
2023-03-06 11:31:07 +01:00
|
|
|
_ => Err(ShellError::AssignmentRequiresVar { lhs_span: lhs.span }),
|
2022-11-11 07:51:08 +01:00
|
|
|
}
|
2022-07-02 13:48:43 +02:00
|
|
|
}
|
2021-07-23 07:14:49 +02:00
|
|
|
}
|
2021-07-16 03:10:22 +02:00
|
|
|
}
|
2022-01-12 05:06:56 +01:00
|
|
|
Expr::Subexpression(block_id) => {
|
2021-10-25 08:31:39 +02:00
|
|
|
let block = engine_state.get_block(*block_id);
|
2021-07-23 07:14:49 +02:00
|
|
|
|
2021-10-25 06:01:02 +02:00
|
|
|
// FIXME: protect this collect with ctrl-c
|
2021-11-06 06:50:33 +01:00
|
|
|
Ok(
|
2022-12-07 19:31:57 +01:00
|
|
|
eval_subexpression(engine_state, stack, block, PipelineData::empty())?
|
2021-11-06 06:50:33 +01:00
|
|
|
.into_value(expr.span),
|
|
|
|
)
|
2021-07-23 07:14:49 +02:00
|
|
|
}
|
2022-11-10 09:21:49 +01:00
|
|
|
Expr::RowCondition(block_id) | Expr::Closure(block_id) => {
|
2022-01-12 05:06:56 +01:00
|
|
|
let mut captures = HashMap::new();
|
|
|
|
let block = engine_state.get_block(*block_id);
|
|
|
|
|
|
|
|
for var_id in &block.captures {
|
2022-01-29 14:00:48 +01:00
|
|
|
captures.insert(*var_id, stack.get_var(*var_id, expr.span)?);
|
2022-01-12 05:06:56 +01:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Ok(Value::closure(*block_id, captures, expr.span))
|
2022-01-12 05:06:56 +01:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Expr::Block(block_id) => Ok(Value::block(*block_id, expr.span)),
|
2021-07-23 23:19:30 +02:00
|
|
|
Expr::List(x) => {
|
|
|
|
let mut output = vec![];
|
|
|
|
for expr in x {
|
2021-10-25 08:31:39 +02:00
|
|
|
output.push(eval_expression(engine_state, stack, expr)?);
|
2021-07-23 23:19:30 +02:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Ok(Value::list(output, expr.span))
|
2021-07-23 23:19:30 +02:00
|
|
|
}
|
2021-11-11 00:14:00 +01:00
|
|
|
Expr::Record(fields) => {
|
Create `Record` type (#10103)
# Description
This PR creates a new `Record` type to reduce duplicate code and
possibly bugs as well. (This is an edited version of #9648.)
- `Record` implements `FromIterator` and `IntoIterator` and so can be
iterated over or collected into. For example, this helps with
conversions to and from (hash)maps. (Also, no more
`cols.iter().zip(vals)`!)
- `Record` has a `push(col, val)` function to help insure that the
number of columns is equal to the number of values. I caught a few
potential bugs thanks to this (e.g. in the `ls` command).
- Finally, this PR also adds a `record!` macro that helps simplify
record creation. It is used like so:
```rust
record! {
"key1" => some_value,
"key2" => Value::string("text", span),
"key3" => Value::int(optional_int.unwrap_or(0), span),
"key4" => Value::bool(config.setting, span),
}
```
Since macros hinder formatting, etc., the right hand side values should
be relatively short and sweet like the examples above.
Where possible, prefer `record!` or `.collect()` on an iterator instead
of multiple `Record::push`s, since the first two automatically set the
record capacity and do less work overall.
# User-Facing Changes
Besides the changes in `nu-protocol` the only other breaking changes are
to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
2023-08-24 21:50:29 +02:00
|
|
|
let mut record = Record::new();
|
|
|
|
|
2021-11-11 00:14:00 +01:00
|
|
|
for (col, val) in fields {
|
2022-05-26 02:10:31 +02:00
|
|
|
// avoid duplicate cols.
|
|
|
|
let col_name = eval_expression(engine_state, stack, col)?.as_string()?;
|
Create `Record` type (#10103)
# Description
This PR creates a new `Record` type to reduce duplicate code and
possibly bugs as well. (This is an edited version of #9648.)
- `Record` implements `FromIterator` and `IntoIterator` and so can be
iterated over or collected into. For example, this helps with
conversions to and from (hash)maps. (Also, no more
`cols.iter().zip(vals)`!)
- `Record` has a `push(col, val)` function to help insure that the
number of columns is equal to the number of values. I caught a few
potential bugs thanks to this (e.g. in the `ls` command).
- Finally, this PR also adds a `record!` macro that helps simplify
record creation. It is used like so:
```rust
record! {
"key1" => some_value,
"key2" => Value::string("text", span),
"key3" => Value::int(optional_int.unwrap_or(0), span),
"key4" => Value::bool(config.setting, span),
}
```
Since macros hinder formatting, etc., the right hand side values should
be relatively short and sweet like the examples above.
Where possible, prefer `record!` or `.collect()` on an iterator instead
of multiple `Record::push`s, since the first two automatically set the
record capacity and do less work overall.
# User-Facing Changes
Besides the changes in `nu-protocol` the only other breaking changes are
to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
2023-08-24 21:50:29 +02:00
|
|
|
let pos = record.cols.iter().position(|c| c == &col_name);
|
2022-05-26 02:10:31 +02:00
|
|
|
match pos {
|
|
|
|
Some(index) => {
|
2023-04-01 20:09:33 +02:00
|
|
|
return Err(ShellError::ColumnDefinedTwice {
|
|
|
|
second_use: col.span,
|
|
|
|
first_use: fields[index].0.span,
|
|
|
|
})
|
2022-05-26 02:10:31 +02:00
|
|
|
}
|
|
|
|
None => {
|
Create `Record` type (#10103)
# Description
This PR creates a new `Record` type to reduce duplicate code and
possibly bugs as well. (This is an edited version of #9648.)
- `Record` implements `FromIterator` and `IntoIterator` and so can be
iterated over or collected into. For example, this helps with
conversions to and from (hash)maps. (Also, no more
`cols.iter().zip(vals)`!)
- `Record` has a `push(col, val)` function to help insure that the
number of columns is equal to the number of values. I caught a few
potential bugs thanks to this (e.g. in the `ls` command).
- Finally, this PR also adds a `record!` macro that helps simplify
record creation. It is used like so:
```rust
record! {
"key1" => some_value,
"key2" => Value::string("text", span),
"key3" => Value::int(optional_int.unwrap_or(0), span),
"key4" => Value::bool(config.setting, span),
}
```
Since macros hinder formatting, etc., the right hand side values should
be relatively short and sweet like the examples above.
Where possible, prefer `record!` or `.collect()` on an iterator instead
of multiple `Record::push`s, since the first two automatically set the
record capacity and do less work overall.
# User-Facing Changes
Besides the changes in `nu-protocol` the only other breaking changes are
to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
2023-08-24 21:50:29 +02:00
|
|
|
record.push(col_name, eval_expression(engine_state, stack, val)?);
|
2022-05-26 02:10:31 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-11 00:14:00 +01:00
|
|
|
}
|
|
|
|
|
Create `Record` type (#10103)
# Description
This PR creates a new `Record` type to reduce duplicate code and
possibly bugs as well. (This is an edited version of #9648.)
- `Record` implements `FromIterator` and `IntoIterator` and so can be
iterated over or collected into. For example, this helps with
conversions to and from (hash)maps. (Also, no more
`cols.iter().zip(vals)`!)
- `Record` has a `push(col, val)` function to help insure that the
number of columns is equal to the number of values. I caught a few
potential bugs thanks to this (e.g. in the `ls` command).
- Finally, this PR also adds a `record!` macro that helps simplify
record creation. It is used like so:
```rust
record! {
"key1" => some_value,
"key2" => Value::string("text", span),
"key3" => Value::int(optional_int.unwrap_or(0), span),
"key4" => Value::bool(config.setting, span),
}
```
Since macros hinder formatting, etc., the right hand side values should
be relatively short and sweet like the examples above.
Where possible, prefer `record!` or `.collect()` on an iterator instead
of multiple `Record::push`s, since the first two automatically set the
record capacity and do less work overall.
# User-Facing Changes
Besides the changes in `nu-protocol` the only other breaking changes are
to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
2023-08-24 21:50:29 +02:00
|
|
|
Ok(Value::record(record, expr.span))
|
2021-11-11 00:14:00 +01:00
|
|
|
}
|
2021-08-28 21:17:30 +02:00
|
|
|
Expr::Table(headers, vals) => {
|
|
|
|
let mut output_headers = vec![];
|
|
|
|
for expr in headers {
|
2021-10-25 08:31:39 +02:00
|
|
|
output_headers.push(eval_expression(engine_state, stack, expr)?.as_string()?);
|
2021-08-28 21:17:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut output_rows = vec![];
|
|
|
|
for val in vals {
|
|
|
|
let mut row = vec![];
|
|
|
|
for expr in val {
|
2021-10-25 08:31:39 +02:00
|
|
|
row.push(eval_expression(engine_state, stack, expr)?);
|
2021-08-28 21:17:30 +02:00
|
|
|
}
|
Create `Record` type (#10103)
# Description
This PR creates a new `Record` type to reduce duplicate code and
possibly bugs as well. (This is an edited version of #9648.)
- `Record` implements `FromIterator` and `IntoIterator` and so can be
iterated over or collected into. For example, this helps with
conversions to and from (hash)maps. (Also, no more
`cols.iter().zip(vals)`!)
- `Record` has a `push(col, val)` function to help insure that the
number of columns is equal to the number of values. I caught a few
potential bugs thanks to this (e.g. in the `ls` command).
- Finally, this PR also adds a `record!` macro that helps simplify
record creation. It is used like so:
```rust
record! {
"key1" => some_value,
"key2" => Value::string("text", span),
"key3" => Value::int(optional_int.unwrap_or(0), span),
"key4" => Value::bool(config.setting, span),
}
```
Since macros hinder formatting, etc., the right hand side values should
be relatively short and sweet like the examples above.
Where possible, prefer `record!` or `.collect()` on an iterator instead
of multiple `Record::push`s, since the first two automatically set the
record capacity and do less work overall.
# User-Facing Changes
Besides the changes in `nu-protocol` the only other breaking changes are
to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
2023-08-24 21:50:29 +02:00
|
|
|
output_rows.push(Value::record(
|
|
|
|
Record {
|
|
|
|
cols: output_headers.clone(),
|
|
|
|
vals: row,
|
|
|
|
},
|
|
|
|
expr.span,
|
|
|
|
));
|
2021-08-28 21:17:30 +02:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Ok(Value::list(output_rows, expr.span))
|
2021-08-28 21:17:30 +02:00
|
|
|
}
|
2021-10-25 08:31:39 +02:00
|
|
|
Expr::Keyword(_, _, expr) => eval_expression(engine_state, stack, expr),
|
2021-12-25 21:50:02 +01:00
|
|
|
Expr::StringInterpolation(exprs) => {
|
|
|
|
let mut parts = vec![];
|
|
|
|
for expr in exprs {
|
|
|
|
parts.push(eval_expression(engine_state, stack, expr)?);
|
|
|
|
}
|
|
|
|
|
2022-04-19 00:28:01 +02:00
|
|
|
let config = engine_state.get_config();
|
2021-12-25 21:50:02 +01:00
|
|
|
|
|
|
|
parts
|
|
|
|
.into_iter()
|
|
|
|
.into_pipeline_data(None)
|
2022-04-19 00:28:01 +02:00
|
|
|
.collect_string("", config)
|
2023-09-03 16:27:29 +02:00
|
|
|
.map(|x| Value::string(x, expr.span))
|
2021-12-25 21:50:02 +01:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Expr::String(s) => Ok(Value::string(s.clone(), expr.span)),
|
2022-01-16 14:55:56 +01:00
|
|
|
Expr::Filepath(s) => {
|
|
|
|
let cwd = current_dir_str(engine_state, stack)?;
|
|
|
|
let path = expand_path_with(s, cwd);
|
|
|
|
|
Reduced LOC by replacing several instances of `Value::Int {}`, `Value::Float{}`, `Value::Bool {}`, and `Value::String {}` with `Value::int()`, `Value::float()`, `Value::boolean()` and `Value::string()` (#7412)
# Description
While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
val: a,
span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.
# User-Facing Changes
None.
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 17:37:51 +01:00
|
|
|
Ok(Value::string(path.to_string_lossy(), expr.span))
|
2022-01-16 14:55:56 +01:00
|
|
|
}
|
2022-04-22 22:18:51 +02:00
|
|
|
Expr::Directory(s) => {
|
2022-04-23 01:48:10 +02:00
|
|
|
if s == "-" {
|
Reduced LOC by replacing several instances of `Value::Int {}`, `Value::Float{}`, `Value::Bool {}`, and `Value::String {}` with `Value::int()`, `Value::float()`, `Value::boolean()` and `Value::string()` (#7412)
# Description
While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
val: a,
span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.
# User-Facing Changes
None.
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 17:37:51 +01:00
|
|
|
Ok(Value::string("-", expr.span))
|
2022-04-23 01:48:10 +02:00
|
|
|
} else {
|
|
|
|
let cwd = current_dir_str(engine_state, stack)?;
|
|
|
|
let path = expand_path_with(s, cwd);
|
2022-04-22 22:18:51 +02:00
|
|
|
|
Reduced LOC by replacing several instances of `Value::Int {}`, `Value::Float{}`, `Value::Bool {}`, and `Value::String {}` with `Value::int()`, `Value::float()`, `Value::boolean()` and `Value::string()` (#7412)
# Description
While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
val: a,
span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.
# User-Facing Changes
None.
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 17:37:51 +01:00
|
|
|
Ok(Value::string(path.to_string_lossy(), expr.span))
|
2022-04-23 01:48:10 +02:00
|
|
|
}
|
2022-04-22 22:18:51 +02:00
|
|
|
}
|
2022-01-16 14:55:56 +01:00
|
|
|
Expr::GlobPattern(s) => {
|
|
|
|
let cwd = current_dir_str(engine_state, stack)?;
|
|
|
|
let path = expand_path_with(s, cwd);
|
|
|
|
|
Reduced LOC by replacing several instances of `Value::Int {}`, `Value::Float{}`, `Value::Bool {}`, and `Value::String {}` with `Value::int()`, `Value::float()`, `Value::boolean()` and `Value::string()` (#7412)
# Description
While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
val: a,
span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.
# User-Facing Changes
None.
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 17:37:51 +01:00
|
|
|
Ok(Value::string(path.to_string_lossy(), expr.span))
|
2022-01-16 14:55:56 +01:00
|
|
|
}
|
2023-09-03 16:27:29 +02:00
|
|
|
Expr::Signature(_) => Ok(Value::nothing(expr.span)),
|
|
|
|
Expr::Garbage => Ok(Value::nothing(expr.span)),
|
|
|
|
Expr::Nothing => Ok(Value::nothing(expr.span)),
|
2021-07-16 03:10:22 +02:00
|
|
|
}
|
2021-07-23 07:14:49 +02:00
|
|
|
}
|
|
|
|
|
2022-01-26 20:00:25 +01:00
|
|
|
/// Checks the expression to see if it's a internal or external call. If so, passes the input
|
|
|
|
/// into the call and gets out the result
|
|
|
|
/// Otherwise, invokes the expression
|
2022-07-20 14:44:42 +02:00
|
|
|
///
|
2022-11-24 04:58:15 +01:00
|
|
|
/// It returns PipelineData with a boolean flag, indicating if the external failed to run.
|
2022-07-20 14:44:42 +02:00
|
|
|
/// The boolean flag **may only be true** for external calls, for internal calls, it always to be false.
|
2022-01-26 20:00:25 +01:00
|
|
|
pub fn eval_expression_with_input(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
expr: &Expression,
|
|
|
|
mut input: PipelineData,
|
2022-02-21 23:22:21 +01:00
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
2022-07-20 14:44:42 +02:00
|
|
|
) -> Result<(PipelineData, bool), ShellError> {
|
2022-01-26 20:00:25 +01:00
|
|
|
match expr {
|
|
|
|
Expression {
|
|
|
|
expr: Expr::Call(call),
|
|
|
|
..
|
|
|
|
} => {
|
2022-02-21 23:22:21 +01:00
|
|
|
if !redirect_stdout || redirect_stderr {
|
|
|
|
// we're doing something different than the defaults
|
|
|
|
let mut call = call.clone();
|
|
|
|
call.redirect_stdout = redirect_stdout;
|
|
|
|
call.redirect_stderr = redirect_stderr;
|
|
|
|
input = eval_call(engine_state, stack, &call, input)?;
|
|
|
|
} else {
|
|
|
|
input = eval_call(engine_state, stack, call, input)?;
|
|
|
|
}
|
2022-01-26 20:00:25 +01:00
|
|
|
}
|
|
|
|
Expression {
|
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 04:51:57 +01:00
|
|
|
expr: Expr::ExternalCall(head, args, is_subexpression),
|
2022-01-26 20:00:25 +01:00
|
|
|
..
|
|
|
|
} => {
|
2022-09-30 14:13:46 +02:00
|
|
|
input = eval_external(
|
2022-02-21 23:22:21 +01:00
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
head,
|
|
|
|
args,
|
|
|
|
input,
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
RedirectTarget::Piped(redirect_stdout, redirect_stderr),
|
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 04:51:57 +01:00
|
|
|
*is_subexpression,
|
2022-02-21 23:22:21 +01:00
|
|
|
)?;
|
2022-01-26 20:00:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Expression {
|
|
|
|
expr: Expr::Subexpression(block_id),
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
let block = engine_state.get_block(*block_id);
|
|
|
|
|
|
|
|
// FIXME: protect this collect with ctrl-c
|
|
|
|
input = eval_subexpression(engine_state, stack, block, input)?;
|
|
|
|
}
|
|
|
|
|
2023-03-22 21:14:10 +01:00
|
|
|
elem @ Expression {
|
|
|
|
expr: Expr::FullCellPath(full_cell_path),
|
|
|
|
..
|
|
|
|
} => match &full_cell_path.head {
|
|
|
|
Expression {
|
|
|
|
expr: Expr::Subexpression(block_id),
|
|
|
|
span,
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
let block = engine_state.get_block(*block_id);
|
|
|
|
|
|
|
|
// FIXME: protect this collect with ctrl-c
|
|
|
|
input = eval_subexpression(engine_state, stack, block, input)?;
|
|
|
|
let value = input.into_value(*span);
|
|
|
|
input = value
|
|
|
|
.follow_cell_path(&full_cell_path.tail, false)?
|
|
|
|
.into_pipeline_data()
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
input = eval_expression(engine_state, stack, elem)?.into_pipeline_data();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2022-01-26 20:00:25 +01:00
|
|
|
elem => {
|
|
|
|
input = eval_expression(engine_state, stack, elem)?.into_pipeline_data();
|
|
|
|
}
|
2022-09-30 14:13:46 +02:00
|
|
|
};
|
|
|
|
|
2023-03-16 19:07:32 +01:00
|
|
|
Ok(might_consume_external_result(input))
|
2022-09-30 14:13:46 +02:00
|
|
|
}
|
2022-01-26 20:00:25 +01:00
|
|
|
|
2022-12-14 19:25:32 +01:00
|
|
|
// Try to catch and detect if external command runs to failed.
|
2022-09-30 14:13:46 +02:00
|
|
|
fn might_consume_external_result(input: PipelineData) -> (PipelineData, bool) {
|
2022-12-14 19:25:32 +01:00
|
|
|
input.is_external_failed()
|
2022-01-26 20:00:25 +01:00
|
|
|
}
|
|
|
|
|
2023-10-03 06:57:21 +02:00
|
|
|
fn eval_element_with_input(
|
2022-11-18 22:46:48 +01:00
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
element: &PipelineElement,
|
2023-01-30 16:49:31 +01:00
|
|
|
mut input: PipelineData,
|
2022-11-18 22:46:48 +01:00
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
|
|
|
) -> Result<(PipelineData, bool), ShellError> {
|
|
|
|
match element {
|
2022-12-13 04:36:13 +01:00
|
|
|
PipelineElement::Expression(_, expr) => eval_expression_with_input(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
expr,
|
|
|
|
input,
|
|
|
|
redirect_stdout,
|
|
|
|
redirect_stderr,
|
|
|
|
),
|
2022-11-22 19:26:13 +01:00
|
|
|
PipelineElement::Redirection(span, redirection, expr) => match &expr.expr {
|
2023-07-20 13:56:46 +02:00
|
|
|
Expr::String(_)
|
|
|
|
| Expr::FullCellPath(_)
|
|
|
|
| Expr::StringInterpolation(_)
|
|
|
|
| Expr::Filepath(_) => {
|
2023-01-30 16:49:31 +01:00
|
|
|
let exit_code = match &mut input {
|
|
|
|
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
|
|
|
|
_ => None,
|
|
|
|
};
|
2022-11-22 19:26:13 +01:00
|
|
|
let input = match (redirection, input) {
|
|
|
|
(
|
|
|
|
Redirection::Stderr,
|
|
|
|
PipelineData::ExternalStream {
|
|
|
|
stderr,
|
|
|
|
exit_code,
|
|
|
|
span,
|
|
|
|
metadata,
|
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 04:51:57 +01:00
|
|
|
trim_end_newline,
|
2022-11-22 19:26:13 +01:00
|
|
|
..
|
|
|
|
},
|
|
|
|
) => PipelineData::ExternalStream {
|
|
|
|
stdout: stderr,
|
|
|
|
stderr: None,
|
|
|
|
exit_code,
|
|
|
|
span,
|
|
|
|
metadata,
|
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 04:51:57 +01:00
|
|
|
trim_end_newline,
|
2022-11-22 19:26:13 +01:00
|
|
|
},
|
|
|
|
(_, input) => input,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(save_command) = engine_state.find_decl(b"save", &[]) {
|
|
|
|
eval_call(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
&Call {
|
|
|
|
decl_id: save_command,
|
|
|
|
head: *span,
|
|
|
|
arguments: vec![
|
|
|
|
Argument::Positional(expr.clone()),
|
|
|
|
Argument::Named((
|
|
|
|
Spanned {
|
2022-12-01 13:26:17 +01:00
|
|
|
item: "raw".into(),
|
|
|
|
span: *span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)),
|
|
|
|
Argument::Named((
|
|
|
|
Spanned {
|
|
|
|
item: "force".into(),
|
2022-11-22 19:26:13 +01:00
|
|
|
span: *span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)),
|
|
|
|
],
|
|
|
|
redirect_stdout: false,
|
|
|
|
redirect_stderr: false,
|
2023-04-05 18:56:48 +02:00
|
|
|
parser_info: HashMap::new(),
|
2022-11-22 19:26:13 +01:00
|
|
|
},
|
|
|
|
input,
|
|
|
|
)
|
2023-01-30 16:49:31 +01:00
|
|
|
.map(|_| {
|
|
|
|
// save is internal command, normally it exists with non-ExternalStream
|
|
|
|
// but here in redirection context, we make it returns ExternalStream
|
|
|
|
// So nu handles exit_code correctly
|
|
|
|
(
|
|
|
|
PipelineData::ExternalStream {
|
|
|
|
stdout: None,
|
|
|
|
stderr: None,
|
|
|
|
exit_code,
|
|
|
|
span: *span,
|
|
|
|
metadata: None,
|
|
|
|
trim_end_newline: false,
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
})
|
2022-11-22 19:26:13 +01:00
|
|
|
} else {
|
|
|
|
Err(ShellError::CommandNotFound(*span))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(ShellError::CommandNotFound(*span)),
|
|
|
|
},
|
Support redirect `err` and `out` to different streams (#7685)
# Description
Closes: #7364
# User-Facing Changes
Given the following shell script:
```bash
x=$(printf '=%.0s' {1..100})
echo $x
echo $x 1>&2
```
It supports the following command:
```
bash test.sh out> out.txt err> err.txt
```
Then both `out.txt` and `err.txt` will contain `=`(100 times)
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SeparateRedirection` if we meet two Redirection
token(which is generated by `lex` function),
During converting from lite block to block,
`LiteElement::SeparateRedirection` will be converted to
`PipelineElement::SeparateRedirection`.
Then in the block eval process, if we get
`PipelineElement::SeparateRedirection`, we invoke `save` command with
`--stderr` arguments to acthive our behavior.
## What happened internally?
Take the following command as example:
```
^ls out> out.txt err> err.txt
```
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, LiteCommand { comments: [], parts: [Span { start: 39063, end: 39070 }] })
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(
None,
LiteCommand { comments: [], parts: [Span { start: 38525, end: 38528 }] }),
// new one! two Redirection merged into one SeparateRedirection.
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, LiteCommand { comments: [], parts: [Span { start: 38534, end: 38541 }] }),
err: (Span { start: 38542, end: 38546 }, LiteCommand { comments: [], parts: [Span { start: 38547, end: 38554 }] })
}
]
}]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, Stdout, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, Expression { expr: String("err.txt"), span: Span { start: 39063, end: 39070 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 38526, end: 38528 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 38525, end: 38528 },
ty: Any,
custom_completion: None
}),
// new one! SeparateRedirection
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, Expression { expr: String("out.txt"), span: Span { start: 38534, end: 38541 }, ty: String, custom_completion: None }),
err: (Span { start: 38542, end: 38546 }, Expression { expr: String("err.txt"), span: Span { start: 38547, end: 38554 }, ty: String, custom_completion: None })
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 10:22:30 +01:00
|
|
|
PipelineElement::SeparateRedirection {
|
|
|
|
out: (out_span, out_expr),
|
|
|
|
err: (err_span, err_expr),
|
|
|
|
} => match (&out_expr.expr, &err_expr.expr) {
|
2023-07-20 13:56:46 +02:00
|
|
|
(
|
|
|
|
Expr::String(_)
|
|
|
|
| Expr::FullCellPath(_)
|
|
|
|
| Expr::StringInterpolation(_)
|
|
|
|
| Expr::Filepath(_),
|
|
|
|
Expr::String(_)
|
|
|
|
| Expr::FullCellPath(_)
|
|
|
|
| Expr::StringInterpolation(_)
|
|
|
|
| Expr::Filepath(_),
|
|
|
|
) => {
|
Support redirect `err` and `out` to different streams (#7685)
# Description
Closes: #7364
# User-Facing Changes
Given the following shell script:
```bash
x=$(printf '=%.0s' {1..100})
echo $x
echo $x 1>&2
```
It supports the following command:
```
bash test.sh out> out.txt err> err.txt
```
Then both `out.txt` and `err.txt` will contain `=`(100 times)
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SeparateRedirection` if we meet two Redirection
token(which is generated by `lex` function),
During converting from lite block to block,
`LiteElement::SeparateRedirection` will be converted to
`PipelineElement::SeparateRedirection`.
Then in the block eval process, if we get
`PipelineElement::SeparateRedirection`, we invoke `save` command with
`--stderr` arguments to acthive our behavior.
## What happened internally?
Take the following command as example:
```
^ls out> out.txt err> err.txt
```
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, LiteCommand { comments: [], parts: [Span { start: 39063, end: 39070 }] })
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(
None,
LiteCommand { comments: [], parts: [Span { start: 38525, end: 38528 }] }),
// new one! two Redirection merged into one SeparateRedirection.
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, LiteCommand { comments: [], parts: [Span { start: 38534, end: 38541 }] }),
err: (Span { start: 38542, end: 38546 }, LiteCommand { comments: [], parts: [Span { start: 38547, end: 38554 }] })
}
]
}]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, Stdout, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, Expression { expr: String("err.txt"), span: Span { start: 39063, end: 39070 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 38526, end: 38528 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 38525, end: 38528 },
ty: Any,
custom_completion: None
}),
// new one! SeparateRedirection
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, Expression { expr: String("out.txt"), span: Span { start: 38534, end: 38541 }, ty: String, custom_completion: None }),
err: (Span { start: 38542, end: 38546 }, Expression { expr: String("err.txt"), span: Span { start: 38547, end: 38554 }, ty: String, custom_completion: None })
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 10:22:30 +01:00
|
|
|
if let Some(save_command) = engine_state.find_decl(b"save", &[]) {
|
2023-01-30 16:49:31 +01:00
|
|
|
let exit_code = match &mut input {
|
|
|
|
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
|
|
|
|
_ => None,
|
|
|
|
};
|
Support redirect `err` and `out` to different streams (#7685)
# Description
Closes: #7364
# User-Facing Changes
Given the following shell script:
```bash
x=$(printf '=%.0s' {1..100})
echo $x
echo $x 1>&2
```
It supports the following command:
```
bash test.sh out> out.txt err> err.txt
```
Then both `out.txt` and `err.txt` will contain `=`(100 times)
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SeparateRedirection` if we meet two Redirection
token(which is generated by `lex` function),
During converting from lite block to block,
`LiteElement::SeparateRedirection` will be converted to
`PipelineElement::SeparateRedirection`.
Then in the block eval process, if we get
`PipelineElement::SeparateRedirection`, we invoke `save` command with
`--stderr` arguments to acthive our behavior.
## What happened internally?
Take the following command as example:
```
^ls out> out.txt err> err.txt
```
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, LiteCommand { comments: [], parts: [Span { start: 39063, end: 39070 }] })
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(
None,
LiteCommand { comments: [], parts: [Span { start: 38525, end: 38528 }] }),
// new one! two Redirection merged into one SeparateRedirection.
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, LiteCommand { comments: [], parts: [Span { start: 38534, end: 38541 }] }),
err: (Span { start: 38542, end: 38546 }, LiteCommand { comments: [], parts: [Span { start: 38547, end: 38554 }] })
}
]
}]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, Stdout, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, Expression { expr: String("err.txt"), span: Span { start: 39063, end: 39070 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 38526, end: 38528 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 38525, end: 38528 },
ty: Any,
custom_completion: None
}),
// new one! SeparateRedirection
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, Expression { expr: String("out.txt"), span: Span { start: 38534, end: 38541 }, ty: String, custom_completion: None }),
err: (Span { start: 38542, end: 38546 }, Expression { expr: String("err.txt"), span: Span { start: 38547, end: 38554 }, ty: String, custom_completion: None })
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 10:22:30 +01:00
|
|
|
eval_call(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
&Call {
|
|
|
|
decl_id: save_command,
|
|
|
|
head: *out_span,
|
|
|
|
arguments: vec![
|
|
|
|
Argument::Positional(out_expr.clone()),
|
|
|
|
Argument::Named((
|
|
|
|
Spanned {
|
|
|
|
item: "stderr".into(),
|
|
|
|
span: *err_span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
Some(err_expr.clone()),
|
|
|
|
)),
|
|
|
|
Argument::Named((
|
|
|
|
Spanned {
|
|
|
|
item: "raw".into(),
|
|
|
|
span: *out_span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)),
|
|
|
|
Argument::Named((
|
|
|
|
Spanned {
|
|
|
|
item: "force".into(),
|
|
|
|
span: *out_span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)),
|
|
|
|
],
|
|
|
|
redirect_stdout: false,
|
|
|
|
redirect_stderr: false,
|
2023-04-05 18:56:48 +02:00
|
|
|
parser_info: HashMap::new(),
|
Support redirect `err` and `out` to different streams (#7685)
# Description
Closes: #7364
# User-Facing Changes
Given the following shell script:
```bash
x=$(printf '=%.0s' {1..100})
echo $x
echo $x 1>&2
```
It supports the following command:
```
bash test.sh out> out.txt err> err.txt
```
Then both `out.txt` and `err.txt` will contain `=`(100 times)
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SeparateRedirection` if we meet two Redirection
token(which is generated by `lex` function),
During converting from lite block to block,
`LiteElement::SeparateRedirection` will be converted to
`PipelineElement::SeparateRedirection`.
Then in the block eval process, if we get
`PipelineElement::SeparateRedirection`, we invoke `save` command with
`--stderr` arguments to acthive our behavior.
## What happened internally?
Take the following command as example:
```
^ls out> out.txt err> err.txt
```
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, LiteCommand { comments: [], parts: [Span { start: 39063, end: 39070 }] })
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(
None,
LiteCommand { comments: [], parts: [Span { start: 38525, end: 38528 }] }),
// new one! two Redirection merged into one SeparateRedirection.
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, LiteCommand { comments: [], parts: [Span { start: 38534, end: 38541 }] }),
err: (Span { start: 38542, end: 38546 }, LiteCommand { comments: [], parts: [Span { start: 38547, end: 38554 }] })
}
]
}]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, Stdout, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, Expression { expr: String("err.txt"), span: Span { start: 39063, end: 39070 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 38526, end: 38528 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 38525, end: 38528 },
ty: Any,
custom_completion: None
}),
// new one! SeparateRedirection
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, Expression { expr: String("out.txt"), span: Span { start: 38534, end: 38541 }, ty: String, custom_completion: None }),
err: (Span { start: 38542, end: 38546 }, Expression { expr: String("err.txt"), span: Span { start: 38547, end: 38554 }, ty: String, custom_completion: None })
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 10:22:30 +01:00
|
|
|
},
|
|
|
|
input,
|
|
|
|
)
|
2023-01-30 16:49:31 +01:00
|
|
|
.map(|_| {
|
|
|
|
// save is internal command, normally it exists with non-ExternalStream
|
|
|
|
// but here in redirection context, we make it returns ExternalStream
|
|
|
|
// So nu handles exit_code correctly
|
|
|
|
(
|
|
|
|
PipelineData::ExternalStream {
|
|
|
|
stdout: None,
|
|
|
|
stderr: None,
|
|
|
|
exit_code,
|
|
|
|
span: *out_span,
|
|
|
|
metadata: None,
|
|
|
|
trim_end_newline: false,
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
})
|
Support redirect `err` and `out` to different streams (#7685)
# Description
Closes: #7364
# User-Facing Changes
Given the following shell script:
```bash
x=$(printf '=%.0s' {1..100})
echo $x
echo $x 1>&2
```
It supports the following command:
```
bash test.sh out> out.txt err> err.txt
```
Then both `out.txt` and `err.txt` will contain `=`(100 times)
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SeparateRedirection` if we meet two Redirection
token(which is generated by `lex` function),
During converting from lite block to block,
`LiteElement::SeparateRedirection` will be converted to
`PipelineElement::SeparateRedirection`.
Then in the block eval process, if we get
`PipelineElement::SeparateRedirection`, we invoke `save` command with
`--stderr` arguments to acthive our behavior.
## What happened internally?
Take the following command as example:
```
^ls out> out.txt err> err.txt
```
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, LiteCommand { comments: [], parts: [Span { start: 39063, end: 39070 }] })
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(
None,
LiteCommand { comments: [], parts: [Span { start: 38525, end: 38528 }] }),
// new one! two Redirection merged into one SeparateRedirection.
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, LiteCommand { comments: [], parts: [Span { start: 38534, end: 38541 }] }),
err: (Span { start: 38542, end: 38546 }, LiteCommand { comments: [], parts: [Span { start: 38547, end: 38554 }] })
}
]
}]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, Stdout, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, Expression { expr: String("err.txt"), span: Span { start: 39063, end: 39070 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 38526, end: 38528 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 38525, end: 38528 },
ty: Any,
custom_completion: None
}),
// new one! SeparateRedirection
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, Expression { expr: String("out.txt"), span: Span { start: 38534, end: 38541 }, ty: String, custom_completion: None }),
err: (Span { start: 38542, end: 38546 }, Expression { expr: String("err.txt"), span: Span { start: 38547, end: 38554 }, ty: String, custom_completion: None })
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 10:22:30 +01:00
|
|
|
} else {
|
|
|
|
Err(ShellError::CommandNotFound(*out_span))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(_out_other, err_other) => {
|
|
|
|
if let Expr::String(_) = err_other {
|
|
|
|
Err(ShellError::CommandNotFound(*out_span))
|
|
|
|
} else {
|
|
|
|
Err(ShellError::CommandNotFound(*err_span))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
PipelineElement::SameTargetRedirection {
|
|
|
|
cmd: (cmd_span, cmd_exp),
|
|
|
|
redirection: (redirect_span, redirect_exp),
|
|
|
|
} => {
|
|
|
|
// general idea: eval cmd and call save command to redirect stdout to result.
|
|
|
|
input = match &cmd_exp.expr {
|
|
|
|
Expr::ExternalCall(head, args, is_subexpression) => {
|
|
|
|
// if cmd's expression is ExternalStream, then invoke run-external with
|
|
|
|
// special --redirect-combine flag.
|
|
|
|
eval_external(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
head,
|
|
|
|
args,
|
|
|
|
input,
|
|
|
|
RedirectTarget::CombinedPipe,
|
|
|
|
*is_subexpression,
|
|
|
|
)?
|
|
|
|
}
|
2023-10-25 16:35:51 +02:00
|
|
|
_ => {
|
|
|
|
// we need to redirect output, so the result can be saved and pass to `save` command.
|
|
|
|
eval_element_with_input(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
&PipelineElement::Expression(*cmd_span, cmd_exp.clone()),
|
|
|
|
input,
|
|
|
|
true,
|
|
|
|
redirect_stderr,
|
|
|
|
)
|
|
|
|
.map(|x| x.0)?
|
|
|
|
}
|
Avoid blocking when `o+e>` redirects too much stderr message (#8784)
# Description
Fixes: #8565
Here is another pr #7240 tried to address the issue, but it works in a
wrong way.
After this change `o+e>` won't redirect all stdout message then stderr
message and it works more like how bash does.
# User-Facing Changes
For the given python code:
```python
# test.py
import sys
print('aa'*300, flush=True)
print('bb'*999999, file=sys.stderr, flush=True)
print('cc'*300, flush=True)
```
Running `python test.py out+err> a.txt` shoudn't hang nushell, and
`a.txt` keeps output in the same order
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SameTargetRedirection` if we meet `out+err>` redirection
token(which is generated by lex function),
During converting from lite block to block,
LiteElement::SameTargetRedirection will be converted to
PipelineElement::SameTargetRedirection.
Then in the block eval process, if we get
PipelineElement::SameTargetRedirection, we'll invoke `run-external` with
`--redirect-combine` flag, then pipe the result into save command
## What happened internally?
Take the following command as example:
`^ls o+e> log.txt`
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline {
commands: [
SameTargetRedirection {
cmd: (None, LiteCommand { comments: [], parts: [Span { start: 147945, end: 147948}]}),
redirection: (Span { start: 147949, end: 147957 }, LiteCommand { comments: [], parts: [Span { start: 147958, end: 147965 }]})
}
]
}
]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, StdoutAndStderr, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
SameTargetRedirection {
cmd: (None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 147946, end: 147948 }, ty: String, custom_completion: None}, [], false),
span: Span { start: 147945, end: 147948},
ty: Any, custom_completion: None
}),
redirection: (Span { start: 147949, end: 147957}, Expression {expr: String("log.txt"), span: Span { start: 147958, end: 147965 },ty: String,custom_completion: None}
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-utils/standard_library/tests.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
> 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-18 00:47:03 +02:00
|
|
|
};
|
|
|
|
eval_element_with_input(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
&PipelineElement::Redirection(
|
|
|
|
*redirect_span,
|
|
|
|
Redirection::Stdout,
|
|
|
|
redirect_exp.clone(),
|
|
|
|
),
|
|
|
|
input,
|
|
|
|
redirect_stdout,
|
|
|
|
redirect_stderr,
|
|
|
|
)
|
|
|
|
}
|
2022-12-13 04:36:13 +01:00
|
|
|
PipelineElement::And(_, expr) => eval_expression_with_input(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
expr,
|
|
|
|
input,
|
|
|
|
redirect_stdout,
|
|
|
|
redirect_stderr,
|
|
|
|
),
|
|
|
|
PipelineElement::Or(_, expr) => eval_expression_with_input(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
expr,
|
|
|
|
input,
|
|
|
|
redirect_stdout,
|
|
|
|
redirect_stderr,
|
|
|
|
),
|
2022-11-18 22:46:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-24 21:39:16 +01:00
|
|
|
pub fn eval_block_with_early_return(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
block: &Block,
|
|
|
|
input: PipelineData,
|
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
|
|
|
) -> Result<PipelineData, ShellError> {
|
|
|
|
match eval_block(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
block,
|
|
|
|
input,
|
|
|
|
redirect_stdout,
|
|
|
|
redirect_stderr,
|
|
|
|
) {
|
|
|
|
Err(ShellError::Return(_, value)) => Ok(PipelineData::Value(*value, None)),
|
|
|
|
x => x,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-03 04:15:01 +02:00
|
|
|
pub fn eval_block(
|
2021-10-25 08:31:39 +02:00
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
2021-09-03 04:15:01 +02:00
|
|
|
block: &Block,
|
2021-10-25 06:01:02 +02:00
|
|
|
mut input: PipelineData,
|
2022-02-21 23:22:21 +01:00
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
2021-10-25 06:01:02 +02:00
|
|
|
) -> Result<PipelineData, ShellError> {
|
2023-01-05 03:38:50 +01:00
|
|
|
// if Block contains recursion, make sure we don't recurse too deeply (to avoid stack overflow)
|
|
|
|
if let Some(recursive) = block.recursive {
|
|
|
|
// picked 50 arbitrarily, should work on all architectures
|
|
|
|
const RECURSION_LIMIT: u64 = 50;
|
|
|
|
if recursive {
|
|
|
|
if *stack.recursion_count >= RECURSION_LIMIT {
|
|
|
|
stack.recursion_count = Box::new(0);
|
|
|
|
return Err(ShellError::RecursionLimitReached {
|
|
|
|
recursion_limit: RECURSION_LIMIT,
|
|
|
|
span: block.span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
*stack.recursion_count += 1;
|
|
|
|
}
|
|
|
|
}
|
2023-02-11 22:35:48 +01:00
|
|
|
|
2022-02-15 20:31:14 +01:00
|
|
|
let num_pipelines = block.len();
|
2023-02-11 22:35:48 +01:00
|
|
|
|
2022-12-13 04:36:13 +01:00
|
|
|
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
|
2023-09-29 21:57:15 +02:00
|
|
|
let mut elements_iter = pipeline.elements.iter().peekable();
|
|
|
|
while let Some(element) = elements_iter.next() {
|
2022-11-24 04:58:15 +01:00
|
|
|
let redirect_stderr = redirect_stderr
|
2023-09-29 21:57:15 +02:00
|
|
|
|| (elements_iter.peek().map_or(false, |next_element| {
|
|
|
|
matches!(
|
|
|
|
next_element,
|
2022-11-24 04:58:15 +01:00
|
|
|
PipelineElement::Redirection(_, Redirection::Stderr, _)
|
|
|
|
| PipelineElement::Redirection(_, Redirection::StdoutAndStderr, _)
|
Support redirect `err` and `out` to different streams (#7685)
# Description
Closes: #7364
# User-Facing Changes
Given the following shell script:
```bash
x=$(printf '=%.0s' {1..100})
echo $x
echo $x 1>&2
```
It supports the following command:
```
bash test.sh out> out.txt err> err.txt
```
Then both `out.txt` and `err.txt` will contain `=`(100 times)
## About the change
The core idea is that when doing lite-parsing, introduce a new variant
`LiteElement::SeparateRedirection` if we meet two Redirection
token(which is generated by `lex` function),
During converting from lite block to block,
`LiteElement::SeparateRedirection` will be converted to
`PipelineElement::SeparateRedirection`.
Then in the block eval process, if we get
`PipelineElement::SeparateRedirection`, we invoke `save` command with
`--stderr` arguments to acthive our behavior.
## What happened internally?
Take the following command as example:
```
^ls out> out.txt err> err.txt
```
lex parsing result(`Tokens`) are not changed, but `LiteBlock` and
`Block` is changed after this pr.
### LiteBlock before
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(None, LiteCommand { comments: [], parts: [Span { start: 39041, end: 39044 }] }),
// actually the span of first Redirection is wrong too..
Redirection(Span { start: 39058, end: 39062 }, Stdout, LiteCommand { comments: [], parts: [Span { start: 39050, end: 39057 }] }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, LiteCommand { comments: [], parts: [Span { start: 39063, end: 39070 }] })
]
}]
}
```
### LiteBlock after
```rust
LiteBlock {
block: [
LitePipeline { commands: [
Command(
None,
LiteCommand { comments: [], parts: [Span { start: 38525, end: 38528 }] }),
// new one! two Redirection merged into one SeparateRedirection.
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, LiteCommand { comments: [], parts: [Span { start: 38534, end: 38541 }] }),
err: (Span { start: 38542, end: 38546 }, LiteCommand { comments: [], parts: [Span { start: 38547, end: 38554 }] })
}
]
}]
}
```
### Block before
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 39042, end: 39044 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 39041, end: 39044 },
ty: Any, custom_completion: None
}),
Redirection(Span { start: 39058, end: 39062 }, Stdout, Expression { expr: String("out.txt"), span: Span { start: 39050, end: 39057 }, ty: String, custom_completion: None }),
Redirection(Span { start: 39058, end: 39062 }, Stderr, Expression { expr: String("err.txt"), span: Span { start: 39063, end: 39070 }, ty: String, custom_completion: None })] }
```
### Block after
```rust
Pipeline {
elements: [
Expression(None, Expression {
expr: ExternalCall(Expression { expr: String("ls"), span: Span { start: 38526, end: 38528 }, ty: String, custom_completion: None }, [], false),
span: Span { start: 38525, end: 38528 },
ty: Any,
custom_completion: None
}),
// new one! SeparateRedirection
SeparateRedirection {
out: (Span { start: 38529, end: 38533 }, Expression { expr: String("out.txt"), span: Span { start: 38534, end: 38541 }, ty: String, custom_completion: None }),
err: (Span { start: 38542, end: 38546 }, Expression { expr: String("err.txt"), span: Span { start: 38547, end: 38554 }, ty: String, custom_completion: None })
}
]
}
```
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-01-12 10:22:30 +01:00
|
|
|
| PipelineElement::SeparateRedirection { .. }
|
2023-09-29 21:57:15 +02:00
|
|
|
)
|
|
|
|
}));
|
|
|
|
|
|
|
|
let redirect_stdout = redirect_stdout
|
|
|
|
|| (elements_iter.peek().map_or(false, |next_element| {
|
|
|
|
matches!(
|
|
|
|
next_element,
|
|
|
|
PipelineElement::Redirection(_, Redirection::Stdout, _)
|
|
|
|
| PipelineElement::Redirection(_, Redirection::StdoutAndStderr, _)
|
|
|
|
| PipelineElement::Expression(..)
|
|
|
|
| PipelineElement::SeparateRedirection { .. }
|
|
|
|
)
|
|
|
|
}));
|
2022-11-24 04:58:15 +01:00
|
|
|
|
2022-07-20 14:44:42 +02:00
|
|
|
// if eval internal command failed, it can just make early return with `Err(ShellError)`.
|
2022-11-18 22:46:48 +01:00
|
|
|
let eval_result = eval_element_with_input(
|
2022-02-15 20:31:14 +01:00
|
|
|
engine_state,
|
|
|
|
stack,
|
2023-09-29 21:57:15 +02:00
|
|
|
element,
|
2022-02-15 20:31:14 +01:00
|
|
|
input,
|
2023-09-29 21:57:15 +02:00
|
|
|
redirect_stdout,
|
2022-11-24 04:58:15 +01:00
|
|
|
redirect_stderr,
|
|
|
|
);
|
|
|
|
|
|
|
|
match (eval_result, redirect_stderr) {
|
|
|
|
(Ok((pipeline_data, _)), true) => {
|
|
|
|
input = pipeline_data;
|
|
|
|
|
|
|
|
// external command may runs to failed
|
|
|
|
// make early return so remaining commands will not be executed.
|
|
|
|
// don't return `Err(ShellError)`, so nushell wouldn't show extra error message.
|
|
|
|
}
|
2023-03-12 09:57:27 +01:00
|
|
|
(Err(error), true) => {
|
|
|
|
input = PipelineData::Value(
|
2023-09-03 16:27:29 +02:00
|
|
|
Value::error(
|
|
|
|
error,
|
|
|
|
Span::unknown(), // FIXME: where does this span come from?
|
|
|
|
),
|
2023-03-12 09:57:27 +01:00
|
|
|
None,
|
|
|
|
)
|
|
|
|
}
|
2022-11-24 04:58:15 +01:00
|
|
|
(output, false) => {
|
|
|
|
let output = output?;
|
|
|
|
input = output.0;
|
2022-12-13 04:36:13 +01:00
|
|
|
// external command may runs to failed
|
2022-11-24 04:58:15 +01:00
|
|
|
// make early return so remaining commands will not be executed.
|
|
|
|
// don't return `Err(ShellError)`, so nushell wouldn't show extra error message.
|
|
|
|
if output.1 {
|
|
|
|
return Ok(input);
|
|
|
|
}
|
|
|
|
}
|
2022-07-20 14:44:42 +02:00
|
|
|
}
|
2021-11-06 06:50:33 +01:00
|
|
|
}
|
2022-02-06 03:03:06 +01:00
|
|
|
|
2022-02-15 20:31:14 +01:00
|
|
|
if pipeline_idx < (num_pipelines) - 1 {
|
2022-12-13 04:36:13 +01:00
|
|
|
match input {
|
|
|
|
PipelineData::Value(Value::Nothing { .. }, ..) => {}
|
|
|
|
PipelineData::ExternalStream {
|
|
|
|
ref mut exit_code, ..
|
|
|
|
} => {
|
|
|
|
let exit_code = exit_code.take();
|
|
|
|
|
2023-03-16 23:53:46 +01:00
|
|
|
input.drain()?;
|
2022-12-12 21:53:46 +01:00
|
|
|
|
2022-12-13 04:36:13 +01:00
|
|
|
if let Some(exit_code) = exit_code {
|
|
|
|
let mut v: Vec<_> = exit_code.collect();
|
2022-12-12 21:53:46 +01:00
|
|
|
|
2022-12-13 04:36:13 +01:00
|
|
|
if let Some(v) = v.pop() {
|
|
|
|
stack.add_env_var("LAST_EXIT_CODE".into(), v);
|
|
|
|
}
|
|
|
|
}
|
2022-12-12 21:53:46 +01:00
|
|
|
}
|
2023-03-16 23:53:46 +01:00
|
|
|
_ => input.drain()?,
|
2022-02-06 03:03:06 +01:00
|
|
|
}
|
|
|
|
|
2022-12-13 04:36:13 +01:00
|
|
|
input = PipelineData::empty()
|
2022-02-06 03:03:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-11 20:50:03 +02:00
|
|
|
Ok(input)
|
2022-02-06 03:03:06 +01:00
|
|
|
}
|
|
|
|
|
2021-11-06 06:50:33 +01:00
|
|
|
pub fn eval_subexpression(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
block: &Block,
|
|
|
|
mut input: PipelineData,
|
|
|
|
) -> Result<PipelineData, ShellError> {
|
2022-02-15 20:31:14 +01:00
|
|
|
for pipeline in block.pipelines.iter() {
|
2023-03-16 19:07:32 +01:00
|
|
|
for expr in pipeline.elements.iter() {
|
|
|
|
input = eval_element_with_input(engine_state, stack, expr, input, true, false)?.0
|
2021-07-16 03:10:22 +02:00
|
|
|
}
|
|
|
|
}
|
2021-07-23 07:14:49 +02:00
|
|
|
|
2021-09-03 04:15:01 +02:00
|
|
|
Ok(input)
|
2021-07-16 03:10:22 +02:00
|
|
|
}
|
2021-10-05 04:27:39 +02:00
|
|
|
|
2021-10-29 20:15:17 +02:00
|
|
|
pub fn eval_variable(
|
2021-11-02 04:08:05 +01:00
|
|
|
engine_state: &EngineState,
|
2021-10-29 20:15:17 +02:00
|
|
|
stack: &Stack,
|
|
|
|
var_id: VarId,
|
|
|
|
span: Span,
|
|
|
|
) -> Result<Value, ShellError> {
|
2022-01-29 14:00:48 +01:00
|
|
|
match var_id {
|
LazyRecord (#7619)
This is an attempt to implement a new `Value::LazyRecord` variant for
performance reasons.
`LazyRecord` is like a regular `Record`, but it's possible to access
individual columns without evaluating other columns. I've implemented
`LazyRecord` for the special `$nu` variable; accessing `$nu` is
relatively slow because of all the information in `scope`, and [`$nu`
accounts for about 2/3 of Nu's startup time on
Linux](https://github.com/nushell/nushell/issues/6677#issuecomment-1364618122).
### Benchmarks
I ran some benchmarks on my desktop (Linux, 12900K) and the results are
very pleasing.
Nu's time to start up and run a command (`cargo build --release;
hyperfine 'target/release/nu -c "echo \"Hello, world!\""' --shell=none
--warmup 10`) goes from **8.8ms to 3.2ms, about 2.8x faster**.
Tests are also much faster! Running `cargo nextest` (with our very slow
`proptest` tests disabled) goes from **7.2s to 4.4s (1.6x faster)**,
because most tests involve launching a new instance of Nu.
### Design (updated)
I've added a new `LazyRecord` trait and added a `Value` variant wrapping
those trait objects, much like `CustomValue`. `LazyRecord`
implementations must implement these 2 functions:
```rust
// All column names
fn column_names(&self) -> Vec<&'static str>;
// Get 1 specific column value
fn get_column_value(&self, column: &str) -> Result<Value, ShellError>;
```
### Serializability
`Value` variants must implement `Serializable` and `Deserializable`, which poses some problems because I want to use unserializable things like `EngineState` in `LazyRecord`s. To work around this, I basically lie to the type system:
1. Add `#[typetag::serde(tag = "type")]` to `LazyRecord` to make it serializable
2. Any unserializable fields in `LazyRecord` implementations get marked with `#[serde(skip)]`
3. At the point where a `LazyRecord` normally would get serialized and sent to a plugin, I instead collect it into a regular `Value::Record` (which can be serialized)
2023-01-19 04:27:26 +01:00
|
|
|
// $nu
|
2023-09-01 08:18:55 +02:00
|
|
|
nu_protocol::NU_VARIABLE_ID => {
|
|
|
|
if let Some(val) = engine_state.get_constant(var_id) {
|
|
|
|
Ok(val.clone())
|
|
|
|
} else {
|
|
|
|
Err(ShellError::VariableNotFoundAtRuntime { span })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// $env
|
2022-01-29 14:00:48 +01:00
|
|
|
ENV_VARIABLE_ID => {
|
|
|
|
let env_vars = stack.get_env_vars(engine_state);
|
|
|
|
let env_columns = env_vars.keys();
|
|
|
|
let env_values = env_vars.values();
|
|
|
|
|
|
|
|
let mut pairs = env_columns
|
|
|
|
.map(|x| x.to_string())
|
|
|
|
.zip(env_values.cloned())
|
|
|
|
.collect::<Vec<(String, Value)>>();
|
|
|
|
|
|
|
|
pairs.sort_by(|a, b| a.0.cmp(&b.0));
|
|
|
|
|
Create `Record` type (#10103)
# Description
This PR creates a new `Record` type to reduce duplicate code and
possibly bugs as well. (This is an edited version of #9648.)
- `Record` implements `FromIterator` and `IntoIterator` and so can be
iterated over or collected into. For example, this helps with
conversions to and from (hash)maps. (Also, no more
`cols.iter().zip(vals)`!)
- `Record` has a `push(col, val)` function to help insure that the
number of columns is equal to the number of values. I caught a few
potential bugs thanks to this (e.g. in the `ls` command).
- Finally, this PR also adds a `record!` macro that helps simplify
record creation. It is used like so:
```rust
record! {
"key1" => some_value,
"key2" => Value::string("text", span),
"key3" => Value::int(optional_int.unwrap_or(0), span),
"key4" => Value::bool(config.setting, span),
}
```
Since macros hinder formatting, etc., the right hand side values should
be relatively short and sweet like the examples above.
Where possible, prefer `record!` or `.collect()` on an iterator instead
of multiple `Record::push`s, since the first two automatically set the
record capacity and do less work overall.
# User-Facing Changes
Besides the changes in `nu-protocol` the only other breaking changes are
to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
2023-08-24 21:50:29 +02:00
|
|
|
Ok(Value::record(pairs.into_iter().collect(), span))
|
2022-01-29 14:00:48 +01:00
|
|
|
}
|
|
|
|
var_id => stack.get_var(var_id, span),
|
2021-10-29 20:15:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-24 22:48:05 +02:00
|
|
|
fn compute(size: i64, unit: Unit, span: Span) -> Result<Value, ShellError> {
|
2023-05-12 19:18:11 +02:00
|
|
|
unit.to_value(size, span)
|
2021-10-05 04:27:39 +02:00
|
|
|
}
|