use crate::{ hir, hir::syntax_shape::{ color_fallible_syntax, color_syntax_with, continue_expression, expand_expr, expand_syntax, DelimitedShape, ExpandContext, ExpandExpression, ExpressionContinuationShape, ExpressionListShape, FallibleColorSyntax, MemberShape, PathTailShape, PathTailSyntax, VariablePathShape, }, hir::tokens_iterator::TokensIterator, parse::token_tree::Delimiter, }; use nu_errors::{ParseError, ShellError}; use nu_source::Span; #[derive(Debug, Copy, Clone)] pub struct AnyBlockShape; impl FallibleColorSyntax for AnyBlockShape { type Info = (); type Input = (); fn name(&self) -> &'static str { "AnyBlockShape" } fn color_syntax<'a, 'b>( &self, _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result<(), ShellError> { let block = token_nodes.peek_non_ws().not_eof("block"); let block = match block { Err(_) => return Ok(()), Ok(block) => block, }; // is it just a block? let block = block.node.as_block(); match block { // If so, color it as a block Some((children, spans)) => { token_nodes.child(children, context.source.clone(), |token_nodes| { color_syntax_with( &DelimitedShape, &(Delimiter::Brace, spans.0, spans.1), token_nodes, context, ); }); return Ok(()); } _ => {} } // Otherwise, look for a shorthand block. If none found, fail color_fallible_syntax(&ShorthandBlock, token_nodes, context) } } impl ExpandExpression for AnyBlockShape { fn name(&self) -> &'static str { "any block" } fn expand_expr<'a, 'b>( &self, token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { let block = token_nodes.peek_non_ws().not_eof("block")?; // is it just a block? let block = block.node.as_block(); match block { Some((block, _tags)) => { let mut iterator = TokensIterator::new(&block.item, block.span, context.source.clone(), false); let exprs = expand_syntax(&ExpressionListShape, &mut iterator, context)?.exprs; return Ok(hir::RawExpression::Block(exprs.item).into_expr(block.span)); } _ => {} } expand_syntax(&ShorthandBlock, token_nodes, context) } } #[derive(Debug, Copy, Clone)] pub struct ShorthandBlock; impl FallibleColorSyntax for ShorthandBlock { type Info = (); type Input = (); fn name(&self) -> &'static str { "ShorthandBlock" } fn color_syntax<'a, 'b>( &self, _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result<(), ShellError> { // Try to find a shorthand head. If none found, fail color_fallible_syntax(&ShorthandPath, token_nodes, context)?; loop { // Check to see whether there's any continuation after the head expression let result = color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context); match result { // if no continuation was found, we're done Err(_) => break, // if a continuation was found, look for another one Ok(_) => continue, } } Ok(()) } } impl ExpandExpression for ShorthandBlock { fn name(&self) -> &'static str { "shorthand block" } fn expand_expr<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { let path = expand_expr(&ShorthandPath, token_nodes, context)?; let start = path.span; let expr = continue_expression(path, token_nodes, context); let end = expr.span; let block = hir::RawExpression::Block(vec![expr]).into_expr(start.until(end)); Ok(block) } } /// A shorthand for `$it.foo."bar"`, used inside of a shorthand block #[derive(Debug, Copy, Clone)] pub struct ShorthandPath; impl FallibleColorSyntax for ShorthandPath { type Info = (); type Input = (); fn name(&self) -> &'static str { "ShorthandPath" } fn color_syntax<'a, 'b>( &self, _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result<(), ShellError> { token_nodes.atomic(|token_nodes| { let variable = color_fallible_syntax(&VariablePathShape, token_nodes, context); match variable { Ok(_) => { // if it's a variable path, that's the head part return Ok(()); } Err(_) => { // otherwise, we'll try to find a member path } } // look for a member (`` -> `$it.`) color_fallible_syntax(&MemberShape, token_nodes, context)?; // Now that we've synthesized the head, of the path, proceed to expand the tail of the path // like any other path. let tail = color_fallible_syntax(&PathTailShape, token_nodes, context); match tail { Ok(_) => {} Err(_) => { // It's ok if there's no path tail; a single member is sufficient } } Ok(()) }) } } impl ExpandExpression for ShorthandPath { fn name(&self) -> &'static str { "shorthand path" } fn expand_expr<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { // if it's a variable path, that's the head part let path = expand_expr(&VariablePathShape, token_nodes, context); match path { Ok(path) => return Ok(path), Err(_) => {} } // Synthesize the head of the shorthand path (`` -> `$it.`) let mut head = expand_expr(&ShorthandHeadShape, token_nodes, context)?; // Now that we've synthesized the head, of the path, proceed to expand the tail of the path // like any other path. let tail = expand_syntax(&PathTailShape, token_nodes, context); match tail { Err(_) => return Ok(head), Ok(PathTailSyntax { tail, .. }) => { // For each member that `PathTailShape` expanded, join it onto the existing expression // to form a new path for member in tail { head = hir::Expression::dot_member(head, member); } Ok(head) } } } } /// A shorthand for `$it.foo."bar"`, used inside of a shorthand block #[derive(Debug, Copy, Clone)] pub struct ShorthandHeadShape; impl ExpandExpression for ShorthandHeadShape { fn name(&self) -> &'static str { "shorthand head" } fn expand_expr<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { let head = expand_syntax(&MemberShape, token_nodes, context)?; let head = head.to_path_member(context.source); // Synthesize an `$it` expression let it = synthetic_it(); let span = head.span; Ok(hir::Expression::path(it, vec![head], span)) } } fn synthetic_it() -> hir::Expression { hir::Expression::it_variable(Span::unknown(), Span::unknown()) }