diff --git a/fish-rust/src/common.rs b/fish-rust/src/common.rs index ede940074..7d6a24856 100644 --- a/fish-rust/src/common.rs +++ b/fish-rust/src/common.rs @@ -1855,38 +1855,44 @@ impl Drop for ScopeGuard { } } -/// A scoped manager to save the current value of some variable, and optionally set it to a new -/// value. When dropped, it restores the variable to its old value. -/// -/// This can be handy when there are multiple code paths to exit a block. Note that this can only be -/// used if the code does not access the captured variable again for the duration of the scope. If -/// that's not the case (the code will refuse to compile), use a [`ScopeGuard`] instance instead. -pub struct ScopedPush<'a, T> { - var: &'a mut T, - saved_value: Option, -} - -impl<'a, T> ScopedPush<'a, T> { - pub fn new(var: &'a mut T, new_value: T) -> Self { - let saved_value = mem::replace(var, new_value); - - Self { - var, - saved_value: Some(saved_value), - } +/// A scoped manager to save the current value of some variable, and set it to a new value. When +/// dropped, it restores the variable to its old value. +#[allow(clippy::type_complexity)] // Not sure how to extract the return type. +pub fn scoped_push( + mut ctx: Context, + accessor: Accessor, + new_value: T, +) -> ScopeGuard<(Context, Accessor, T), fn(&mut (Context, Accessor, T)), Context> +where + Accessor: Fn(&mut Context) -> &mut T, + T: Copy, +{ + fn restore_saved_value(data: &mut (Context, Accessor, T)) + where + Accessor: Fn(&mut Context) -> &mut T, + { + let (ref mut ctx, ref accessor, saved_value) = data; + *accessor(ctx) = *saved_value; } - - pub fn restore(&mut self) { - if let Some(saved_value) = self.saved_value.take() { - *self.var = saved_value; - } + fn view_context(data: &(Context, Accessor, T)) -> &Context + where + Accessor: Fn(&mut Context) -> &mut T, + { + &data.0 } -} - -impl<'a, T> Drop for ScopedPush<'a, T> { - fn drop(&mut self) { - self.restore() + fn view_context_mut(data: &mut (Context, Accessor, T)) -> &mut Context + where + Accessor: Fn(&mut Context) -> &mut T, + { + &mut data.0 } + let saved_value = mem::replace(accessor(&mut ctx), new_value); + ScopeGuard::with_view( + (ctx, accessor, saved_value), + view_context, + view_context_mut, + restore_saved_value, + ) } pub const fn assert_send() {} diff --git a/fish-rust/src/event.rs b/fish-rust/src/event.rs index 461804298..91c44fb7a 100644 --- a/fish-rust/src/event.rs +++ b/fish-rust/src/event.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex}; use widestring_suffix::widestrs; use crate::builtins::shared::io_streams_t; -use crate::common::{escape_string, replace_with, EscapeFlags, EscapeStringStyle, ScopeGuard}; +use crate::common::{escape_string, scoped_push, EscapeFlags, EscapeStringStyle, ScopeGuard}; use crate::ffi::{self, block_t, parser_t, signal_check_cancel, signal_handle, Repin}; use crate::flog::FLOG; use crate::signal::Signal; @@ -676,13 +676,17 @@ fn fire_internal(parser: &mut parser_t, event: &Event) { ); // Suppress fish_trace during events. - let saved_is_event = replace_with(&mut parser.libdata_pod().is_event, |old| old + 1); - let saved_suppress_fish_trace = - std::mem::replace(&mut parser.libdata_pod().suppress_fish_trace, true); - let mut parser = ScopeGuard::new(parser, |parser| { - parser.libdata_pod().is_event = saved_is_event; - parser.libdata_pod().suppress_fish_trace = saved_suppress_fish_trace; - }); + let is_event = parser.libdata_pod().is_event; + let mut parser = scoped_push( + parser, + |parser| &mut parser.libdata_pod().is_event, + is_event + 1, + ); + let mut parser = scoped_push( + &mut *parser, + |parser| &mut parser.libdata_pod().suppress_fish_trace, + true, + ); // Capture the event handlers that match this event. let fire: Vec<_> = EVENT_HANDLERS @@ -717,7 +721,7 @@ fn fire_internal(parser: &mut parser_t, event: &Event) { let saved_is_interactive = std::mem::replace(&mut parser.libdata_pod().is_interactive, false); let saved_statuses = parser.get_last_statuses().within_unique_ptr(); - let mut parser = ScopeGuard::new(&mut parser, |parser| { + let mut parser = ScopeGuard::new(&mut *parser, |parser| { parser.pin().set_last_statuses(saved_statuses); parser.libdata_pod().is_interactive = saved_is_interactive; }); @@ -731,14 +735,14 @@ fn fire_internal(parser: &mut parser_t, event: &Event) { "'" ); - let b = parser + let b = (*parser) .pin() .push_block(block_t::event_block((event as *const Event).cast()).within_unique_ptr()); - parser + (*parser) .pin() .eval_string_ffi1(&buffer.to_ffi()) .within_unique_ptr(); - parser.pin().pop_block(b); + (*parser).pin().pop_block(b); handler.fired.store(true, Ordering::Relaxed); fired_one_shot |= handler.is_one_shot();