Match Hook fields to match new config defaults

This commit is contained in:
Ian Manske
2024-11-14 20:53:26 -08:00
parent 8c1ab7e0a3
commit cba76b6e23
4 changed files with 107 additions and 101 deletions

View File

@ -16,7 +16,7 @@ use crate::{
use crossterm::cursor::SetCursorStyle; use crossterm::cursor::SetCursorStyle;
use log::{error, trace, warn}; use log::{error, trace, warn};
use miette::{ErrReport, IntoDiagnostic, Result}; use miette::{ErrReport, IntoDiagnostic, Result};
use nu_cmd_base::{hook::eval_hook, util::get_editor}; use nu_cmd_base::util::get_editor;
use nu_color_config::StyleComputer; use nu_color_config::StyleComputer;
#[allow(deprecated)] #[allow(deprecated)]
use nu_engine::{convert_env_values, current_dir_str, env_to_strings}; use nu_engine::{convert_env_values, current_dir_str, env_to_strings};
@ -313,20 +313,26 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("reset signals", start_time, use_color); perf!("reset signals", start_time, use_color);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user, // Right before we start our prompt and take input from the user, fire the "pre_prompt" hook
// fire the "pre_prompt" hook if let Err(err) = hook::eval_hooks(
if let Some(hook) = engine_state.get_config().hooks.pre_prompt.clone() { engine_state,
if let Err(err) = eval_hook(engine_state, &mut stack, None, vec![], &hook, "pre_prompt") { &mut stack,
report_shell_error(engine_state, &err); vec![],
} &engine_state.get_config().hooks.pre_prompt.clone(),
"pre_prompt",
) {
report_shell_error(engine_state, &err);
} }
perf!("pre-prompt hook", start_time, use_color); perf!("pre-prompt hook", start_time, use_color);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for // Next, check all the environment variables they ask for
// fire the "env_change" hook // fire the "env_change" hook
let env_change = engine_state.get_config().hooks.env_change.clone(); if let Err(error) = hook::eval_env_change_hook(
if let Err(error) = hook::eval_env_change_hook(env_change, engine_state, &mut stack) { &engine_state.get_config().hooks.env_change.clone(),
engine_state,
&mut stack,
) {
report_shell_error(engine_state, &error) report_shell_error(engine_state, &error)
} }
perf!("env-change hook", start_time, use_color); perf!("env-change hook", start_time, use_color);
@ -511,18 +517,17 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
// Right before we start running the code the user gave us, fire the `pre_execution` // Right before we start running the code the user gave us, fire the `pre_execution`
// hook // hook
if let Some(hook) = config.hooks.pre_execution.clone() { {
// Set the REPL buffer to the current command for the "pre_execution" hook // Set the REPL buffer to the current command for the "pre_execution" hook
let mut repl = engine_state.repl_state.lock().expect("repl state mutex"); let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
repl.buffer = repl_cmd_line_text.to_string(); repl.buffer = repl_cmd_line_text.to_string();
drop(repl); drop(repl);
if let Err(err) = eval_hook( if let Err(err) = hook::eval_hooks(
engine_state, engine_state,
&mut stack, &mut stack,
None,
vec![], vec![],
&hook, &engine_state.get_config().hooks.pre_execution.clone(),
"pre_execution", "pre_execution",
) { ) {
report_shell_error(engine_state, &err); report_shell_error(engine_state, &err);

View File

@ -7,49 +7,56 @@ use nu_protocol::{
engine::{Closure, EngineState, Stack, StateWorkingSet}, engine::{Closure, EngineState, Stack, StateWorkingSet},
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
}; };
use std::sync::Arc; use std::{collections::HashMap, sync::Arc};
pub fn eval_env_change_hook( pub fn eval_env_change_hook(
env_change_hook: Option<Value>, env_change_hook: &HashMap<String, Value>,
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
if let Some(hook) = env_change_hook { for (env, hook) in env_change_hook {
match hook { let before = engine_state.previous_env_vars.get(env);
Value::Record { val, .. } => { let after = stack.get_env_var(engine_state, env);
for (env_name, hook_value) in &*val { if before != after {
let before = engine_state.previous_env_vars.get(env_name); let before = before.cloned().unwrap_or_default();
let after = stack.get_env_var(engine_state, env_name); let after = after.cloned().unwrap_or_default();
if before != after {
let before = before.cloned().unwrap_or_default();
let after = after.cloned().unwrap_or_default();
eval_hook( eval_hook(
engine_state, engine_state,
stack, stack,
None, None,
vec![("$before".into(), before), ("$after".into(), after.clone())], vec![("$before".into(), before), ("$after".into(), after.clone())],
hook_value, hook,
"env_change", "env_change",
)?; )?;
Arc::make_mut(&mut engine_state.previous_env_vars) Arc::make_mut(&mut engine_state.previous_env_vars).insert(env.clone(), after);
.insert(env_name.clone(), after);
}
}
}
x => {
return Err(ShellError::TypeMismatch {
err_message: "record for the 'env_change' hook".to_string(),
span: x.span(),
});
}
} }
} }
Ok(()) Ok(())
} }
pub fn eval_hooks(
engine_state: &mut EngineState,
stack: &mut Stack,
arguments: Vec<(String, Value)>,
hooks: &[Value],
hook_name: &str,
) -> Result<(), ShellError> {
for hook in hooks {
eval_hook(
engine_state,
stack,
None,
arguments.clone(),
hook,
&format!("{hook_name} list, recursive"),
)?;
}
Ok(())
}
pub fn eval_hook( pub fn eval_hook(
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
@ -127,16 +134,7 @@ pub fn eval_hook(
} }
} }
Value::List { vals, .. } => { Value::List { vals, .. } => {
for val in vals { eval_hooks(engine_state, stack, arguments, vals, hook_name)?;
eval_hook(
engine_state,
stack,
None,
arguments.clone(),
val,
&format!("{hook_name} list, recursive"),
)?;
}
} }
Value::Record { val, .. } => { Value::Record { val, .. } => {
// Hooks can optionally be a record in this form: // Hooks can optionally be a record in this form:

View File

@ -1,13 +1,13 @@
use super::prelude::*; use super::prelude::*;
use crate as nu_protocol; use crate as nu_protocol;
use crate::Record; use std::collections::HashMap;
/// Definition of a parsed hook from the config object /// Definition of a parsed hook from the config object
#[derive(Clone, Debug, IntoValue, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, IntoValue, PartialEq, Serialize, Deserialize)]
pub struct Hooks { pub struct Hooks {
pub pre_prompt: Option<Value>, pub pre_prompt: Vec<Value>,
pub pre_execution: Option<Value>, pub pre_execution: Vec<Value>,
pub env_change: Option<Value>, pub env_change: HashMap<String, Value>,
pub display_output: Option<Value>, pub display_output: Option<Value>,
pub command_not_found: Option<Value>, pub command_not_found: Option<Value>,
} }
@ -15,14 +15,14 @@ pub struct Hooks {
impl Hooks { impl Hooks {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
pre_prompt: Some(Value::list(vec![], Span::unknown())), pre_prompt: Vec::new(),
pre_execution: Some(Value::list(vec![], Span::unknown())), pre_execution: Vec::new(),
env_change: Some(Value::record(Record::default(), Span::unknown())), env_change: HashMap::new(),
display_output: Some(Value::string( display_output: Some(Value::string(
"if (term size).columns >= 100 { table -e } else { table }", "if (term size).columns >= 100 { table -e } else { table }",
Span::unknown(), Span::unknown(),
)), )),
command_not_found: Some(Value::list(vec![], Span::unknown())), command_not_found: None,
} }
} }
} }
@ -40,14 +40,6 @@ impl UpdateFromValue for Hooks {
path: &mut ConfigPath<'a>, path: &mut ConfigPath<'a>,
errors: &mut ConfigErrors, errors: &mut ConfigErrors,
) { ) {
fn update_option(field: &mut Option<Value>, value: &Value) {
if value.is_nothing() {
*field = None;
} else {
*field = Some(value.clone());
}
}
let Value::Record { val: record, .. } = value else { let Value::Record { val: record, .. } = value else {
errors.type_mismatch(path, Type::record(), value); errors.type_mismatch(path, Type::record(), value);
return; return;
@ -56,11 +48,35 @@ impl UpdateFromValue for Hooks {
for (col, val) in record.iter() { for (col, val) in record.iter() {
let path = &mut path.push(col); let path = &mut path.push(col);
match col.as_str() { match col.as_str() {
"pre_prompt" => update_option(&mut self.pre_prompt, val), "pre_prompt" => {
"pre_execution" => update_option(&mut self.pre_execution, val), if let Ok(hooks) = val.as_list() {
"env_change" => update_option(&mut self.env_change, val), self.pre_prompt = hooks.into()
"display_output" => update_option(&mut self.display_output, val), } else {
"command_not_found" => update_option(&mut self.command_not_found, val), errors.type_mismatch(path, Type::list(Type::Any), val);
}
}
"pre_execution" => {
if let Ok(hooks) = val.as_list() {
self.pre_execution = hooks.into()
} else {
errors.type_mismatch(path, Type::list(Type::Any), val);
}
}
"env_change" => self.env_change.update(val, path, errors),
"display_output" => {
self.display_output = if val.is_nothing() {
None
} else {
Some(val.clone())
}
}
"command_not_found" => {
self.command_not_found = if val.is_nothing() {
None
} else {
Some(val.clone())
}
}
_ => errors.unknown_option(path, val), _ => errors.unknown_option(path, val),
} }
} }

View File

@ -1,4 +1,4 @@
use nu_cmd_base::hook::{eval_env_change_hook, eval_hook}; use nu_cmd_base::hook::{eval_env_change_hook, eval_hooks};
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::parse; use nu_parser::parse;
use nu_protocol::{ use nu_protocol::{
@ -250,24 +250,14 @@ pub fn nu_repl() {
} }
// Check for pre_prompt hook // Check for pre_prompt hook
let config = engine_state.get_config(); let hook = engine_state.get_config().hooks.pre_prompt.clone();
if let Some(hook) = config.hooks.pre_prompt.clone() { if let Err(err) = eval_hooks(&mut engine_state, &mut stack, vec![], &hook, "pre_prompt") {
if let Err(err) = eval_hook( outcome_err(&engine_state, &err);
&mut engine_state,
&mut stack,
None,
vec![],
&hook,
"pre_prompt",
) {
outcome_err(&engine_state, &err);
}
} }
// Check for env change hook // Check for env change hook
let config = engine_state.get_config();
if let Err(err) = eval_env_change_hook( if let Err(err) = eval_env_change_hook(
config.hooks.env_change.clone(), &engine_state.get_config().hooks.env_change.clone(),
&mut engine_state, &mut engine_state,
&mut stack, &mut stack,
) { ) {
@ -275,7 +265,6 @@ pub fn nu_repl() {
} }
// Check for pre_execution hook // Check for pre_execution hook
let config = engine_state.get_config();
engine_state engine_state
.repl_state .repl_state
@ -283,17 +272,15 @@ pub fn nu_repl() {
.expect("repl state mutex") .expect("repl state mutex")
.buffer = line.to_string(); .buffer = line.to_string();
if let Some(hook) = config.hooks.pre_execution.clone() { let hook = engine_state.get_config().hooks.pre_execution.clone();
if let Err(err) = eval_hook( if let Err(err) = eval_hooks(
&mut engine_state, &mut engine_state,
&mut stack, &mut stack,
None, vec![],
vec![], &hook,
&hook, "pre_execution",
"pre_execution", ) {
) { outcome_err(&engine_state, &err);
outcome_err(&engine_state, &err);
}
} }
// Eval the REPL line // Eval the REPL line