Solomon dd56c813f9
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
```
2025-03-20 14:20:28 -04:00

111 lines
2.8 KiB
Rust

use super::Pipeline;
use crate::{engine::StateWorkingSet, ir::IrBlock, OutDest, Signature, Span, Type, VarId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Block {
pub signature: Box<Signature>,
pub pipelines: Vec<Pipeline>,
pub captures: Vec<(VarId, Span)>,
pub redirect_env: bool,
/// The block compiled to IR instructions. Not available for subexpressions.
pub ir_block: Option<IrBlock>,
pub span: Option<Span>, // None option encodes no span to avoid using test_span()
}
impl Block {
pub fn len(&self) -> usize {
self.pipelines.len()
}
pub fn is_empty(&self) -> bool {
self.pipelines.is_empty()
}
pub fn pipe_redirection(
&self,
working_set: &StateWorkingSet,
) -> (Option<OutDest>, Option<OutDest>) {
if let Some(first) = self.pipelines.first() {
first.pipe_redirection(working_set)
} else {
(None, None)
}
}
}
impl Default for Block {
fn default() -> Self {
Self::new()
}
}
impl Block {
pub fn new() -> Self {
Self {
signature: Box::new(Signature::new("")),
pipelines: vec![],
captures: vec![],
redirect_env: false,
ir_block: None,
span: None,
}
}
pub fn new_with_capacity(capacity: usize) -> Self {
Self {
signature: Box::new(Signature::new("")),
pipelines: Vec::with_capacity(capacity),
captures: vec![],
redirect_env: false,
ir_block: None,
span: None,
}
}
pub fn output_type(&self) -> Type {
if let Some(last) = self.pipelines.last() {
if let Some(last) = last.elements.last() {
if last.redirection.is_some() {
Type::Any
} else {
last.expr.ty.clone()
}
} else {
Type::Nothing
}
} else {
Type::Nothing
}
}
/// Replace any `$in` variables in the initial element of pipelines within the block
pub fn replace_in_variable(
&mut self,
working_set: &mut StateWorkingSet<'_>,
new_var_id: VarId,
) {
for pipeline in self.pipelines.iter_mut() {
if let Some(element) = pipeline.elements.first_mut() {
element.replace_in_variable(working_set, new_var_id);
}
}
}
}
impl<T> From<T> for Block
where
T: Iterator<Item = Pipeline>,
{
fn from(pipelines: T) -> Self {
Self {
signature: Box::new(Signature::new("")),
pipelines: pipelines.collect(),
captures: vec![],
redirect_env: false,
ir_block: None,
span: None,
}
}
}