Fix hooks on 0.92.0 (#12383)

# Description
Fixes #12382, where overlay changes from hooks were not preserved into
the global state. This was due to creating child stacks for hooks, when
the global stack should have been used instead.
This commit is contained in:
Ian Manske 2024-04-04 07:25:54 +00:00 committed by GitHub
parent cbf7feef22
commit cd00a489af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 60 additions and 61 deletions

View File

@ -281,10 +281,42 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
); );
start_time = std::time::Instant::now(); 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_error_new(engine_state, &err);
}
}
perf(
"pre-prompt hook",
start_time,
file!(),
line!(),
column!(),
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) {
report_error_new(engine_state, &error)
}
perf(
"env-change hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
let engine_reference = Arc::new(engine_state.clone());
let config = engine_state.get_config(); let config = engine_state.get_config();
let engine_reference = std::sync::Arc::new(engine_state.clone()); start_time = std::time::Instant::now();
// Find the configured cursor shapes for each mode // Find the configured cursor shapes for each mode
let cursor_config = CursorConfig { let cursor_config = CursorConfig {
vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_insert), vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_insert),
@ -304,7 +336,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
// at this line we have cloned the state for the completer and the transient prompt // at this line we have cloned the state for the completer and the transient prompt
// until we drop those, we cannot use the stack in the REPL loop itself // until we drop those, we cannot use the stack in the REPL loop itself
// See STACK-REFERENCE to see where we have taken a reference // See STACK-REFERENCE to see where we have taken a reference
let mut stack_arc = Arc::new(stack); let stack_arc = Arc::new(stack);
let mut line_editor = line_editor let mut line_editor = line_editor
.use_kitty_keyboard_enhancement(config.use_kitty_protocol) .use_kitty_keyboard_enhancement(config.use_kitty_protocol)
@ -427,50 +459,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
use_color, 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) = config.hooks.pre_prompt.clone() {
if let Err(err) = eval_hook(
engine_state,
&mut Stack::with_parent(stack_arc.clone()),
None,
vec![],
&hook,
"pre_prompt",
) {
report_error_new(engine_state, &err);
}
}
perf(
"pre-prompt hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for
// fire the "env_change" hook
let config = engine_state.get_config();
if let Err(error) = hook::eval_env_change_hook(
config.hooks.env_change.clone(),
engine_state,
&mut Stack::with_parent(stack_arc.clone()),
) {
report_error_new(engine_state, &error)
}
perf(
"env-change hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
let config = &engine_state.get_config().clone(); let config = &engine_state.get_config().clone();
prompt_update::update_prompt( prompt_update::update_prompt(
@ -508,6 +496,8 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
.with_completer(Box::<DefaultCompleter>::default()); .with_completer(Box::<DefaultCompleter>::default());
let shell_integration = config.shell_integration; let shell_integration = config.shell_integration;
let mut stack = Stack::unwrap_unique(stack_arc);
match input { match input {
Ok(Signal::Success(s)) => { Ok(Signal::Success(s)) => {
let hostname = System::host_name(); let hostname = System::host_name();
@ -530,7 +520,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(err) = eval_hook( if let Err(err) = eval_hook(
engine_state, engine_state,
&mut Stack::with_parent(stack_arc.clone()), &mut stack,
None, None,
vec![], vec![],
&hook, &hook,
@ -552,8 +542,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
// Actual command execution logic starts from here // Actual command execution logic starts from here
let start_time = Instant::now(); let start_time = Instant::now();
let mut stack = Stack::unwrap_unique(stack_arc);
match parse_operation(s.clone(), engine_state, &stack) { match parse_operation(s.clone(), engine_state, &stack) {
Ok(operation) => match operation { Ok(operation) => match operation {
ReplOperation::AutoCd { cwd, target, span } => { ReplOperation::AutoCd { cwd, target, span } => {
@ -598,22 +586,20 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
} }
flush_engine_state_repl_buffer(engine_state, &mut line_editor); flush_engine_state_repl_buffer(engine_state, &mut line_editor);
// put the stack back into the arc
stack_arc = Arc::new(stack);
} }
Ok(Signal::CtrlC) => { Ok(Signal::CtrlC) => {
// `Reedline` clears the line content. New prompt is shown // `Reedline` clears the line content. New prompt is shown
if shell_integration { if shell_integration {
run_ansi_sequence(&get_command_finished_marker(&stack_arc, engine_state)); run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
} }
} }
Ok(Signal::CtrlD) => { Ok(Signal::CtrlD) => {
// When exiting clear to a new line // When exiting clear to a new line
if shell_integration { if shell_integration {
run_ansi_sequence(&get_command_finished_marker(&stack_arc, engine_state)); run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
} }
println!(); println!();
return (false, Stack::unwrap_unique(stack_arc), line_editor); return (false, stack, line_editor);
} }
Err(err) => { Err(err) => {
let message = err.to_string(); let message = err.to_string();
@ -625,7 +611,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
// Alternatively only allow that expected failures let the REPL loop // Alternatively only allow that expected failures let the REPL loop
} }
if shell_integration { if shell_integration {
run_ansi_sequence(&get_command_finished_marker(&stack_arc, engine_state)); run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
} }
} }
} }
@ -647,7 +633,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
use_color, use_color,
); );
(true, Stack::unwrap_unique(stack_arc), line_editor) (true, stack, line_editor)
} }
/// ///

View File

@ -125,9 +125,9 @@ impl Stack {
unique_stack.env_vars = child.env_vars; unique_stack.env_vars = child.env_vars;
unique_stack.env_hidden = child.env_hidden; unique_stack.env_hidden = child.env_hidden;
unique_stack.active_overlays = child.active_overlays; unique_stack.active_overlays = child.active_overlays;
for item in child.parent_deletions.into_iter() { unique_stack
unique_stack.vars.remove(item); .vars
} .retain(|(var, _)| !child.parent_deletions.contains(var));
unique_stack unique_stack
} }
pub fn with_env( pub fn with_env(

View File

@ -551,3 +551,16 @@ fn err_hook_parse_error() {
assert!(actual_repl.err.contains("unsupported_config_value")); assert!(actual_repl.err.contains("unsupported_config_value"));
assert_eq!(actual_repl.out, ""); assert_eq!(actual_repl.out, "");
} }
#[test]
fn env_change_overlay() {
let inp = &[
"module test { export-env { $env.BAR = 2 } }",
&env_change_hook_code("FOO", "'overlay use test'"),
"$env.FOO = 1",
"$env.BAR",
];
let actual_repl = nu!(nu_repl_code(inp));
assert_eq!(actual_repl.out, "2");
}