mirror of
https://github.com/nushell/nushell.git
synced 2025-05-01 08:34:26 +02:00
preserve variable capture spans in blocks (#15334)
Closes #15160 # User-Facing Changes Certain "variable not found" errors no longer highlight the surrounding block. Before: ```nushell do { match foo { _ => $in } } Error: nu:🐚:variable_not_found × Variable not found ╭─[entry #1:1:1] 1 │ ╭─▶ do { 2 │ │ match foo { 3 │ │ _ => $in 4 │ │ } 5 │ ├─▶ } · ╰──── variable not found ``` After: ```nushell Error: nu:🐚:variable_not_found × Variable not found ╭─[entry #1:3:10] 2 │ match foo { 3 │ _ => $in · ─┬─ · ╰── variable not found ```
This commit is contained in:
parent
7a6cfa24fc
commit
dd56c813f9
@ -642,17 +642,17 @@ impl Eval for EvalRuntime {
|
|||||||
.get_block(block_id)
|
.get_block(block_id)
|
||||||
.captures
|
.captures
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&id| {
|
.map(|(id, span)| {
|
||||||
stack
|
stack
|
||||||
.get_var(id, span)
|
.get_var(*id, *span)
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
engine_state
|
engine_state
|
||||||
.get_var(id)
|
.get_var(*id)
|
||||||
.const_val
|
.const_val
|
||||||
.clone()
|
.clone()
|
||||||
.ok_or(ShellError::VariableNotFoundAtRuntime { span })
|
.ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
|
||||||
})
|
})
|
||||||
.map(|var| (id, var))
|
.map(|var| (*id, var))
|
||||||
})
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
@ -857,7 +857,7 @@ fn literal_value(
|
|||||||
let captures = block
|
let captures = block
|
||||||
.captures
|
.captures
|
||||||
.iter()
|
.iter()
|
||||||
.map(|var_id| get_var(ctx, *var_id, span).map(|val| (*var_id, val)))
|
.map(|(var_id, span)| get_var(ctx, *var_id, *span).map(|val| (*var_id, val)))
|
||||||
.collect::<Result<Vec<_>, ShellError>>()?;
|
.collect::<Result<Vec<_>, ShellError>>()?;
|
||||||
Value::closure(
|
Value::closure(
|
||||||
Closure {
|
Closure {
|
||||||
|
@ -6589,9 +6589,9 @@ pub fn discover_captures_in_expr(
|
|||||||
None => {
|
None => {
|
||||||
let block = working_set.get_block(block_id);
|
let block = working_set.get_block(block_id);
|
||||||
if !block.captures.is_empty() {
|
if !block.captures.is_empty() {
|
||||||
for capture in &block.captures {
|
for (capture, span) in &block.captures {
|
||||||
if !seen.contains(capture) {
|
if !seen.contains(capture) {
|
||||||
output.push((*capture, call.head));
|
output.push((*capture, *span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -6905,8 +6905,7 @@ pub fn parse(
|
|||||||
&mut captures,
|
&mut captures,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
Arc::make_mut(&mut output).captures =
|
Arc::make_mut(&mut output).captures = captures;
|
||||||
captures.into_iter().map(|(var_id, _)| var_id).collect();
|
|
||||||
}
|
}
|
||||||
Err(err) => working_set.error(err),
|
Err(err) => working_set.error(err),
|
||||||
}
|
}
|
||||||
@ -6961,7 +6960,7 @@ pub fn parse(
|
|||||||
&& block_id.get() >= working_set.permanent_state.num_blocks()
|
&& block_id.get() >= working_set.permanent_state.num_blocks()
|
||||||
{
|
{
|
||||||
let block = working_set.get_block_mut(block_id);
|
let block = working_set.get_block_mut(block_id);
|
||||||
block.captures = captures.into_iter().map(|(var_id, _)| var_id).collect();
|
block.captures = captures;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub signature: Box<Signature>,
|
pub signature: Box<Signature>,
|
||||||
pub pipelines: Vec<Pipeline>,
|
pub pipelines: Vec<Pipeline>,
|
||||||
pub captures: Vec<VarId>,
|
pub captures: Vec<(VarId, Span)>,
|
||||||
pub redirect_env: bool,
|
pub redirect_env: bool,
|
||||||
/// The block compiled to IR instructions. Not available for subexpressions.
|
/// The block compiled to IR instructions. Not available for subexpressions.
|
||||||
pub ir_block: Option<IrBlock>,
|
pub ir_block: Option<IrBlock>,
|
||||||
|
@ -111,7 +111,10 @@ impl Expression {
|
|||||||
Expr::UnaryNot(expr) => expr.has_in_variable(working_set),
|
Expr::UnaryNot(expr) => expr.has_in_variable(working_set),
|
||||||
Expr::Block(block_id) | Expr::Closure(block_id) => {
|
Expr::Block(block_id) | Expr::Closure(block_id) => {
|
||||||
let block = working_set.get_block(*block_id);
|
let block = working_set.get_block(*block_id);
|
||||||
block.captures.contains(&IN_VARIABLE_ID)
|
block
|
||||||
|
.captures
|
||||||
|
.iter()
|
||||||
|
.any(|(var_id, _)| var_id == &IN_VARIABLE_ID)
|
||||||
|| block
|
|| block
|
||||||
.pipelines
|
.pipelines
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -322,12 +322,12 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_captures(&self, engine_state: &EngineState, captures: &[VarId]) -> Stack {
|
pub fn gather_captures(&self, engine_state: &EngineState, captures: &[(VarId, Span)]) -> Stack {
|
||||||
let mut vars = Vec::with_capacity(captures.len());
|
let mut vars = Vec::with_capacity(captures.len());
|
||||||
|
|
||||||
let fake_span = Span::new(0, 0);
|
let fake_span = Span::new(0, 0);
|
||||||
|
|
||||||
for capture in captures {
|
for (capture, _) in captures {
|
||||||
// Note: this assumes we have calculated captures correctly and that commands
|
// Note: this assumes we have calculated captures correctly and that commands
|
||||||
// that take in a var decl will manually set this into scope when running the blocks
|
// that take in a var decl will manually set this into scope when running the blocks
|
||||||
if let Ok(value) = self.get_var(*capture, fake_span) {
|
if let Ok(value) = self.get_var(*capture, fake_span) {
|
||||||
|
Loading…
Reference in New Issue
Block a user