2022-11-11 22:20:28 +00:00
|
|
|
use crate::{current_dir_str, get_full_help, scope::create_scope};
|
2022-01-16 13:55:56 +00:00
|
|
|
use nu_path::expand_path_with;
|
2021-10-25 04:01:02 +00:00
|
|
|
use nu_protocol::{
|
2022-11-11 07:16:07 +00:00
|
|
|
ast::{
|
2022-11-22 18:26:13 +00:00
|
|
|
Argument, Assignment, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math,
|
|
|
|
Operator, PathMember, PipelineElement, Redirection,
|
2022-11-11 07:16:07 +00:00
|
|
|
},
|
2022-11-11 22:20:28 +00:00
|
|
|
engine::{EngineState, Stack},
|
2022-12-14 18:25:32 +00:00
|
|
|
Config, HistoryFileFormat, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
|
|
|
Range, ShellError, Span, Spanned, Unit, Value, VarId, ENV_VARIABLE_ID,
|
2021-10-25 04:01:02 +00:00
|
|
|
};
|
2022-05-17 18:28:18 +00:00
|
|
|
use nu_utils::stdout_write_all_and_flush;
|
2022-04-23 21:33:27 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use sysinfo::SystemExt;
|
2021-10-13 17:53:27 +00:00
|
|
|
|
2021-08-15 22:33:34 +00:00
|
|
|
pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
|
2021-07-23 05:14:49 +00:00
|
|
|
match op {
|
|
|
|
Expression {
|
|
|
|
expr: Expr::Operator(operator),
|
|
|
|
..
|
|
|
|
} => Ok(operator.clone()),
|
2021-09-05 23:16:27 +00:00
|
|
|
Expression { span, expr, .. } => {
|
|
|
|
Err(ShellError::UnknownOperator(format!("{:?}", expr), *span))
|
|
|
|
}
|
2021-07-16 01:10:22 +00:00
|
|
|
}
|
2021-07-23 05:14:49 +00:00
|
|
|
}
|
2021-07-16 01:10:22 +00:00
|
|
|
|
2022-03-10 07:49:02 +00:00
|
|
|
pub fn eval_call(
|
2021-10-25 06:31:39 +00:00
|
|
|
engine_state: &EngineState,
|
2021-12-25 19:39:42 +00:00
|
|
|
caller_stack: &mut Stack,
|
2021-10-25 04:01:02 +00:00
|
|
|
call: &Call,
|
|
|
|
input: PipelineData,
|
|
|
|
) -> Result<PipelineData, ShellError> {
|
2022-12-15 17:39:24 +00:00
|
|
|
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
|
|
|
return Ok(Value::Nothing { span: call.head }.into_pipeline_data());
|
2022-05-02 08:18:25 +00:00
|
|
|
}
|
2021-12-06 04:09:49 +00:00
|
|
|
let decl = engine_state.get_decl(call.decl_id);
|
2021-11-28 19:35:02 +00:00
|
|
|
|
2022-04-09 05:17:48 +00:00
|
|
|
if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
|
2022-02-10 02:24:29 +00:00
|
|
|
let mut signature = decl.signature();
|
|
|
|
signature.usage = decl.usage().to_string();
|
|
|
|
signature.extra_usage = decl.extra_usage().to_string();
|
|
|
|
|
2022-11-20 13:22:42 +00:00
|
|
|
let full_help = get_full_help(
|
|
|
|
&signature,
|
|
|
|
&decl.examples(),
|
|
|
|
engine_state,
|
|
|
|
caller_stack,
|
|
|
|
decl.is_parser_keyword(),
|
|
|
|
);
|
2021-10-13 17:53:27 +00:00
|
|
|
Ok(Value::String {
|
|
|
|
val: full_help,
|
|
|
|
span: call.head,
|
2021-10-25 04:01:02 +00:00
|
|
|
}
|
|
|
|
.into_pipeline_data())
|
2021-10-13 17:53:27 +00:00
|
|
|
} else if let Some(block_id) = decl.get_block_id() {
|
2021-10-25 20:04:23 +00:00
|
|
|
let block = engine_state.get_block(block_id);
|
|
|
|
|
2022-01-12 04:06:56 +00:00
|
|
|
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
2021-12-20 06:58:09 +00:00
|
|
|
|
|
|
|
for (param_idx, param) in decl
|
|
|
|
.signature()
|
|
|
|
.required_positional
|
|
|
|
.iter()
|
|
|
|
.chain(decl.signature().optional_positional.iter())
|
|
|
|
.enumerate()
|
|
|
|
{
|
2021-07-23 21:19:30 +00:00
|
|
|
let var_id = param
|
|
|
|
.var_id
|
|
|
|
.expect("internal error: all custom parameters must have var_ids");
|
|
|
|
|
2022-04-09 02:55:02 +00:00
|
|
|
if let Some(arg) = call.positional_nth(param_idx) {
|
2021-12-25 19:39:42 +00:00
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
|
|
|
callee_stack.add_var(var_id, result);
|
2022-03-07 20:08:56 +00:00
|
|
|
} else if let Some(arg) = ¶m.default_value {
|
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
|
|
|
callee_stack.add_var(var_id, result);
|
2021-12-20 06:58:09 +00:00
|
|
|
} else {
|
2021-12-25 19:39:42 +00:00
|
|
|
callee_stack.add_var(var_id, Value::nothing(call.head));
|
2021-12-20 06:58:09 +00:00
|
|
|
}
|
2021-07-23 21:19:30 +00:00
|
|
|
}
|
2021-09-07 03:37:02 +00:00
|
|
|
|
|
|
|
if let Some(rest_positional) = decl.signature().rest_positional {
|
|
|
|
let mut rest_items = vec![];
|
|
|
|
|
2022-04-09 02:55:02 +00:00
|
|
|
for arg in call.positional_iter().skip(
|
2021-09-07 03:37:02 +00:00
|
|
|
decl.signature().required_positional.len()
|
|
|
|
+ decl.signature().optional_positional.len(),
|
|
|
|
) {
|
2021-12-25 19:39:42 +00:00
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
2021-09-07 03:37:02 +00:00
|
|
|
rest_items.push(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
let span = if let Some(rest_item) = rest_items.first() {
|
2021-10-11 18:45:31 +00:00
|
|
|
rest_item.span()?
|
2021-09-07 03:37:02 +00:00
|
|
|
} else {
|
2021-12-19 07:46:13 +00:00
|
|
|
call.head
|
2021-09-07 03:37:02 +00:00
|
|
|
};
|
|
|
|
|
2021-12-25 19:39:42 +00:00
|
|
|
callee_stack.add_var(
|
2021-09-07 03:37:02 +00:00
|
|
|
rest_positional
|
|
|
|
.var_id
|
|
|
|
.expect("Internal error: rest positional parameter lacks var_id"),
|
|
|
|
Value::List {
|
2021-09-07 07:07:11 +00:00
|
|
|
vals: rest_items,
|
2021-09-07 03:37:02 +00:00
|
|
|
span,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
2021-10-11 21:17:45 +00:00
|
|
|
|
|
|
|
for named in decl.signature().named {
|
2021-10-13 17:53:27 +00:00
|
|
|
if let Some(var_id) = named.var_id {
|
|
|
|
let mut found = false;
|
2022-04-09 02:55:02 +00:00
|
|
|
for call_named in call.named_iter() {
|
2021-10-13 17:53:27 +00:00
|
|
|
if call_named.0.item == named.long {
|
2022-04-09 05:17:48 +00:00
|
|
|
if let Some(arg) = &call_named.2 {
|
2021-12-25 19:39:42 +00:00
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
2021-10-12 04:49:17 +00:00
|
|
|
|
2022-03-07 20:08:56 +00:00
|
|
|
callee_stack.add_var(var_id, result);
|
|
|
|
} else if let Some(arg) = &named.default_value {
|
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
|
|
|
|
2021-12-25 19:39:42 +00:00
|
|
|
callee_stack.add_var(var_id, result);
|
2021-10-13 17:53:27 +00:00
|
|
|
} else {
|
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 16:37:51 +00:00
|
|
|
callee_stack.add_var(var_id, Value::boolean(true, call.head))
|
2021-10-13 17:53:27 +00:00
|
|
|
}
|
|
|
|
found = true;
|
2021-10-11 21:17:45 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-12 04:49:17 +00:00
|
|
|
|
2022-01-06 20:32:47 +00:00
|
|
|
if !found {
|
|
|
|
if named.arg.is_none() {
|
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 16:37:51 +00:00
|
|
|
callee_stack.add_var(var_id, Value::boolean(false, call.head))
|
2022-03-07 20:08:56 +00:00
|
|
|
} else if let Some(arg) = &named.default_value {
|
|
|
|
let result = eval_expression(engine_state, caller_stack, arg)?;
|
|
|
|
|
|
|
|
callee_stack.add_var(var_id, result);
|
2022-01-06 20:32:47 +00:00
|
|
|
} else {
|
|
|
|
callee_stack.add_var(var_id, Value::Nothing { span: call.head })
|
|
|
|
}
|
2021-10-13 17:53:27 +00:00
|
|
|
}
|
2021-10-12 04:49:17 +00:00
|
|
|
}
|
2021-10-11 21:17:45 +00:00
|
|
|
}
|
2022-02-04 18:02:03 +00: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 20:39:16 +00:00
|
|
|
let result = eval_block_with_early_return(
|
2022-03-02 12:52:24 +00:00
|
|
|
engine_state,
|
|
|
|
&mut callee_stack,
|
|
|
|
block,
|
|
|
|
input,
|
|
|
|
call.redirect_stdout,
|
|
|
|
call.redirect_stderr,
|
|
|
|
);
|
2022-02-04 18:02:03 +00:00
|
|
|
|
2022-01-29 20:45:46 +00:00
|
|
|
if block.redirect_env {
|
2022-08-18 20:24:39 +00:00
|
|
|
redirect_env(engine_state, caller_stack, &callee_stack);
|
2022-01-29 20:45:46 +00:00
|
|
|
}
|
2022-02-04 18:02:03 +00:00
|
|
|
|
2022-01-29 20:45:46 +00:00
|
|
|
result
|
2021-07-29 22:56:51 +00:00
|
|
|
} else {
|
2021-12-25 19:39:42 +00: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 01:10:22 +00:00
|
|
|
}
|
2021-07-23 05:14:49 +00:00
|
|
|
}
|
2021-07-16 01:10:22 +00:00
|
|
|
|
2022-08-18 20:24:39 +00: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 20:32:56 +00:00
|
|
|
// Grab all environment variables from the callee
|
2022-08-18 20:24:39 +00: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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-20 12:44:42 +00:00
|
|
|
/// Eval extarnal expression
|
|
|
|
///
|
|
|
|
/// It returns PipelineData with a boolean flag, indicate that if the external runs to failed.
|
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 03:51:57 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-09-19 21:48:33 +00:00
|
|
|
fn eval_external(
|
2021-10-25 06:31:39 +00:00
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
2022-01-13 08:17:45 +00:00
|
|
|
head: &Expression,
|
2021-10-08 21:51:47 +00:00
|
|
|
args: &[Expression],
|
2021-10-25 04:01:02 +00:00
|
|
|
input: PipelineData,
|
2022-02-21 22:22:21 +00:00
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
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 03:51:57 +00:00
|
|
|
is_subexpression: bool,
|
2022-09-30 12:13:46 +00:00
|
|
|
) -> Result<PipelineData, ShellError> {
|
2021-10-25 06:31:39 +00:00
|
|
|
let decl_id = engine_state
|
2022-05-07 19:39:22 +00:00
|
|
|
.find_decl("run-external".as_bytes(), &[])
|
2022-01-13 08:17:45 +00:00
|
|
|
.ok_or(ShellError::ExternalNotSupported(head.span))?;
|
2021-09-19 21:48:33 +00:00
|
|
|
|
2021-10-25 06:31:39 +00:00
|
|
|
let command = engine_state.get_decl(decl_id);
|
2021-09-19 21:48:33 +00:00
|
|
|
|
2022-02-04 02:01:45 +00:00
|
|
|
let mut call = Call::new(head.span);
|
2021-10-08 21:51:47 +00:00
|
|
|
|
2022-04-09 02:55:02 +00:00
|
|
|
call.add_positional(head.clone());
|
2021-10-08 21:51:47 +00:00
|
|
|
|
|
|
|
for arg in args {
|
2022-04-09 02:55:02 +00:00
|
|
|
call.add_positional(arg.clone())
|
2021-10-08 21:51:47 +00:00
|
|
|
}
|
2021-09-19 21:48:33 +00:00
|
|
|
|
2022-02-21 22:22:21 +00:00
|
|
|
if redirect_stdout {
|
2022-04-09 02:55:02 +00:00
|
|
|
call.add_named((
|
2021-10-11 21:17:45 +00:00
|
|
|
Spanned {
|
2022-02-21 22:22:21 +00:00
|
|
|
item: "redirect-stdout".into(),
|
|
|
|
span: head.span,
|
|
|
|
},
|
|
|
|
None,
|
2022-04-09 05:17:48 +00:00
|
|
|
None,
|
2022-02-21 22:22:21 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
if redirect_stderr {
|
2022-04-09 02:55:02 +00:00
|
|
|
call.add_named((
|
2022-02-21 22:22:21 +00:00
|
|
|
Spanned {
|
|
|
|
item: "redirect-stderr".into(),
|
2022-01-13 08:17:45 +00:00
|
|
|
span: head.span,
|
2021-10-11 21:17:45 +00:00
|
|
|
},
|
|
|
|
None,
|
2022-04-09 05:17:48 +00:00
|
|
|
None,
|
2021-10-11 21:17:45 +00:00
|
|
|
))
|
2021-09-23 16:42:03 +00: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 03:51:57 +00:00
|
|
|
if is_subexpression {
|
|
|
|
call.add_named((
|
|
|
|
Spanned {
|
|
|
|
item: "trim-end-newline".into(),
|
|
|
|
span: head.span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-09-30 12:13:46 +00:00
|
|
|
command.run(engine_state, stack, &call, input)
|
2021-09-19 21:48:33 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 22:58:15 +00:00
|
|
|
pub fn eval_expression(
|
2021-10-25 06:31:39 +00:00
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
2021-09-02 22:58:15 +00:00
|
|
|
expr: &Expression,
|
|
|
|
) -> Result<Value, ShellError> {
|
2021-07-23 05:14:49 +00:00
|
|
|
match &expr.expr {
|
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 16:37:51 +00:00
|
|
|
Expr::Bool(b) => Ok(Value::boolean(*b, expr.span)),
|
|
|
|
Expr::Int(i) => Ok(Value::int(*i, expr.span)),
|
|
|
|
Expr::Float(f) => Ok(Value::float(*f, expr.span)),
|
2022-02-28 23:31:53 +00:00
|
|
|
Expr::Binary(b) => Ok(Value::Binary {
|
|
|
|
val: b.clone(),
|
|
|
|
span: expr.span,
|
|
|
|
}),
|
2021-10-25 06:31:39 +00:00
|
|
|
Expr::ValueWithUnit(e, unit) => match eval_expression(engine_state, stack, e)? {
|
2021-10-05 02:27:39 +00:00
|
|
|
Value::Int { val, .. } => Ok(compute(val, unit.item, unit.span)),
|
2021-11-06 05:50:33 +00:00
|
|
|
x => Err(ShellError::CantConvert(
|
|
|
|
"unit value".into(),
|
|
|
|
x.get_type().to_string(),
|
|
|
|
e.span,
|
2022-04-18 12:34:10 +00:00
|
|
|
None,
|
2021-11-06 05:50:33 +00:00
|
|
|
)),
|
2021-10-05 02:27:39 +00:00
|
|
|
},
|
2021-09-11 11:13:04 +00:00
|
|
|
Expr::Range(from, next, to, operator) => {
|
2021-09-04 21:52:57 +00:00
|
|
|
let from = if let Some(f) = from {
|
2021-10-25 06:31:39 +00:00
|
|
|
eval_expression(engine_state, stack, f)?
|
2021-09-04 21:52:57 +00:00
|
|
|
} else {
|
2021-12-19 07:46:13 +00:00
|
|
|
Value::Nothing { span: expr.span }
|
2021-09-04 21:52:57 +00:00
|
|
|
};
|
|
|
|
|
2021-09-11 11:13:04 +00:00
|
|
|
let next = if let Some(s) = next {
|
2021-10-25 06:31:39 +00:00
|
|
|
eval_expression(engine_state, stack, s)?
|
2021-09-04 21:52:57 +00:00
|
|
|
} else {
|
2021-12-19 07:46:13 +00:00
|
|
|
Value::Nothing { span: expr.span }
|
2021-09-04 21:52:57 +00:00
|
|
|
};
|
|
|
|
|
2021-09-11 11:13:04 +00:00
|
|
|
let to = if let Some(t) = to {
|
2021-10-25 06:31:39 +00:00
|
|
|
eval_expression(engine_state, stack, t)?
|
2021-09-11 11:13:04 +00:00
|
|
|
} else {
|
2021-12-19 07:46:13 +00:00
|
|
|
Value::Nothing { span: expr.span }
|
2021-09-04 21:52:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Value::Range {
|
2021-09-11 11:13:04 +00:00
|
|
|
val: Box::new(Range::new(expr.span, from, next, to, operator)?),
|
2021-09-04 21:52:57 +00:00
|
|
|
span: expr.span,
|
|
|
|
})
|
|
|
|
}
|
2021-10-29 18:15:17 +00:00
|
|
|
Expr::Var(var_id) => eval_variable(engine_state, stack, *var_id, expr.span),
|
2021-10-25 20:04:23 +00:00
|
|
|
Expr::VarDecl(_) => Ok(Value::Nothing { span: expr.span }),
|
2021-10-02 02:59:11 +00:00
|
|
|
Expr::CellPath(cell_path) => Ok(Value::CellPath {
|
|
|
|
val: cell_path.clone(),
|
|
|
|
span: expr.span,
|
|
|
|
}),
|
2021-09-26 18:39:19 +00:00
|
|
|
Expr::FullCellPath(cell_path) => {
|
2021-10-25 06:31:39 +00:00
|
|
|
let value = eval_expression(engine_state, stack, &cell_path.head)?;
|
2021-09-06 22:02:24 +00:00
|
|
|
|
2022-06-01 13:34:42 +00:00
|
|
|
value.follow_cell_path(&cell_path.tail, false)
|
2021-09-06 22:02:24 +00:00
|
|
|
}
|
2021-12-19 07:46:13 +00:00
|
|
|
Expr::ImportPattern(_) => Ok(Value::Nothing { span: expr.span }),
|
2022-09-04 15:36:42 +00:00
|
|
|
Expr::Overlay(_) => {
|
|
|
|
let name =
|
|
|
|
String::from_utf8_lossy(engine_state.get_span_contents(&expr.span)).to_string();
|
|
|
|
|
|
|
|
Ok(Value::String {
|
|
|
|
val: name,
|
|
|
|
span: expr.span,
|
|
|
|
})
|
|
|
|
}
|
2021-10-25 04:01:02 +00:00
|
|
|
Expr::Call(call) => {
|
|
|
|
// FIXME: protect this collect with ctrl-c
|
2022-12-07 18:31:57 +00:00
|
|
|
Ok(eval_call(engine_state, stack, call, PipelineData::empty())?.into_value(call.head))
|
2021-10-25 04:01:02 +00: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 03:51:57 +00:00
|
|
|
Expr::ExternalCall(head, args, is_subexpression) => {
|
2022-01-13 08:17:45 +00:00
|
|
|
let span = head.span;
|
2021-10-25 04:01:02 +00:00
|
|
|
// FIXME: protect this collect with ctrl-c
|
2021-10-25 21:14:21 +00:00
|
|
|
Ok(eval_external(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
2022-01-13 08:17:45 +00:00
|
|
|
head,
|
2021-10-25 21:14:21 +00:00
|
|
|
args,
|
2022-12-07 18:31:57 +00:00
|
|
|
PipelineData::empty(),
|
2021-12-27 14:58:53 +00:00
|
|
|
false,
|
2022-02-21 22:22:21 +00:00
|
|
|
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 03:51:57 +00:00
|
|
|
*is_subexpression,
|
2021-10-25 21:14:21 +00:00
|
|
|
)?
|
2022-01-13 08:17:45 +00:00
|
|
|
.into_value(span))
|
2021-09-23 16:42:03 +00:00
|
|
|
}
|
2022-02-24 02:02:48 +00:00
|
|
|
Expr::DateTime(dt) => Ok(Value::Date {
|
|
|
|
val: *dt,
|
|
|
|
span: expr.span,
|
|
|
|
}),
|
2021-08-08 21:02:47 +00:00
|
|
|
Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }),
|
2022-04-06 19:10:25 +00:00
|
|
|
Expr::UnaryNot(expr) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, expr)?;
|
|
|
|
match lhs {
|
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 16:37:51 +00:00
|
|
|
Value::Bool { val, .. } => Ok(Value::boolean(!val, expr.span)),
|
2022-04-06 19:10:25 +00:00
|
|
|
_ => Err(ShellError::TypeMismatch("bool".to_string(), expr.span)),
|
|
|
|
}
|
|
|
|
}
|
2021-07-23 05:14:49 +00:00
|
|
|
Expr::BinaryOp(lhs, op, rhs) => {
|
2021-08-10 06:31:34 +00:00
|
|
|
let op_span = op.span;
|
2021-08-15 22:33:34 +00:00
|
|
|
let op = eval_operator(op)?;
|
2021-07-23 05:14:49 +00:00
|
|
|
|
|
|
|
match op {
|
2022-11-11 06:51:08 +00:00
|
|
|
Operator::Boolean(boolean) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
match boolean {
|
|
|
|
Boolean::And => {
|
|
|
|
if lhs.is_false() {
|
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 16:37:51 +00:00
|
|
|
Ok(Value::boolean(false, expr.span))
|
2022-11-11 06:51:08 +00:00
|
|
|
} else {
|
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
|
|
|
lhs.and(op_span, &rhs, expr.span)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Boolean::Or => {
|
|
|
|
if lhs.is_true() {
|
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 16:37:51 +00:00
|
|
|
Ok(Value::boolean(true, expr.span))
|
2022-11-11 06:51:08 +00:00
|
|
|
} else {
|
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
|
|
|
lhs.or(op_span, &rhs, expr.span)
|
|
|
|
}
|
|
|
|
}
|
2022-11-26 16:02:37 +00:00
|
|
|
Boolean::Xor => {
|
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
|
|
|
lhs.xor(op_span, &rhs, expr.span)
|
|
|
|
}
|
2022-02-27 22:02:53 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-11 06:51:08 +00:00
|
|
|
Operator::Math(math) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
2022-02-27 22:02:53 +00:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 06:51:08 +00: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 11:45:20 +00:00
|
|
|
}
|
2022-11-11 06:51:08 +00:00
|
|
|
Operator::Comparison(comparison) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
2022-07-02 18:03:36 +00:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 06:51:08 +00: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 22:30:04 +00:00
|
|
|
Comparison::RegexMatch => {
|
|
|
|
lhs.regex_match(engine_state, op_span, &rhs, false, expr.span)
|
|
|
|
}
|
2022-11-11 06:51:08 +00: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 22:30:04 +00:00
|
|
|
lhs.regex_match(engine_state, op_span, &rhs, true, expr.span)
|
2022-11-11 06:51:08 +00:00
|
|
|
}
|
|
|
|
Comparison::StartsWith => lhs.starts_with(op_span, &rhs, expr.span),
|
|
|
|
Comparison::EndsWith => lhs.ends_with(op_span, &rhs, expr.span),
|
|
|
|
}
|
2022-07-02 18:03:36 +00:00
|
|
|
}
|
2022-11-11 06:51:08 +00:00
|
|
|
Operator::Bits(bits) => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
2022-07-02 11:48:43 +00:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 06:51:08 +00: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 11:48:43 +00:00
|
|
|
}
|
2022-11-11 18:50:43 +00:00
|
|
|
Operator::Assignment(assignment) => {
|
2022-07-02 11:48:43 +00:00
|
|
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
2022-11-11 06:51:08 +00:00
|
|
|
|
2022-11-11 18:50:43 +00: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 16:20:58 +00:00
|
|
|
Assignment::AppendAssign => {
|
|
|
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
|
|
|
lhs.append(op_span, &rhs, op_span)?
|
|
|
|
}
|
2022-11-11 18:50:43 +00:00
|
|
|
};
|
|
|
|
|
2022-11-11 06:51:08 +00: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 {
|
|
|
|
stack.vars.insert(*var_id, rhs);
|
|
|
|
Ok(Value::nothing(lhs.span))
|
|
|
|
} else {
|
|
|
|
Err(ShellError::AssignmentRequiresMutableVar(lhs.span))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Expr::FullCellPath(cell_path) => match &cell_path.head.expr {
|
|
|
|
Expr::Var(var_id) | Expr::VarDecl(var_id) => {
|
2022-12-06 17:51:55 +00:00
|
|
|
// 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 {
|
|
|
|
// 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, .. } => {
|
|
|
|
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-11-11 07:16:07 +00:00
|
|
|
}
|
2022-12-06 17:51:55 +00:00
|
|
|
} else {
|
|
|
|
stack.vars.insert(*var_id, lhs);
|
2022-11-11 07:16:07 +00:00
|
|
|
}
|
2022-11-11 06:51:08 +00:00
|
|
|
Ok(Value::nothing(cell_path.head.span))
|
|
|
|
} else {
|
2022-12-06 17:51:55 +00:00
|
|
|
Err(ShellError::AssignmentRequiresMutableVar(lhs.span))
|
2022-11-11 06:51:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(ShellError::AssignmentRequiresVar(lhs.span)),
|
|
|
|
},
|
|
|
|
_ => Err(ShellError::AssignmentRequiresVar(lhs.span)),
|
|
|
|
}
|
2022-07-02 11:48:43 +00:00
|
|
|
}
|
2021-07-23 05:14:49 +00:00
|
|
|
}
|
2021-07-16 01:10:22 +00:00
|
|
|
}
|
2022-01-12 04:06:56 +00:00
|
|
|
Expr::Subexpression(block_id) => {
|
2021-10-25 06:31:39 +00:00
|
|
|
let block = engine_state.get_block(*block_id);
|
2021-07-23 05:14:49 +00:00
|
|
|
|
2021-10-25 04:01:02 +00:00
|
|
|
// FIXME: protect this collect with ctrl-c
|
2021-11-06 05:50:33 +00:00
|
|
|
Ok(
|
2022-12-07 18:31:57 +00:00
|
|
|
eval_subexpression(engine_state, stack, block, PipelineData::empty())?
|
2021-11-06 05:50:33 +00:00
|
|
|
.into_value(expr.span),
|
|
|
|
)
|
2021-07-23 05:14:49 +00:00
|
|
|
}
|
2022-11-10 08:21:49 +00:00
|
|
|
Expr::RowCondition(block_id) | Expr::Closure(block_id) => {
|
2022-01-12 04:06:56 +00:00
|
|
|
let mut captures = HashMap::new();
|
|
|
|
let block = engine_state.get_block(*block_id);
|
|
|
|
|
|
|
|
for var_id in &block.captures {
|
2022-01-29 13:00:48 +00:00
|
|
|
captures.insert(*var_id, stack.get_var(*var_id, expr.span)?);
|
2022-01-12 04:06:56 +00:00
|
|
|
}
|
2022-11-10 08:21:49 +00:00
|
|
|
Ok(Value::Closure {
|
2022-01-12 04:06:56 +00:00
|
|
|
val: *block_id,
|
|
|
|
captures,
|
|
|
|
span: expr.span,
|
|
|
|
})
|
|
|
|
}
|
2022-11-10 08:21:49 +00:00
|
|
|
Expr::Block(block_id) => Ok(Value::Block {
|
|
|
|
val: *block_id,
|
|
|
|
span: expr.span,
|
|
|
|
}),
|
2021-07-23 21:19:30 +00:00
|
|
|
Expr::List(x) => {
|
|
|
|
let mut output = vec![];
|
|
|
|
for expr in x {
|
2021-10-25 06:31:39 +00:00
|
|
|
output.push(eval_expression(engine_state, stack, expr)?);
|
2021-07-23 21:19:30 +00:00
|
|
|
}
|
2021-08-08 21:02:47 +00:00
|
|
|
Ok(Value::List {
|
2021-09-07 07:07:11 +00:00
|
|
|
vals: output,
|
2021-08-08 21:02:47 +00:00
|
|
|
span: expr.span,
|
|
|
|
})
|
2021-07-23 21:19:30 +00:00
|
|
|
}
|
2021-11-10 23:14:00 +00:00
|
|
|
Expr::Record(fields) => {
|
|
|
|
let mut cols = vec![];
|
|
|
|
let mut vals = vec![];
|
|
|
|
for (col, val) in fields {
|
2022-05-26 00:10:31 +00:00
|
|
|
// avoid duplicate cols.
|
|
|
|
let col_name = eval_expression(engine_state, stack, col)?.as_string()?;
|
|
|
|
let pos = cols.iter().position(|c| c == &col_name);
|
|
|
|
match pos {
|
|
|
|
Some(index) => {
|
|
|
|
vals[index] = eval_expression(engine_state, stack, val)?;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
cols.push(col_name);
|
|
|
|
vals.push(eval_expression(engine_state, stack, val)?);
|
|
|
|
}
|
|
|
|
}
|
2021-11-10 23:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Value::Record {
|
|
|
|
cols,
|
|
|
|
vals,
|
|
|
|
span: expr.span,
|
|
|
|
})
|
|
|
|
}
|
2021-08-28 19:17:30 +00:00
|
|
|
Expr::Table(headers, vals) => {
|
|
|
|
let mut output_headers = vec![];
|
|
|
|
for expr in headers {
|
2021-10-25 06:31:39 +00:00
|
|
|
output_headers.push(eval_expression(engine_state, stack, expr)?.as_string()?);
|
2021-08-28 19:17:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut output_rows = vec![];
|
|
|
|
for val in vals {
|
|
|
|
let mut row = vec![];
|
|
|
|
for expr in val {
|
2021-10-25 06:31:39 +00:00
|
|
|
row.push(eval_expression(engine_state, stack, expr)?);
|
2021-08-28 19:17:30 +00:00
|
|
|
}
|
2021-09-07 07:07:11 +00:00
|
|
|
output_rows.push(Value::Record {
|
|
|
|
cols: output_headers.clone(),
|
|
|
|
vals: row,
|
|
|
|
span: expr.span,
|
|
|
|
});
|
2021-08-28 19:17:30 +00:00
|
|
|
}
|
2021-09-07 07:07:11 +00:00
|
|
|
Ok(Value::List {
|
|
|
|
vals: output_rows,
|
2021-08-28 19:17:30 +00:00
|
|
|
span: expr.span,
|
|
|
|
})
|
|
|
|
}
|
2021-10-25 06:31:39 +00:00
|
|
|
Expr::Keyword(_, _, expr) => eval_expression(engine_state, stack, expr),
|
2021-12-25 20:50:02 +00:00
|
|
|
Expr::StringInterpolation(exprs) => {
|
|
|
|
let mut parts = vec![];
|
|
|
|
for expr in exprs {
|
|
|
|
parts.push(eval_expression(engine_state, stack, expr)?);
|
|
|
|
}
|
|
|
|
|
2022-04-18 22:28:01 +00:00
|
|
|
let config = engine_state.get_config();
|
2021-12-25 20:50:02 +00:00
|
|
|
|
|
|
|
parts
|
|
|
|
.into_iter()
|
|
|
|
.into_pipeline_data(None)
|
2022-04-18 22:28:01 +00:00
|
|
|
.collect_string("", config)
|
2021-12-25 20:50:02 +00:00
|
|
|
.map(|x| Value::String {
|
|
|
|
val: x,
|
|
|
|
span: expr.span,
|
|
|
|
})
|
|
|
|
}
|
2021-07-23 21:19:30 +00:00
|
|
|
Expr::String(s) => Ok(Value::String {
|
|
|
|
val: s.clone(),
|
|
|
|
span: expr.span,
|
|
|
|
}),
|
2022-01-16 13:55:56 +00: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 16:37:51 +00:00
|
|
|
Ok(Value::string(path.to_string_lossy(), expr.span))
|
2022-01-16 13:55:56 +00:00
|
|
|
}
|
2022-04-22 20:18:51 +00:00
|
|
|
Expr::Directory(s) => {
|
2022-04-22 23:48:10 +00: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 16:37:51 +00:00
|
|
|
Ok(Value::string("-", expr.span))
|
2022-04-22 23:48:10 +00:00
|
|
|
} else {
|
|
|
|
let cwd = current_dir_str(engine_state, stack)?;
|
|
|
|
let path = expand_path_with(s, cwd);
|
2022-04-22 20:18:51 +00: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 16:37:51 +00:00
|
|
|
Ok(Value::string(path.to_string_lossy(), expr.span))
|
2022-04-22 23:48:10 +00:00
|
|
|
}
|
2022-04-22 20:18:51 +00:00
|
|
|
}
|
2022-01-16 13:55:56 +00: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 16:37:51 +00:00
|
|
|
Ok(Value::string(path.to_string_lossy(), expr.span))
|
2022-01-16 13:55:56 +00:00
|
|
|
}
|
2021-08-08 21:02:47 +00:00
|
|
|
Expr::Signature(_) => Ok(Value::Nothing { span: expr.span }),
|
|
|
|
Expr::Garbage => Ok(Value::Nothing { span: expr.span }),
|
2021-12-20 01:05:33 +00:00
|
|
|
Expr::Nothing => Ok(Value::Nothing { span: expr.span }),
|
2021-07-16 01:10:22 +00:00
|
|
|
}
|
2021-07-23 05:14:49 +00:00
|
|
|
}
|
|
|
|
|
2022-01-26 19:00:25 +00: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 12:44:42 +00:00
|
|
|
///
|
2022-11-24 03:58:15 +00:00
|
|
|
/// It returns PipelineData with a boolean flag, indicating if the external failed to run.
|
2022-07-20 12:44:42 +00:00
|
|
|
/// The boolean flag **may only be true** for external calls, for internal calls, it always to be false.
|
2022-01-26 19:00:25 +00:00
|
|
|
pub fn eval_expression_with_input(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
expr: &Expression,
|
|
|
|
mut input: PipelineData,
|
2022-02-21 22:22:21 +00:00
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
2022-07-20 12:44:42 +00:00
|
|
|
) -> Result<(PipelineData, bool), ShellError> {
|
2022-01-26 19:00:25 +00:00
|
|
|
match expr {
|
|
|
|
Expression {
|
|
|
|
expr: Expr::Call(call),
|
|
|
|
..
|
|
|
|
} => {
|
2022-02-21 22:22:21 +00: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 19:00:25 +00: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 03:51:57 +00:00
|
|
|
expr: Expr::ExternalCall(head, args, is_subexpression),
|
2022-01-26 19:00:25 +00:00
|
|
|
..
|
|
|
|
} => {
|
2022-09-30 12:13:46 +00:00
|
|
|
input = eval_external(
|
2022-02-21 22:22:21 +00:00
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
head,
|
|
|
|
args,
|
|
|
|
input,
|
|
|
|
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 03:51:57 +00:00
|
|
|
*is_subexpression,
|
2022-02-21 22:22:21 +00:00
|
|
|
)?;
|
2022-01-26 19:00:25 +00: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)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
elem => {
|
|
|
|
input = eval_expression(engine_state, stack, elem)?.into_pipeline_data();
|
|
|
|
}
|
2022-09-30 12:13:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(might_consume_external_result(input))
|
|
|
|
}
|
2022-01-26 19:00:25 +00:00
|
|
|
|
2022-12-14 18:25:32 +00:00
|
|
|
// Try to catch and detect if external command runs to failed.
|
2022-09-30 12:13:46 +00:00
|
|
|
fn might_consume_external_result(input: PipelineData) -> (PipelineData, bool) {
|
2022-12-14 18:25:32 +00:00
|
|
|
input.is_external_failed()
|
2022-01-26 19:00:25 +00:00
|
|
|
}
|
|
|
|
|
2022-11-18 21:46:48 +00:00
|
|
|
pub fn eval_element_with_input(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
element: &PipelineElement,
|
|
|
|
input: PipelineData,
|
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
|
|
|
) -> Result<(PipelineData, bool), ShellError> {
|
|
|
|
match element {
|
2022-12-13 03:36:13 +00:00
|
|
|
PipelineElement::Expression(_, expr) => eval_expression_with_input(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
expr,
|
|
|
|
input,
|
|
|
|
redirect_stdout,
|
|
|
|
redirect_stderr,
|
|
|
|
),
|
2022-11-22 18:26:13 +00:00
|
|
|
PipelineElement::Redirection(span, redirection, expr) => match &expr.expr {
|
|
|
|
Expr::String(_) => {
|
|
|
|
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 03:51:57 +00:00
|
|
|
trim_end_newline,
|
2022-11-22 18:26:13 +00: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 03:51:57 +00:00
|
|
|
trim_end_newline,
|
2022-11-22 18:26:13 +00:00
|
|
|
},
|
|
|
|
(
|
|
|
|
Redirection::StdoutAndStderr,
|
|
|
|
PipelineData::ExternalStream {
|
|
|
|
stdout,
|
|
|
|
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 03:51:57 +00:00
|
|
|
trim_end_newline,
|
2022-11-22 18:26:13 +00:00
|
|
|
},
|
|
|
|
) => match (stdout, stderr) {
|
|
|
|
(Some(stdout), Some(stderr)) => PipelineData::ExternalStream {
|
|
|
|
stdout: Some(stdout.chain(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 03:51:57 +00:00
|
|
|
trim_end_newline,
|
2022-11-22 18:26:13 +00:00
|
|
|
},
|
|
|
|
(None, Some(stderr)) => PipelineData::ExternalStream {
|
|
|
|
stdout: Some(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 03:51:57 +00:00
|
|
|
trim_end_newline,
|
2022-11-22 18:26:13 +00:00
|
|
|
},
|
|
|
|
(Some(stdout), None) => PipelineData::ExternalStream {
|
|
|
|
stdout: Some(stdout),
|
|
|
|
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 03:51:57 +00:00
|
|
|
trim_end_newline,
|
2022-11-22 18:26:13 +00:00
|
|
|
},
|
|
|
|
(None, None) => PipelineData::ExternalStream {
|
|
|
|
stdout: None,
|
|
|
|
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 03:51:57 +00:00
|
|
|
trim_end_newline,
|
2022-11-22 18:26:13 +00: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 12:26:17 +00:00
|
|
|
item: "raw".into(),
|
|
|
|
span: *span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)),
|
|
|
|
Argument::Named((
|
|
|
|
Spanned {
|
|
|
|
item: "force".into(),
|
2022-11-22 18:26:13 +00:00
|
|
|
span: *span,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)),
|
|
|
|
],
|
|
|
|
redirect_stdout: false,
|
|
|
|
redirect_stderr: false,
|
2022-12-22 14:36:13 +00:00
|
|
|
parser_info: vec![],
|
2022-11-22 18:26:13 +00:00
|
|
|
},
|
|
|
|
input,
|
|
|
|
)
|
|
|
|
.map(|x| (x, false))
|
|
|
|
} else {
|
|
|
|
Err(ShellError::CommandNotFound(*span))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(ShellError::CommandNotFound(*span)),
|
|
|
|
},
|
2022-12-13 03:36:13 +00: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 21:46:48 +00: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 20:39:16 +00: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 02:15:01 +00:00
|
|
|
pub fn eval_block(
|
2021-10-25 06:31:39 +00:00
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
2021-09-03 02:15:01 +00:00
|
|
|
block: &Block,
|
2021-10-25 04:01:02 +00:00
|
|
|
mut input: PipelineData,
|
2022-02-21 22:22:21 +00:00
|
|
|
redirect_stdout: bool,
|
|
|
|
redirect_stderr: bool,
|
2021-10-25 04:01:02 +00:00
|
|
|
) -> Result<PipelineData, ShellError> {
|
2022-02-15 19:31:14 +00:00
|
|
|
let num_pipelines = block.len();
|
2022-12-13 03:36:13 +00:00
|
|
|
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
|
2022-11-22 18:26:13 +00:00
|
|
|
let mut i = 0;
|
|
|
|
|
2022-12-13 03:36:13 +00:00
|
|
|
while i < pipeline.elements.len() {
|
2022-11-24 03:58:15 +00:00
|
|
|
let redirect_stderr = redirect_stderr
|
|
|
|
|| ((i < pipeline.elements.len() - 1)
|
|
|
|
&& (matches!(
|
|
|
|
pipeline.elements[i + 1],
|
|
|
|
PipelineElement::Redirection(_, Redirection::Stderr, _)
|
|
|
|
| PipelineElement::Redirection(_, Redirection::StdoutAndStderr, _)
|
|
|
|
)));
|
|
|
|
|
2022-07-20 12:44:42 +00:00
|
|
|
// if eval internal command failed, it can just make early return with `Err(ShellError)`.
|
2022-11-18 21:46:48 +00:00
|
|
|
let eval_result = eval_element_with_input(
|
2022-02-15 19:31:14 +00:00
|
|
|
engine_state,
|
|
|
|
stack,
|
2022-11-22 18:26:13 +00:00
|
|
|
&pipeline.elements[i],
|
2022-02-15 19:31:14 +00:00
|
|
|
input,
|
2022-11-23 02:18:34 +00:00
|
|
|
redirect_stdout
|
|
|
|
|| (i != pipeline.elements.len() - 1)
|
|
|
|
&& (matches!(
|
|
|
|
pipeline.elements[i + 1],
|
|
|
|
PipelineElement::Redirection(_, Redirection::Stdout, _)
|
|
|
|
| PipelineElement::Redirection(_, Redirection::StdoutAndStderr, _)
|
|
|
|
| PipelineElement::Expression(..)
|
|
|
|
)),
|
2022-11-24 03:58:15 +00: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.
|
|
|
|
}
|
|
|
|
(Err(error), true) => input = PipelineData::Value(Value::Error { error }, None),
|
|
|
|
(output, false) => {
|
|
|
|
let output = output?;
|
|
|
|
input = output.0;
|
2022-12-13 03:36:13 +00:00
|
|
|
// external command may runs to failed
|
2022-11-24 03:58:15 +00: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 12:44:42 +00:00
|
|
|
}
|
2022-11-22 18:26:13 +00:00
|
|
|
|
|
|
|
i += 1;
|
2021-11-06 05:50:33 +00:00
|
|
|
}
|
2022-02-06 02:03:06 +00:00
|
|
|
|
2022-02-15 19:31:14 +00:00
|
|
|
if pipeline_idx < (num_pipelines) - 1 {
|
2022-12-13 03:36:13 +00:00
|
|
|
match input {
|
|
|
|
PipelineData::Value(Value::Nothing { .. }, ..) => {}
|
|
|
|
PipelineData::ExternalStream {
|
|
|
|
ref mut exit_code, ..
|
|
|
|
} => {
|
|
|
|
let exit_code = exit_code.take();
|
|
|
|
|
|
|
|
// Drain the input to the screen via tabular output
|
|
|
|
let config = engine_state.get_config();
|
|
|
|
|
|
|
|
match engine_state.find_decl("table".as_bytes(), &[]) {
|
|
|
|
Some(decl_id) => {
|
|
|
|
let table = engine_state.get_decl(decl_id).run(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
&Call::new(Span::new(0, 0)),
|
|
|
|
input,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
print_or_return(table, config)?;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
print_or_return(input, config)?;
|
|
|
|
}
|
|
|
|
};
|
2022-12-12 20:53:46 +00:00
|
|
|
|
2022-12-13 03:36:13 +00:00
|
|
|
if let Some(exit_code) = exit_code {
|
|
|
|
let mut v: Vec<_> = exit_code.collect();
|
2022-12-12 20:53:46 +00:00
|
|
|
|
2022-12-13 03:36:13 +00:00
|
|
|
if let Some(v) = v.pop() {
|
|
|
|
stack.add_env_var("LAST_EXIT_CODE".into(), v);
|
|
|
|
}
|
|
|
|
}
|
2022-12-12 20:53:46 +00:00
|
|
|
}
|
2022-12-13 03:36:13 +00:00
|
|
|
_ => {
|
|
|
|
// Drain the input to the screen via tabular output
|
|
|
|
let config = engine_state.get_config();
|
|
|
|
|
|
|
|
match engine_state.find_decl("table".as_bytes(), &[]) {
|
|
|
|
Some(decl_id) => {
|
|
|
|
let table = engine_state.get_decl(decl_id);
|
|
|
|
|
|
|
|
if let Some(block_id) = table.get_block_id() {
|
|
|
|
let block = engine_state.get_block(block_id);
|
|
|
|
eval_block(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
block,
|
|
|
|
input,
|
|
|
|
redirect_stdout,
|
|
|
|
redirect_stderr,
|
|
|
|
)?;
|
|
|
|
} else {
|
|
|
|
let table = table.run(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
&Call::new(Span::new(0, 0)),
|
|
|
|
input,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
print_or_return(table, config)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
print_or_return(input, config)?;
|
|
|
|
}
|
|
|
|
};
|
2022-02-06 02:03:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-13 03:36:13 +00:00
|
|
|
input = PipelineData::empty()
|
2022-02-06 02:03:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-13 03:36:13 +00:00
|
|
|
Ok(input)
|
2022-02-06 02:03:06 +00:00
|
|
|
}
|
|
|
|
|
2022-07-25 17:00:31 +00:00
|
|
|
fn print_or_return(pipeline_data: PipelineData, config: &Config) -> Result<(), ShellError> {
|
|
|
|
for item in pipeline_data {
|
|
|
|
if let Value::Error { error } = item {
|
|
|
|
return Err(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut out = item.into_string("\n", config);
|
|
|
|
out.push('\n');
|
|
|
|
|
|
|
|
stdout_write_all_and_flush(out)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-11-06 05:50:33 +00:00
|
|
|
pub fn eval_subexpression(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
block: &Block,
|
|
|
|
mut input: PipelineData,
|
|
|
|
) -> Result<PipelineData, ShellError> {
|
2022-02-15 19:31:14 +00:00
|
|
|
for pipeline in block.pipelines.iter() {
|
2022-11-18 21:46:48 +00:00
|
|
|
for expr in pipeline.elements.iter() {
|
|
|
|
input = eval_element_with_input(engine_state, stack, expr, input, true, false)?.0
|
2021-07-16 01:10:22 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-23 05:14:49 +00:00
|
|
|
|
2021-09-03 02:15:01 +00:00
|
|
|
Ok(input)
|
2021-07-16 01:10:22 +00:00
|
|
|
}
|
2021-10-05 02:27:39 +00:00
|
|
|
|
2021-10-29 18:15:17 +00:00
|
|
|
pub fn eval_variable(
|
2021-11-02 03:08:05 +00:00
|
|
|
engine_state: &EngineState,
|
2021-10-29 18:15:17 +00:00
|
|
|
stack: &Stack,
|
|
|
|
var_id: VarId,
|
|
|
|
span: Span,
|
|
|
|
) -> Result<Value, ShellError> {
|
2022-01-29 13:00:48 +00:00
|
|
|
match var_id {
|
|
|
|
nu_protocol::NU_VARIABLE_ID => {
|
|
|
|
// $nu
|
|
|
|
let mut output_cols = vec![];
|
|
|
|
let mut output_vals = vec![];
|
2021-10-29 18:15:17 +00:00
|
|
|
|
2022-08-22 16:30:09 +00:00
|
|
|
if let Some(path) = engine_state.get_config_path("config-path") {
|
|
|
|
output_cols.push("config-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: path.to_string_lossy().to_string(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(path) = engine_state.get_config_path("env-path") {
|
|
|
|
output_cols.push("env-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: path.to_string_lossy().to_string(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
if let Some(mut config_path) = nu_path::config_dir() {
|
|
|
|
config_path.push("nushell");
|
2022-04-06 17:11:51 +00:00
|
|
|
let mut env_config_path = config_path.clone();
|
2022-06-06 11:52:37 +00:00
|
|
|
let mut loginshell_path = config_path.clone();
|
2021-10-29 18:15:17 +00:00
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
let mut history_path = config_path.clone();
|
2021-10-29 18:15:17 +00:00
|
|
|
|
2022-06-15 08:06:49 +00:00
|
|
|
match engine_state.config.history_file_format {
|
|
|
|
HistoryFileFormat::Sqlite => {
|
|
|
|
history_path.push("history.sqlite3");
|
|
|
|
}
|
|
|
|
HistoryFileFormat::PlainText => {
|
|
|
|
history_path.push("history.txt");
|
|
|
|
}
|
|
|
|
}
|
2022-06-14 20:53:33 +00:00
|
|
|
// let mut history_path = config_files::get_history_path(); // todo: this should use the get_history_path method but idk where to put that function
|
2021-10-29 18:15:17 +00:00
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
output_cols.push("history-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: history_path.to_string_lossy().to_string(),
|
|
|
|
span,
|
|
|
|
});
|
2021-10-29 18:15:17 +00:00
|
|
|
|
2022-08-22 16:30:09 +00:00
|
|
|
if engine_state.get_config_path("config-path").is_none() {
|
|
|
|
config_path.push("config.nu");
|
2021-12-11 19:29:56 +00:00
|
|
|
|
2022-08-22 16:30:09 +00:00
|
|
|
output_cols.push("config-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: config_path.to_string_lossy().to_string(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
2022-04-06 17:11:51 +00:00
|
|
|
|
2022-08-22 16:30:09 +00:00
|
|
|
if engine_state.get_config_path("env-path").is_none() {
|
|
|
|
env_config_path.push("env.nu");
|
2022-04-06 17:11:51 +00:00
|
|
|
|
2022-08-22 16:30:09 +00:00
|
|
|
output_cols.push("env-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: env_config_path.to_string_lossy().to_string(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
2022-06-06 11:52:37 +00:00
|
|
|
|
|
|
|
loginshell_path.push("login.nu");
|
|
|
|
|
|
|
|
output_cols.push("loginshell-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: loginshell_path.to_string_lossy().to_string(),
|
|
|
|
span,
|
|
|
|
});
|
2022-01-29 13:00:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
if let Some(path) = &engine_state.plugin_signatures {
|
|
|
|
if let Some(path_str) = path.to_str() {
|
|
|
|
output_cols.push("plugin-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: path_str.into(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
2021-12-02 06:35:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-04 16:36:11 +00:00
|
|
|
output_cols.push("scope".into());
|
|
|
|
output_vals.push(create_scope(engine_state, stack, span)?);
|
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
if let Some(home_path) = nu_path::home_dir() {
|
|
|
|
if let Some(home_path_str) = home_path.to_str() {
|
|
|
|
output_cols.push("home-path".into());
|
|
|
|
output_vals.push(Value::String {
|
|
|
|
val: home_path_str.into(),
|
|
|
|
span,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-12-11 19:38:36 +00:00
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
let temp = std::env::temp_dir();
|
|
|
|
if let Some(temp_path) = temp.to_str() {
|
|
|
|
output_cols.push("temp-path".into());
|
2021-12-11 19:12:30 +00:00
|
|
|
output_vals.push(Value::String {
|
2022-01-29 13:00:48 +00:00
|
|
|
val: temp_path.into(),
|
2021-12-11 19:12:30 +00:00
|
|
|
span,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-12 16:54:59 +00:00
|
|
|
let pid = std::process::id();
|
|
|
|
output_cols.push("pid".into());
|
|
|
|
output_vals.push(Value::int(pid as i64, span));
|
|
|
|
|
2022-04-23 21:33:27 +00:00
|
|
|
let sys = sysinfo::System::new();
|
|
|
|
let ver = match sys.kernel_version() {
|
|
|
|
Some(v) => v,
|
|
|
|
None => "unknown".into(),
|
|
|
|
};
|
|
|
|
|
2022-04-19 19:11:58 +00:00
|
|
|
let os_record = Value::Record {
|
2022-04-23 21:33:27 +00:00
|
|
|
cols: vec![
|
|
|
|
"name".into(),
|
|
|
|
"arch".into(),
|
|
|
|
"family".into(),
|
|
|
|
"kernel_version".into(),
|
|
|
|
],
|
2022-04-19 19:11:58 +00:00
|
|
|
vals: vec![
|
|
|
|
Value::string(std::env::consts::OS, span),
|
|
|
|
Value::string(std::env::consts::ARCH, span),
|
|
|
|
Value::string(std::env::consts::FAMILY, span),
|
2022-04-23 21:33:27 +00:00
|
|
|
Value::string(ver, span),
|
2022-04-19 19:11:58 +00:00
|
|
|
],
|
|
|
|
span,
|
|
|
|
};
|
|
|
|
output_cols.push("os-info".into());
|
|
|
|
output_vals.push(os_record);
|
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
Ok(Value::Record {
|
|
|
|
cols: output_cols,
|
|
|
|
vals: output_vals,
|
2021-12-11 20:00:29 +00:00
|
|
|
span,
|
|
|
|
})
|
|
|
|
}
|
2022-01-29 13:00:48 +00: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));
|
|
|
|
|
|
|
|
let (env_columns, env_values) = pairs.into_iter().unzip();
|
|
|
|
|
|
|
|
Ok(Value::Record {
|
|
|
|
cols: env_columns,
|
|
|
|
vals: env_values,
|
|
|
|
span,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
var_id => stack.get_var(var_id, span),
|
2021-10-29 18:15:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-17 01:04:54 +00:00
|
|
|
fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
2021-10-05 02:27:39 +00:00
|
|
|
match unit {
|
|
|
|
Unit::Byte => Value::Filesize { val: size, span },
|
|
|
|
Unit::Kilobyte => Value::Filesize {
|
|
|
|
val: size * 1000,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Megabyte => Value::Filesize {
|
|
|
|
val: size * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Gigabyte => Value::Filesize {
|
|
|
|
val: size * 1000 * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Terabyte => Value::Filesize {
|
|
|
|
val: size * 1000 * 1000 * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Petabyte => Value::Filesize {
|
|
|
|
val: size * 1000 * 1000 * 1000 * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
2022-07-26 13:05:37 +00:00
|
|
|
Unit::Exabyte => Value::Filesize {
|
|
|
|
val: size * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Zettabyte => Value::Filesize {
|
|
|
|
val: size * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
2021-10-05 02:27:39 +00:00
|
|
|
|
|
|
|
Unit::Kibibyte => Value::Filesize {
|
|
|
|
val: size * 1024,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Mebibyte => Value::Filesize {
|
|
|
|
val: size * 1024 * 1024,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Gibibyte => Value::Filesize {
|
|
|
|
val: size * 1024 * 1024 * 1024,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Tebibyte => Value::Filesize {
|
|
|
|
val: size * 1024 * 1024 * 1024 * 1024,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Pebibyte => Value::Filesize {
|
|
|
|
val: size * 1024 * 1024 * 1024 * 1024 * 1024,
|
|
|
|
span,
|
|
|
|
},
|
2022-07-26 13:05:37 +00:00
|
|
|
Unit::Exbibyte => Value::Filesize {
|
|
|
|
val: size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Zebibyte => Value::Filesize {
|
|
|
|
val: size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
|
|
|
span,
|
|
|
|
},
|
2021-10-05 02:27:39 +00:00
|
|
|
|
|
|
|
Unit::Nanosecond => Value::Duration { val: size, span },
|
|
|
|
Unit::Microsecond => Value::Duration {
|
|
|
|
val: size * 1000,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Millisecond => Value::Duration {
|
|
|
|
val: size * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Unit::Second => Value::Duration {
|
|
|
|
val: size * 1000 * 1000 * 1000,
|
|
|
|
span,
|
|
|
|
},
|
2022-09-29 18:24:17 +00:00
|
|
|
Unit::Minute => match size.checked_mul(1000 * 1000 * 1000 * 60) {
|
|
|
|
Some(val) => Value::Duration { val, span },
|
|
|
|
None => Value::Error {
|
|
|
|
error: ShellError::GenericError(
|
|
|
|
"duration too large".into(),
|
|
|
|
"duration too large".into(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
),
|
|
|
|
},
|
2021-10-05 02:27:39 +00:00
|
|
|
},
|
2022-09-29 18:24:17 +00:00
|
|
|
Unit::Hour => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60) {
|
|
|
|
Some(val) => Value::Duration { val, span },
|
|
|
|
None => Value::Error {
|
|
|
|
error: ShellError::GenericError(
|
|
|
|
"duration too large".into(),
|
|
|
|
"duration too large".into(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
),
|
|
|
|
},
|
2021-10-05 02:27:39 +00:00
|
|
|
},
|
2022-02-02 20:59:01 +00:00
|
|
|
Unit::Day => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24) {
|
|
|
|
Some(val) => Value::Duration { val, span },
|
|
|
|
None => Value::Error {
|
2022-04-18 12:34:10 +00:00
|
|
|
error: ShellError::GenericError(
|
2022-09-29 18:24:17 +00:00
|
|
|
"duration too large".into(),
|
|
|
|
"duration too large".into(),
|
2022-04-18 12:34:10 +00:00
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
2022-02-02 20:59:01 +00:00
|
|
|
),
|
|
|
|
},
|
2021-10-05 02:27:39 +00:00
|
|
|
},
|
2022-02-02 20:59:01 +00:00
|
|
|
Unit::Week => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 7) {
|
|
|
|
Some(val) => Value::Duration { val, span },
|
|
|
|
None => Value::Error {
|
2022-04-18 12:34:10 +00:00
|
|
|
error: ShellError::GenericError(
|
2022-09-29 18:24:17 +00:00
|
|
|
"duration too large".into(),
|
|
|
|
"duration too large".into(),
|
2022-07-26 13:05:37 +00:00
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
2021-10-05 02:27:39 +00:00
|
|
|
}
|
|
|
|
}
|