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 log::{error, trace, warn};
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;
#[allow(deprecated)]
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);
start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user,
// fire the "pre_prompt" hook
if let Some(hook) = engine_state.get_config().hooks.pre_prompt.clone() {
if let Err(err) = eval_hook(engine_state, &mut stack, None, vec![], &hook, "pre_prompt") {
report_shell_error(engine_state, &err);
}
// Right before we start our prompt and take input from the user, fire the "pre_prompt" hook
if let Err(err) = hook::eval_hooks(
engine_state,
&mut stack,
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);
start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for
// 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(env_change, engine_state, &mut stack) {
if let Err(error) = hook::eval_env_change_hook(
&engine_state.get_config().hooks.env_change.clone(),
engine_state,
&mut stack,
) {
report_shell_error(engine_state, &error)
}
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`
// hook
if let Some(hook) = config.hooks.pre_execution.clone() {
{
// 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");
repl.buffer = repl_cmd_line_text.to_string();
drop(repl);
if let Err(err) = eval_hook(
if let Err(err) = hook::eval_hooks(
engine_state,
&mut stack,
None,
vec![],
&hook,
&engine_state.get_config().hooks.pre_execution.clone(),
"pre_execution",
) {
report_shell_error(engine_state, &err);

View File

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

View File

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

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