Add NU_REPL_PREV_ERROR env variable

This commit is contained in:
Ian Manske
2024-09-29 13:23:13 -07:00
parent 8200831b07
commit 87843966fb
6 changed files with 72 additions and 26 deletions

View File

@@ -121,7 +121,7 @@ fn bench_eval_source(
let mut engine = engine.clone(); let mut engine = engine.clone();
let fname: &str = &fname.clone(); let fname: &str = &fname.clone();
let source: &[u8] = &source.clone(); let source: &[u8] = &source.clone();
black_box(eval_source( let _ = black_box(eval_source(
&mut engine, &mut engine,
&mut stack, &mut stack,
source, source,

View File

@@ -299,7 +299,8 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -
&old_plugin_file_path.to_string_lossy(), &old_plugin_file_path.to_string_lossy(),
PipelineData::Empty, PipelineData::Empty,
false, false,
) != 0 )
.is_err()
{ {
return false; return false;
} }

View File

@@ -1,4 +1,4 @@
use crate::util::eval_source; use crate::util::{eval_source, ParseOrShellError};
use log::{info, trace}; use log::{info, trace};
use nu_engine::{convert_env_values, eval_block}; use nu_engine::{convert_env_values, eval_block};
use nu_parser::parse; use nu_parser::parse;
@@ -106,7 +106,7 @@ pub fn evaluate_file(
engine_state.merge_delta(working_set.delta)?; engine_state.merge_delta(working_set.delta)?;
// Check if the file contains a main command. // Check if the file contains a main command.
let exit_code = if engine_state.find_decl(b"main", &[]).is_some() { let result = if engine_state.find_decl(b"main", &[]).is_some() {
// Evaluate the file, but don't run main yet. // Evaluate the file, but don't run main yet.
let pipeline = let pipeline =
match eval_block::<WithoutDebug>(engine_state, stack, &block, PipelineData::empty()) { match eval_block::<WithoutDebug>(engine_state, stack, &block, PipelineData::empty()) {
@@ -136,7 +136,11 @@ pub fn evaluate_file(
eval_source(engine_state, stack, &file, file_path_str, input, true) eval_source(engine_state, stack, &file, file_path_str, input, true)
}; };
if exit_code != 0 { if let Err(err) = result {
let exit_code = match err {
ParseOrShellError::ShellError(err) => err.exit_code(),
ParseOrShellError::ParseError(_) => 1,
};
std::process::exit(exit_code); std::process::exit(exit_code);
} }

View File

@@ -10,7 +10,7 @@ use crate::{
nu_highlight::NoOpHighlighter, nu_highlight::NoOpHighlighter,
prompt_update, prompt_update,
reedline_config::{add_menus, create_keybindings, KeybindingsMode}, reedline_config::{add_menus, create_keybindings, KeybindingsMode},
util::eval_source, util::{eval_source, ParseOrShellError},
NuHighlighter, NuValidator, NushellPrompt, NuHighlighter, NuValidator, NushellPrompt,
}; };
use crossterm::cursor::SetCursorStyle; use crossterm::cursor::SetCursorStyle;
@@ -27,6 +27,7 @@ use nu_protocol::{
report_shell_error, HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, report_shell_error, HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
Value, Value,
}; };
use nu_protocol::{record, IntoValue};
use nu_utils::{ use nu_utils::{
filesystem::{have_permission, PermissionResult}, filesystem::{have_permission, PermissionResult},
perf, perf,
@@ -101,7 +102,8 @@ pub fn evaluate_repl(
let temp_file = temp_dir().join(format!("{}.nu", uuid::Uuid::new_v4())); let temp_file = temp_dir().join(format!("{}.nu", uuid::Uuid::new_v4()));
if let Some(s) = prerun_command { if let Some(s) = prerun_command {
eval_source( // TODO: ignore this error?
let _ = eval_source(
engine_state, engine_state,
&mut unique_stack, &mut unique_stack,
s.item.as_bytes(), s.item.as_bytes(),
@@ -156,7 +158,8 @@ pub fn evaluate_repl(
engine_state.generate_nu_constant(); engine_state.generate_nu_constant();
if load_std_lib.is_none() && engine_state.get_config().show_banner { if load_std_lib.is_none() && engine_state.get_config().show_banner {
eval_source( // TODO: ignore this error?
let _ = eval_source(
engine_state, engine_state,
&mut unique_stack, &mut unique_stack,
r#"use std banner; banner"#.as_bytes(), r#"use std banner; banner"#.as_bytes(),
@@ -906,7 +909,7 @@ fn do_run_cmd(
run_shell_integration_osc2(Some(s), engine_state, stack, use_color); run_shell_integration_osc2(Some(s), engine_state, stack, use_color);
} }
eval_source( let result = eval_source(
engine_state, engine_state,
stack, stack,
s.as_bytes(), s.as_bytes(),
@@ -915,6 +918,21 @@ fn do_run_cmd(
false, false,
); );
if let Err(err) = result {
let span = Span::unknown();
let err = match err {
ParseOrShellError::ShellError(err) => err.into_value(span),
ParseOrShellError::ParseError(err) => record! {
"msg" => Value::string(err.to_string(), span),
"debug" => Value::string(format!("{err:?}"), span),
}
.into_value(span),
};
stack.add_env_var("NU_REPL_PREV_ERROR".into(), err);
} else {
stack.remove_env_var(engine_state, "NU_REPL_PREV_ERROR");
}
line_editor line_editor
} }

View File

@@ -5,8 +5,8 @@ use nu_protocol::{
cli_error::report_compile_error, cli_error::report_compile_error,
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
report_parse_error, report_parse_warning, report_shell_error, PipelineData, ShellError, Span, report_parse_error, report_parse_warning, report_shell_error, ParseError, PipelineData,
Value, ShellError, Span, Value,
}; };
#[cfg(windows)] #[cfg(windows)]
use nu_utils::enable_vt_processing; use nu_utils::enable_vt_processing;
@@ -201,6 +201,24 @@ fn gather_env_vars(
} }
} }
#[derive(Clone, Debug)]
pub enum ParseOrShellError {
ParseError(ParseError),
ShellError(ShellError),
}
impl From<ParseError> for ParseOrShellError {
fn from(err: ParseError) -> Self {
Self::ParseError(err)
}
}
impl From<ShellError> for ParseOrShellError {
fn from(err: ShellError) -> Self {
Self::ShellError(err)
}
}
pub fn eval_source( pub fn eval_source(
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
@@ -208,20 +226,22 @@ pub fn eval_source(
fname: &str, fname: &str,
input: PipelineData, input: PipelineData,
allow_return: bool, allow_return: bool,
) -> i32 { ) -> Result<(), ParseOrShellError> {
let start_time = std::time::Instant::now(); let start_time = std::time::Instant::now();
let exit_code = match evaluate_source(engine_state, stack, source, fname, input, allow_return) { let result = match evaluate_source(engine_state, stack, source, fname, input, allow_return) {
Ok(failed) => { Ok(()) => {
let code = failed.into(); stack.set_last_exit_code(0, Span::unknown());
stack.set_last_exit_code(code, Span::unknown()); Ok(())
code
} }
Err(err) => { Err(ParseOrShellError::ShellError(err)) => {
report_shell_error(engine_state, &err); report_shell_error(engine_state, &err);
let code = err.exit_code();
stack.set_last_error(&err); stack.set_last_error(&err);
code Err(ParseOrShellError::ShellError(err))
}
Err(ParseOrShellError::ParseError(err)) => {
stack.set_last_exit_code(1, Span::unknown());
Err(ParseOrShellError::ParseError(err))
} }
}; };
@@ -237,7 +257,7 @@ pub fn eval_source(
engine_state.get_config().use_ansi_coloring engine_state.get_config().use_ansi_coloring
); );
exit_code result
} }
fn evaluate_source( fn evaluate_source(
@@ -247,7 +267,7 @@ fn evaluate_source(
fname: &str, fname: &str,
input: PipelineData, input: PipelineData,
allow_return: bool, allow_return: bool,
) -> Result<bool, ShellError> { ) -> Result<(), ParseOrShellError> {
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
let output = parse( let output = parse(
@@ -262,7 +282,7 @@ fn evaluate_source(
if let Some(err) = working_set.parse_errors.first() { if let Some(err) = working_set.parse_errors.first() {
report_parse_error(&working_set, err); report_parse_error(&working_set, err);
return Ok(true); return Err(ParseOrShellError::ParseError(err.clone()));
} }
if let Some(err) = working_set.compile_errors.first() { if let Some(err) = working_set.compile_errors.first() {
@@ -297,7 +317,7 @@ fn evaluate_source(
pipeline.print(engine_state, stack, true, false) pipeline.print(engine_state, stack, true, false)
}?; }?;
Ok(false) Ok(())
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -155,7 +155,9 @@ pub(crate) fn read_loginshell_file(engine_state: &mut EngineState, stack: &mut S
pub(crate) fn read_default_env_file(engine_state: &mut EngineState, stack: &mut Stack) { pub(crate) fn read_default_env_file(engine_state: &mut EngineState, stack: &mut Stack) {
let config_file = get_default_env(); let config_file = get_default_env();
eval_source(
// TODO: ignore this error?
let _ = eval_source(
engine_state, engine_state,
stack, stack,
config_file.as_bytes(), config_file.as_bytes(),
@@ -236,7 +238,8 @@ fn eval_default_config(
&config_file, is_env_config &config_file, is_env_config
); );
// Just use the contents of "default_config.nu" or "default_env.nu" // Just use the contents of "default_config.nu" or "default_env.nu"
eval_source( // TODO: ignore this error?
let _ = eval_source(
engine_state, engine_state,
stack, stack,
config_file.as_bytes(), config_file.as_bytes(),