Make append/prepend consistent for ranges (#10231)

# Description
This PR makes `append`/`prepend` more consistent, in particular, it allows you to
work with ranges. Previously, you couldn't append a list by range:
```nu
> 0..1 | append 2..4
╭──────╮
│    0 │
│    1 │
│ 2..4 │
╰──────╯
```

Now it works:
```nu
> 0..1 | append 2..4
╭───╮
│ 0 │
│ 1 │
│ 2 │
│ 3 │
│ 4 │
╰───╯
```

# User-Facing Changes
If someone needs the old behavior, then it can be obtained like this:
```nu
> 0..1 | append [2..4]
╭──────╮
│    0 │
│    1 │
│ 2..4 │
╰──────╯
```
This commit is contained in:
Nano 2023-09-05 11:47:51 +12:00 committed by GitHub
parent 08aaa9494c
commit eca9f461da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 139 deletions

View file

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
SyntaxShape, Type, Value,
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -40,68 +40,67 @@ only unwrap the outer list, and leave the variable's contents untouched."#
fn examples(&self) -> Vec<Example> {
vec![
Example {
example: "[0,1,2,3] | append 4",
example: "[0 1 2 3] | append 4",
description: "Append one integer to a list",
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
])),
},
Example {
example: "0 | append [1 2 3]",
description: "Append a list to an item",
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
])),
},
Example {
example: r#""a" | append ["b"] "#,
description: "Append a list of string to a string",
result: Some(Value::list(
vec![Value::test_string("a"), Value::test_string("b")],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_string("a"),
Value::test_string("b"),
])),
},
Example {
example: "[0,1] | append [2,3,4]",
example: "[0 1] | append [2 3 4]",
description: "Append three integer items",
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
])),
},
Example {
example: "[0,1] | append [2,nu,4,shell]",
example: "[0 1] | append [2 nu 4 shell]",
description: "Append integers and strings",
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_string("nu"),
Value::test_int(4),
Value::test_string("shell"),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_string("nu"),
Value::test_int(4),
Value::test_string("shell"),
])),
},
Example {
example: "[0 1] | append 2..4",
description: "Append a range of integers to a list",
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
])),
},
]
}
@ -113,35 +112,17 @@ only unwrap the outer list, and leave the variable's contents untouched."#
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let val: Value = call.req(engine_state, stack, 0)?;
let vec: Vec<Value> = process_value(val);
let other: Value = call.req(engine_state, stack, 0)?;
let metadata = input.metadata();
Ok(input
.into_iter()
.chain(vec)
.chain(other.into_pipeline_data())
.into_pipeline_data(engine_state.ctrlc.clone())
.set_metadata(metadata))
}
}
fn process_value(val: Value) -> Vec<Value> {
match val {
Value::List {
vals: input_vals, ..
} => {
let mut output = vec![];
for input_val in input_vals {
output.push(input_val);
}
output
}
_ => {
vec![val]
}
}
}
#[cfg(test)]
mod test {
use super::*;

View file

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
SyntaxShape, Type, Value,
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -45,67 +45,66 @@ only unwrap the outer list, and leave the variable's contents untouched."#
Example {
example: "0 | prepend [1 2 3]",
description: "prepend a list to an item",
result: Some(Value::list(
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(0),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(0),
])),
},
Example {
example: r#""a" | prepend ["b"] "#,
description: "Prepend a list of strings to a string",
result: Some(Value::list(
vec![Value::test_string("b"), Value::test_string("a")],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_string("b"),
Value::test_string("a"),
])),
},
Example {
example: "[1,2,3,4] | prepend 0",
example: "[1 2 3 4] | prepend 0",
description: "Prepend one integer item",
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
])),
},
Example {
example: "[2,3,4] | prepend [0,1]",
example: "[2 3 4] | prepend [0 1]",
description: "Prepend two integer items",
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
])),
},
Example {
example: "[2,nu,4,shell] | prepend [0,1,rocks]",
example: "[2 nu 4 shell] | prepend [0 1 rocks]",
description: "Prepend integers and strings",
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_string("rocks"),
Value::test_int(2),
Value::test_string("nu"),
Value::test_int(4),
Value::test_string("shell"),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_string("rocks"),
Value::test_int(2),
Value::test_string("nu"),
Value::test_int(4),
Value::test_string("shell"),
])),
},
Example {
example: "[3 4] | prepend 0..2",
description: "Prepend a range",
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
])),
},
]
}
@ -117,11 +116,11 @@ only unwrap the outer list, and leave the variable's contents untouched."#
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let val: Value = call.req(engine_state, stack, 0)?;
let vec: Vec<Value> = process_value(val);
let other: Value = call.req(engine_state, stack, 0)?;
let metadata = input.metadata();
Ok(vec
Ok(other
.into_pipeline_data()
.into_iter()
.chain(input)
.into_pipeline_data(engine_state.ctrlc.clone())
@ -129,23 +128,6 @@ only unwrap the outer list, and leave the variable's contents untouched."#
}
}
fn process_value(val: Value) -> Vec<Value> {
match val {
Value::List {
vals: input_vals, ..
} => {
let mut output = vec![];
for input_val in input_vals {
output.push(input_val);
}
output
}
_ => {
vec![val]
}
}
}
#[cfg(test)]
mod test {
use super::*;