Relax the closure syntax, highlight differently (#8846)

# Description

This relaxes the closure syntax so that `||` is no longer required. This
allows for `ls | each { $in.name }` for example.

I've gone ahead and changed the syntax highlighting so that blocks and
closures are distinct for now.

# User-Facing Changes

Removes `||` requirement for closures.

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
JT 2023-04-12 05:21:52 +12:00 committed by GitHub
parent 46dba8853a
commit 8ee52b6ee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 34 deletions

View File

@ -111,6 +111,9 @@ impl Highlighter for NuHighlighter {
FlatShape::Block => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Closure => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
FlatShape::Directory => add_colored_token(&shape.1, next_token),

View File

@ -9,6 +9,7 @@ pub fn default_shape_color(shape: String) -> Style {
"shape_binary" => Style::new().fg(Color::Purple).bold(),
"shape_block" => Style::new().fg(Color::Blue).bold(),
"shape_bool" => Style::new().fg(Color::LightCyan),
"shape_closure" => Style::new().fg(Color::Green).bold(),
"shape_custom" => Style::new().fg(Color::Green),
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_directory" => Style::new().fg(Color::Cyan),

View File

@ -12,6 +12,7 @@ pub enum FlatShape {
Binary,
Block,
Bool,
Closure,
Custom(DeclId),
DateTime,
Directory,
@ -50,6 +51,7 @@ impl Display for FlatShape {
FlatShape::Binary => write!(f, "shape_binary"),
FlatShape::Block => write!(f, "shape_block"),
FlatShape::Bool => write!(f, "shape_bool"),
FlatShape::Closure => write!(f, "shape_closure"),
FlatShape::Custom(_) => write!(f, "shape_custom"),
FlatShape::DateTime => write!(f, "shape_datetime"),
FlatShape::Directory => write!(f, "shape_directory"),
@ -85,6 +87,7 @@ impl Display for FlatShape {
pub fn flatten_block(working_set: &StateWorkingSet, block: &Block) -> Vec<(Span, FlatShape)> {
let mut output = vec![];
for pipeline in &block.pipelines {
output.extend(flatten_pipeline(working_set, pipeline));
}
@ -115,10 +118,41 @@ pub fn flatten_expression(
output.extend(flatten_expression(working_set, inner_expr));
output
}
Expr::Block(block_id)
| Expr::Closure(block_id)
| Expr::RowCondition(block_id)
| Expr::Subexpression(block_id) => {
Expr::Closure(block_id) => {
let outer_span = expr.span;
let mut output = vec![];
let block = working_set.get_block(*block_id);
let flattened = flatten_block(working_set, block);
if let Some(first) = flattened.first() {
if first.0.start > outer_span.start {
output.push((
Span::new(outer_span.start, first.0.start),
FlatShape::Closure,
));
}
}
let last = if let Some(last) = flattened.last() {
if last.0.end < outer_span.end {
Some((Span::new(last.0.end, outer_span.end), FlatShape::Closure))
} else {
None
}
} else {
None
};
output.extend(flattened);
if let Some(last) = last {
output.push(last)
}
output
}
Expr::Block(block_id) | Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
let outer_span = expr.span;
let mut output = vec![];
@ -149,14 +183,23 @@ pub fn flatten_expression(
output
}
Expr::Call(call) => {
let mut output = vec![(call.head, FlatShape::InternalCall(call.decl_id))];
let mut output = vec![];
if call.head.end != 0 {
// Make sure we don't push synthetic calls
output.push((call.head, FlatShape::InternalCall(call.decl_id)));
}
let mut args = vec![];
for positional in call.positional_iter() {
args.extend(flatten_expression(working_set, positional));
let flattened = flatten_expression(working_set, positional);
args.extend(flattened);
}
for named in call.named_iter() {
args.push((named.0.span, FlatShape::Flag));
if named.0.span.end != 0 {
// Ignore synthetic flags
args.push((named.0.span, FlatShape::Flag));
}
if let Some(expr) = &named.2 {
args.extend(flatten_expression(working_set, expr));
}

View File

@ -1589,10 +1589,8 @@ pub fn parse_brace_expr(
if matches!(second_token, None) {
// If we're empty, that means an empty record or closure
if matches!(shape, SyntaxShape::Closure(None)) {
parse_closure_expression(working_set, shape, span, false)
} else if matches!(shape, SyntaxShape::Closure(Some(_))) {
parse_closure_expression(working_set, shape, span, true)
if matches!(shape, SyntaxShape::Closure(_)) {
parse_closure_expression(working_set, shape, span)
} else if matches!(shape, SyntaxShape::Block) {
parse_block_expression(working_set, span)
} else if matches!(shape, SyntaxShape::MatchBlock) {
@ -1603,13 +1601,11 @@ pub fn parse_brace_expr(
} else if matches!(second_token_contents, Some(TokenContents::Pipe))
|| matches!(second_token_contents, Some(TokenContents::PipePipe))
{
parse_closure_expression(working_set, shape, span, true)
parse_closure_expression(working_set, shape, span)
} else if matches!(third_token, Some(b":")) {
parse_full_cell_path(working_set, None, span)
} else if matches!(shape, SyntaxShape::Closure(None)) {
parse_closure_expression(working_set, shape, span, false)
} else if matches!(shape, SyntaxShape::Closure(Some(_))) || matches!(shape, SyntaxShape::Any) {
parse_closure_expression(working_set, shape, span, true)
} else if matches!(shape, SyntaxShape::Closure(_)) || matches!(shape, SyntaxShape::Any) {
parse_closure_expression(working_set, shape, span)
} else if matches!(shape, SyntaxShape::Block) {
parse_block_expression(working_set, span)
} else if matches!(shape, SyntaxShape::MatchBlock) {
@ -4201,7 +4197,6 @@ pub fn parse_closure_expression(
working_set: &mut StateWorkingSet,
shape: &SyntaxShape,
span: Span,
require_pipe: bool,
) -> Expression {
trace!("parsing: closure expression");
@ -4275,15 +4270,7 @@ pub fn parse_closure_expression(
Some((Box::new(Signature::new("closure".to_string())), *span)),
1,
),
_ => {
if require_pipe {
working_set.error(ParseError::ClosureMissingPipe(span));
working_set.exit_scope();
return garbage(span);
} else {
(None, 0)
}
}
_ => (None, 0),
};
// TODO: Finish this

View File

@ -38,13 +38,6 @@ pub enum ParseError {
#[diagnostic(code(nu::parser::parse_mismatch))]
Expected(String, #[label("expected {0}")] Span),
#[error("Missing || inside closure")]
#[diagnostic(
code(nu::parser::closure_missing_pipe),
help("Try add || to the beginning of closure")
)]
ClosureMissingPipe(#[label("Parsing as a closure, but || is missing")] Span),
#[error("Type mismatch during operation.")]
#[diagnostic(code(nu::parser::type_mismatch))]
Mismatch(String, String, #[label("expected {0}, found {1}")] Span), // expected, found, span
@ -510,7 +503,6 @@ impl ParseError {
ParseError::UnknownOperator(_, _, s) => *s,
ParseError::InvalidLiteral(_, _, s) => *s,
ParseError::NotAConstant(s) => *s,
ParseError::ClosureMissingPipe(s) => *s,
}
}
}

View File

@ -57,6 +57,7 @@ let dark_theme = {
shape_binary: purple_bold
shape_block: blue_bold
shape_bool: light_cyan
shape_closure: green_bold
shape_custom: green
shape_datetime: cyan_bold
shape_directory: cyan
@ -140,6 +141,7 @@ let light_theme = {
shape_binary: purple_bold
shape_block: blue_bold
shape_bool: light_cyan
shape_closure: green_bold
shape_custom: green
shape_datetime: cyan_bold
shape_directory: cyan