mirror of
https://github.com/nushell/nushell.git
synced 2025-04-03 06:01:11 +02:00
307 lines
9.8 KiB
Rust
307 lines
9.8 KiB
Rust
use crate::{
|
|
hir::syntax_shape::{
|
|
color_syntax, expand_atom, expand_expr, expand_syntax, AtomicToken, ColorSyntax,
|
|
ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, MaybeSpaceShape,
|
|
UnspannedAtomicToken,
|
|
},
|
|
hir::Expression,
|
|
TokensIterator,
|
|
};
|
|
use nu_errors::ParseError;
|
|
use nu_protocol::SpannedTypeName;
|
|
use nu_source::{b, DebugDocBuilder, HasSpan, PrettyDebug, Span, Spanned, SpannedItem};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ExternalTokensSyntax {
|
|
pub tokens: Spanned<Vec<Spanned<String>>>,
|
|
}
|
|
|
|
impl HasSpan for ExternalTokensSyntax {
|
|
fn span(&self) -> Span {
|
|
self.tokens.span
|
|
}
|
|
}
|
|
|
|
impl PrettyDebug for ExternalTokensSyntax {
|
|
fn pretty(&self) -> DebugDocBuilder {
|
|
b::intersperse(
|
|
self.tokens
|
|
.iter()
|
|
.map(|token| b::primitive(format!("{:?}", token.item))),
|
|
b::space(),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct ExternalTokensShape;
|
|
|
|
impl ExpandSyntax for ExternalTokensShape {
|
|
type Output = ExternalTokensSyntax;
|
|
|
|
fn name(&self) -> &'static str {
|
|
"external command"
|
|
}
|
|
|
|
fn expand_syntax<'a, 'b>(
|
|
&self,
|
|
token_nodes: &'b mut TokensIterator<'a>,
|
|
context: &ExpandContext,
|
|
) -> Result<Self::Output, ParseError> {
|
|
let mut out: Vec<Spanned<String>> = vec![];
|
|
|
|
let start = token_nodes.span_at_cursor();
|
|
|
|
loop {
|
|
match expand_syntax(&ExternalExpressionShape, token_nodes, context) {
|
|
Err(_) | Ok(None) => break,
|
|
Ok(Some(span)) => out.push(span.spanned_string(context.source())),
|
|
}
|
|
}
|
|
|
|
let end = token_nodes.span_at_cursor();
|
|
|
|
Ok(ExternalTokensSyntax {
|
|
tokens: out.spanned(start.until(end)),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ColorSyntax for ExternalTokensShape {
|
|
type Info = ();
|
|
type Input = ();
|
|
|
|
fn name(&self) -> &'static str {
|
|
"ExternalTokensShape"
|
|
}
|
|
|
|
fn color_syntax<'a, 'b>(
|
|
&self,
|
|
_input: &(),
|
|
token_nodes: &'b mut TokensIterator<'a>,
|
|
context: &ExpandContext,
|
|
) -> Self::Info {
|
|
loop {
|
|
// Allow a space
|
|
color_syntax(&MaybeSpaceShape, token_nodes, context);
|
|
|
|
// Process an external expression. External expressions are mostly words, with a
|
|
// few exceptions (like $variables and path expansion rules)
|
|
match color_syntax(&ExternalExpressionShape, token_nodes, context).1 {
|
|
ExternalExpressionResult::Eof => break,
|
|
ExternalExpressionResult::Processed => continue,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct ExternalExpressionShape;
|
|
|
|
impl ExpandSyntax for ExternalExpressionShape {
|
|
type Output = Option<Span>;
|
|
|
|
fn name(&self) -> &'static str {
|
|
"external expression"
|
|
}
|
|
|
|
fn expand_syntax<'a, 'b>(
|
|
&self,
|
|
token_nodes: &'b mut TokensIterator<'a>,
|
|
context: &ExpandContext,
|
|
) -> Result<Self::Output, ParseError> {
|
|
expand_syntax(&MaybeSpaceShape, token_nodes, context)?;
|
|
|
|
let first = expand_atom(
|
|
token_nodes,
|
|
"external command",
|
|
context,
|
|
ExpansionRule::new().allow_external_word(),
|
|
)?
|
|
.span;
|
|
|
|
let mut last = first;
|
|
|
|
loop {
|
|
let continuation = expand_expr(&ExternalContinuationShape, token_nodes, context);
|
|
|
|
if let Ok(continuation) = continuation {
|
|
last = continuation.span;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(Some(first.until(last)))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
struct ExternalHeadShape;
|
|
|
|
impl ExpandExpression for ExternalHeadShape {
|
|
fn name(&self) -> &'static str {
|
|
"external argument"
|
|
}
|
|
|
|
fn expand_expr<'a, 'b>(
|
|
&self,
|
|
token_nodes: &'b mut TokensIterator<'a>,
|
|
context: &ExpandContext,
|
|
) -> Result<Expression, ParseError> {
|
|
let atom = expand_atom(
|
|
token_nodes,
|
|
"external argument",
|
|
context,
|
|
ExpansionRule::new()
|
|
.allow_external_word()
|
|
.treat_size_as_word(),
|
|
)?;
|
|
|
|
let span = atom.span;
|
|
|
|
Ok(match &atom.unspanned {
|
|
UnspannedAtomicToken::Eof { .. } => unreachable!("ExpansionRule doesn't allow EOF"),
|
|
UnspannedAtomicToken::Error { .. } => unreachable!("ExpansionRule doesn't allow Error"),
|
|
UnspannedAtomicToken::Size { .. } => unreachable!("ExpansionRule treats size as word"),
|
|
UnspannedAtomicToken::Whitespace { .. } => {
|
|
unreachable!("ExpansionRule doesn't allow Whitespace")
|
|
}
|
|
UnspannedAtomicToken::Separator { .. } => {
|
|
unreachable!("ExpansionRule doesn't allow Separator")
|
|
}
|
|
UnspannedAtomicToken::Comment { .. } => {
|
|
unreachable!("ExpansionRule doesn't allow Comment")
|
|
}
|
|
UnspannedAtomicToken::ShorthandFlag { .. }
|
|
| UnspannedAtomicToken::SquareDelimited { .. }
|
|
| UnspannedAtomicToken::RoundDelimited { .. } => {
|
|
return Err(ParseError::mismatch(
|
|
"external command name",
|
|
atom.spanned_type_name(),
|
|
))
|
|
}
|
|
UnspannedAtomicToken::ExternalCommand { command } => {
|
|
Expression::external_command(*command, span)
|
|
}
|
|
UnspannedAtomicToken::Number { number } => {
|
|
Expression::number(number.to_number(context.source()), span)
|
|
}
|
|
UnspannedAtomicToken::String { body } => Expression::string(*body, span),
|
|
UnspannedAtomicToken::ItVariable { name } => Expression::it_variable(*name, span),
|
|
UnspannedAtomicToken::Variable { name } => Expression::variable(*name, span),
|
|
UnspannedAtomicToken::ExternalWord { .. }
|
|
| UnspannedAtomicToken::GlobPattern { .. }
|
|
| UnspannedAtomicToken::Word { .. }
|
|
| UnspannedAtomicToken::Dot { .. }
|
|
| UnspannedAtomicToken::DotDot { .. }
|
|
| UnspannedAtomicToken::CompareOperator { .. } => {
|
|
Expression::external_command(span, span)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
struct ExternalContinuationShape;
|
|
|
|
impl ExpandExpression for ExternalContinuationShape {
|
|
fn name(&self) -> &'static str {
|
|
"external argument"
|
|
}
|
|
|
|
fn expand_expr<'a, 'b>(
|
|
&self,
|
|
token_nodes: &'b mut TokensIterator<'a>,
|
|
context: &ExpandContext,
|
|
) -> Result<Expression, ParseError> {
|
|
let atom = expand_atom(
|
|
token_nodes,
|
|
"external argument",
|
|
context,
|
|
ExpansionRule::new()
|
|
.allow_external_word()
|
|
.treat_size_as_word(),
|
|
)?;
|
|
|
|
let span = atom.span;
|
|
|
|
Ok(match &atom.unspanned {
|
|
UnspannedAtomicToken::Eof { .. } => unreachable!("ExpansionRule doesn't allow EOF"),
|
|
UnspannedAtomicToken::Error { .. } => unreachable!("ExpansionRule doesn't allow Error"),
|
|
UnspannedAtomicToken::Number { number } => {
|
|
Expression::number(number.to_number(context.source()), span)
|
|
}
|
|
UnspannedAtomicToken::Size { .. } => unreachable!("ExpansionRule treats size as word"),
|
|
UnspannedAtomicToken::ExternalCommand { .. } => {
|
|
unreachable!("ExpansionRule doesn't allow ExternalCommand")
|
|
}
|
|
UnspannedAtomicToken::Whitespace { .. } => {
|
|
unreachable!("ExpansionRule doesn't allow Whitespace")
|
|
}
|
|
UnspannedAtomicToken::Separator { .. } => {
|
|
unreachable!("ExpansionRule doesn't allow Separator")
|
|
}
|
|
UnspannedAtomicToken::Comment { .. } => {
|
|
unreachable!("ExpansionRule doesn't allow Comment")
|
|
}
|
|
UnspannedAtomicToken::String { body } => Expression::string(*body, span),
|
|
UnspannedAtomicToken::ItVariable { name } => Expression::it_variable(*name, span),
|
|
UnspannedAtomicToken::Variable { name } => Expression::variable(*name, span),
|
|
UnspannedAtomicToken::ExternalWord { .. }
|
|
| UnspannedAtomicToken::GlobPattern { .. }
|
|
| UnspannedAtomicToken::Word { .. }
|
|
| UnspannedAtomicToken::ShorthandFlag { .. }
|
|
| UnspannedAtomicToken::Dot { .. }
|
|
| UnspannedAtomicToken::DotDot { .. }
|
|
| UnspannedAtomicToken::CompareOperator { .. } => Expression::bare(span),
|
|
UnspannedAtomicToken::SquareDelimited { .. }
|
|
| UnspannedAtomicToken::RoundDelimited { .. } => {
|
|
return Err(ParseError::mismatch(
|
|
"external argument",
|
|
atom.spanned_type_name(),
|
|
))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ColorSyntax for ExternalExpressionShape {
|
|
type Info = ExternalExpressionResult;
|
|
type Input = ();
|
|
|
|
fn name(&self) -> &'static str {
|
|
"ExternalExpressionShape"
|
|
}
|
|
|
|
fn color_syntax<'a, 'b>(
|
|
&self,
|
|
_input: &(),
|
|
token_nodes: &'b mut TokensIterator<'a>,
|
|
context: &ExpandContext,
|
|
) -> ExternalExpressionResult {
|
|
let atom = match expand_atom(
|
|
token_nodes,
|
|
"external word",
|
|
context,
|
|
ExpansionRule::permissive(),
|
|
) {
|
|
Err(_) => unreachable!("TODO: separate infallible expand_atom"),
|
|
Ok(AtomicToken {
|
|
unspanned: UnspannedAtomicToken::Eof { .. },
|
|
..
|
|
}) => return ExternalExpressionResult::Eof,
|
|
Ok(atom) => atom,
|
|
};
|
|
|
|
token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes));
|
|
return ExternalExpressionResult::Processed;
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub enum ExternalExpressionResult {
|
|
Eof,
|
|
Processed,
|
|
}
|