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

View File

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