Add single tick string interpolation (#581)

* Add single tick string interpolation

* give string interpolation its own highlighting
This commit is contained in:
JT 2021-12-26 07:50:02 +11:00 committed by GitHub
parent d603086d2f
commit ca6baf7a46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 99 additions and 35 deletions

View file

@ -118,6 +118,13 @@ impl Highlighter for NuHighlighter {
next_token,
))
}
FlatShape::StringInterpolation => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Filepath => output.push((
// nushell Path
get_shape_color(shape.1.to_string(), &self.config),

View file

@ -18,6 +18,7 @@ pub fn get_shape_color(shape: String, conf: &Config) -> Style {
"flatshape_operator" => Style::new().fg(Color::Yellow),
"flatshape_signature" => Style::new().fg(Color::Green).bold(),
"flatshape_string" => Style::new().fg(Color::Green),
"flatshape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
"flatshape_filepath" => Style::new().fg(Color::Cyan),
"flatshape_globpattern" => Style::new().fg(Color::Cyan).bold(),
"flatshape_variable" => Style::new().fg(Color::Purple),

View file

@ -3,7 +3,8 @@ use std::cmp::Ordering;
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement};
use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::{
IntoPipelineData, PipelineData, Range, ShellError, Span, Spanned, Type, Unit, Value, VarId,
IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Range, ShellError, Span,
Spanned, Type, Unit, Value, VarId,
};
use crate::get_full_help;
@ -341,6 +342,23 @@ pub fn eval_expression(
})
}
Expr::Keyword(_, _, expr) => eval_expression(engine_state, stack, expr),
Expr::StringInterpolation(exprs) => {
let mut parts = vec![];
for expr in exprs {
parts.push(eval_expression(engine_state, stack, expr)?);
}
let config = stack.get_config().unwrap_or_default();
parts
.into_iter()
.into_pipeline_data(None)
.collect_string("", &config)
.map(|x| Value::String {
val: x,
span: expr.span,
})
}
Expr::String(s) => Ok(Value::String {
val: s.clone(),
span: expr.span,

View file

@ -19,6 +19,7 @@ pub enum FlatShape {
Operator,
Signature,
String,
StringInterpolation,
Filepath,
GlobPattern,
Variable,
@ -42,6 +43,7 @@ impl Display for FlatShape {
FlatShape::Operator => write!(f, "flatshape_operator"),
FlatShape::Signature => write!(f, "flatshape_signature"),
FlatShape::String => write!(f, "flatshape_string"),
FlatShape::StringInterpolation => write!(f, "flatshape_string_interpolation"),
FlatShape::Filepath => write!(f, "flatshape_filepath"),
FlatShape::GlobPattern => write!(f, "flatshape_globpattern"),
FlatShape::Variable => write!(f, "flatshape_variable"),
@ -215,6 +217,26 @@ pub fn flatten_expression(
}
output
}
Expr::StringInterpolation(exprs) => {
let mut output = vec![(
Span {
start: expr.span.start,
end: expr.span.start + 2,
},
FlatShape::StringInterpolation,
)];
for expr in exprs {
output.extend(flatten_expression(working_set, expr));
}
output.push((
Span {
start: expr.span.end - 1,
end: expr.span.end,
},
FlatShape::StringInterpolation,
));
output
}
Expr::Record(list) => {
let mut output = vec![];
for l in list {

View file

@ -1014,7 +1014,7 @@ pub(crate) fn parse_dollar_expr(
) -> (Expression, Option<ParseError>) {
let contents = working_set.get_span_contents(span);
if contents.starts_with(b"$\"") {
if contents.starts_with(b"$\"") || contents.starts_with(b"$'") {
parse_string_interpolation(working_set, span)
} else if let (expr, None) = parse_range(working_set, span) {
(expr, None)
@ -1036,16 +1036,22 @@ pub fn parse_string_interpolation(
let contents = working_set.get_span_contents(span);
let start = if contents.starts_with(b"$\"") {
span.start + 2
let (start, end) = if contents.starts_with(b"$\"") {
let end = if contents.ends_with(b"\"") && contents.len() > 2 {
span.end - 1
} else {
span.end
};
(span.start + 2, end)
} else if contents.starts_with(b"$'") {
let end = if contents.ends_with(b"'") && contents.len() > 2 {
span.end - 1
} else {
span.end
};
(span.start + 2, end)
} else {
span.start
};
let end = if contents.ends_with(b"\"") && contents.len() > 2 {
span.end - 1
} else {
span.end
(span.start, span.end)
};
let inner_span = Span { start, end };
@ -1134,30 +1140,15 @@ pub fn parse_string_interpolation(
}
}
if let Some(decl_id) = working_set.find_decl(b"build-string") {
(
Expression {
expr: Expr::Call(Box::new(Call {
head: Span {
start: span.start,
end: span.start + 2,
},
named: vec![],
positional: output,
decl_id,
})),
span,
ty: Type::String,
custom_completion: None,
},
error,
)
} else {
(
Expression::garbage(span),
Some(ParseError::UnknownCommand(span)),
)
}
(
Expression {
expr: Expr::StringInterpolation(output),
span,
ty: Type::String,
custom_completion: None,
},
error,
)
}
pub fn parse_variable_expr(
@ -3529,6 +3520,12 @@ pub fn find_captures_in_expr(
}
Expr::Signature(_) => {}
Expr::String(_) => {}
Expr::StringInterpolation(exprs) => {
for expr in exprs {
let result = find_captures_in_expr(working_set, expr, seen);
output.extend(&result);
}
}
Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
let block = working_set.get_block(*block_id);
let result = find_captures_in_block(working_set, block, seen);

View file

@ -33,6 +33,7 @@ pub enum Expr {
FullCellPath(Box<FullCellPath>),
ImportPattern(ImportPattern),
Signature(Box<Signature>),
StringInterpolation(Vec<Expression>),
Nothing,
Garbage,
}

View file

@ -160,6 +160,14 @@ impl Expression {
}
false
}
Expr::StringInterpolation(items) => {
for i in items {
if i.has_in_variable(working_set) {
return true;
}
}
false
}
Expr::Operator(_) => false,
Expr::Range(left, middle, right, ..) => {
if let Some(left) = &left {
@ -321,6 +329,11 @@ impl Expression {
}
Expr::Signature(_) => {}
Expr::String(_) => {}
Expr::StringInterpolation(items) => {
for i in items {
i.replace_in_variable(working_set, new_var_id)
}
}
Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
let block = working_set.get_block(*block_id);

View file

@ -79,3 +79,8 @@ fn string_in_valuestream() -> TestResult {
"true",
)
}
#[test]
fn single_tick_interpolation() -> TestResult {
run_test(r#"$'(3 + 4)'"#, "7")
}