Skip to main content

duat_core/
hook.rs

1//! Utilities for hooks in Duat.
2//!
3//! In Duat, hooks are handled through the [`Hookable`] trait. This
4//! trait contains the [`Hookable::Input`] associated type, which is
5//! what should be passed to hooks on the specific [`Hookable`]. By
6//! implementing this trait, you allow an end user to hook executions
7//! whenever said [`Hookable`] is triggered:
8//!
9//! ```rust
10//! # duat_core::doc_duat!(duat);
11//! setup_duat!(setup);
12//! use duat::prelude::*;
13//!
14//! fn setup() {
15//!     hook::add::<BufferOpened>(|pa: &mut Pass, handle: &Handle| {
16//!         let buffer = handle.write(pa);
17//!         if let Some("lisp") = buffer.filetype() {
18//!             buffer.opts.wrap_lines = true;
19//!         }
20//!     });
21//! }
22//! ```
23//!
24//! The hook above is triggered whenever a [`Buffer`] widget is
25//! opened. Like every other hook, it gives you access to the global
26//! state via the [`Pass`]. Additionally, like most hooks, it gives
27//! you a relevant argument, in this case, a [`Handle<Buffer>`], which
28//! you can modify to your liking.
29//!
30//! This is just one of many built-in [`Hookable`]s. Currently, these
31//! are the existing hooks in `duat-core`, but you can also make your
32//! own:
33//!
34//! - [`BufferOpened`] is an alias for [`WidgetOpened<Buffer>`].
35//! - [`BufferSaved`] triggers after the [`Buffer`] is written.
36//! - [`BufferClosed`] triggers on every buffer upon closing Duat.
37//! - [`BufferUnloaded`] triggers on every buffer upon reloading Duat.
38//! - [`BufferUpdated`] triggers whenever a buffer changes.
39//! - [`BufferPrinted`] triggers after a buffer has been printed.
40//! - [`BufferSwitched`] triggers when switching the active buffer.
41//! - [`ConfigLoaded`] triggers after loading the config crate.
42//! - [`ConfigUnloaded`] triggers after unloading the config crate.
43//! - [`ExitedDuat`] triggers after Duat has exited.
44//! - [`FocusedOnDuat`] triggers when Duat gains focus.
45//! - [`UnfocusedFromDuat`] triggers when Duat loses focus.
46//! - [`WidgetOpened`] triggers when a [`Widget`] is opened.
47//! - [`WindowOpened`] triggers when a [`Window`] is created.
48//! - [`FocusedOn`] triggers when a [widget] is focused.
49//! - [`UnfocusedFrom`] triggers when a [widget] is unfocused.
50//! - [`FocusChanged`] is like [`FocusedOn`], but on [dyn `Widget`]s.
51//! - [`KeySent`] triggers when a keys are sent.
52//! - [`KeySentTo`] same, but on a specific [widget].
53//! - [`KeyTyped`] triggers when keys are _typed_, not _sent_.
54//! - [`OnMouseEvent`] triggers with mouse events.
55//! - [`FormSet`] triggers whenever a [`Form`] is added/altered.
56//! - [`ModeSwitched`] triggers when you change [`Mode`].
57//!
58//! # Basic makeout
59//!
60//! When a hook is added, it can take arguments
61//!
62//! ```rust
63//! # duat_core::doc_duat!(duat);
64//! use duat::prelude::*;
65//!
66//! struct CustomHook(usize);
67//! impl Hookable for CustomHook {
68//!     type Input<'h> = usize;
69//!
70//!     fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h> {
71//!         self.0
72//!     }
73//! }
74//!
75//! fn runtime_function_that_triggers_hook(pa: &mut Pass) {
76//!     let arg = 42;
77//!     hook::trigger(pa, CustomHook(arg));
78//! }
79//! ```
80//!
81//! The above example ilustrates how hooks are implemented in Duat.
82//! You essentially pass a struct wich will hold the arguments that
83//! will be passed as input to the hooks. The [`Hookable::Input`]
84//! argument makes it so you can have more convenient parameters for
85//! hooks, like `(usize, &'h str)`, for example.
86//!
87//! Sometimes, you may need to trigger hooks "remotely", that is, in a
88//! place wher you don't have acces to a [`Pass`] (due to not being on
89//! the main thread or for some other reason), you can make use of
90//! [`context::queue`] in order to queue the hook to be executed on
91//! the main thread:
92//!
93//! ```rust
94//! # duat_core::doc_duat!(duat);
95//! # use duat::prelude::*;
96//! # struct CustomHook(usize);
97//! # impl Hookable for CustomHook {
98//! #     type Input<'h> = usize;
99//! #     fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h> { self.0 }
100//! # }
101//! fn on_a_thread_far_far_away() {
102//!     let arg = 42;
103//!     context::queue(move |pa| _ = hook::trigger(pa, CustomHook(arg)));
104//! }
105//! ```
106//!
107//! The main difference (apart from the asynchronous execution) is
108//! that [`hook::trigger`] _returns_ the hook to you, so you can
109//! retrieve its internal values. This can be useful if, for example,
110//! you wish to create a hook for configuring things:
111//!
112//! ```rust
113//! # duat_core::doc_duat!(duat);
114//! use duat::prelude::*;
115//!
116//! #[derive(Default)]
117//! struct MyConfig {
118//!     pub value_1: usize,
119//!     pub value_2: Option<f32>,
120//! }
121//!
122//! struct MyConfigCreated(MyConfig);
123//!
124//! impl Hookable for MyConfigCreated {
125//!     type Input<'h> = &'h mut MyConfig;
126//!
127//!     fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h> {
128//!         &mut self.0
129//!     }
130//! }
131//!
132//! fn create_my_config(pa: &mut Pass) -> MyConfig {
133//!     let my_config = MyConfig::default();
134//!     let MyConfigCreated(my_config) = hook::trigger(pa, MyConfigCreated(my_config));
135//!     my_config
136//! }
137//! ```
138//!
139//! This way, the user can configure `MyConfig` by calling
140//! [`hook::add`]:
141//!
142//! ```rust
143//! # duat_core::doc_duat!(duat);
144//! # #[derive(Default)]
145//! # struct MyConfig {
146//! #     pub value_1: usize,
147//! #     pub value_2: Option<f32>,
148//! # }
149//! # struct MyConfigCreated(MyConfig);
150//! # impl Hookable for MyConfigCreated {
151//! #     type Input<'h> = &'h mut MyConfig;
152//! #     fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h> { &mut self.0 }
153//! # }
154//! use duat::prelude::*;
155//! setup_duat!(setup);
156//!
157//! fn setup() {
158//!     hook::add::<MyConfigCreated>(|pa, my_config| my_config.value_1 = 3);
159//! }
160//! ```
161//!
162//! [`Buffer`]: crate::buffer::Buffer
163//! [`LineNumbers`]: https://docs.rs/duat/latest/duat/widgets/struct.LineNumbers.html
164//! [widget]: Widget
165//! [dyn `Widget`]: Widget
166//! [key]: KeyEvent
167//! [deadlocks]: https://en.wikipedia.org/wiki/Deadlock_(computer_science)
168//! [commands]: crate::cmd
169//! [`Mode`]: crate::mode::Mode
170//! [`&mut Widget`]: Widget
171//! [`context::queue`]: crate::context::queue
172//! [`hook::trigger`]: trigger
173//! [`hook::add`]: add
174//! [`SearchPerformed`]: https://docs.rs/duat/latest/duat/hooks/struct.SearchPerformed.html
175//! [`SearchUpdated`]: https://docs.rs/duat/latest/duat/hooks/struct.SearchUpdated.html
176use std::{any::TypeId, cell::RefCell, collections::HashMap, sync::Mutex};
177
178use crossterm::event::MouseEventKind;
179
180pub use self::global::*;
181use crate::{
182    buffer::Buffer,
183    context::Handle,
184    data::Pass,
185    form::{Form, FormId},
186    mode::{KeyEvent, Mode, MouseEvent},
187    ui::{Widget, Window},
188    utils::catch_panic,
189};
190
191/// Hook functions.
192mod global {
193    use std::sync::{
194        LazyLock,
195        atomic::{AtomicUsize, Ordering},
196    };
197
198    use super::{Hookable, InnerGroupId, InnerHooks};
199    use crate::{data::Pass, hook::Callback};
200
201    static HOOKS: LazyLock<InnerHooks> = LazyLock::new(InnerHooks::default);
202
203    /// A [`GroupId`] that can be used in order to remove hooks.
204    ///
205    /// When [adding grouped hooks], you can either use strings or a
206    /// [`GroupId`]. You should use strings for publicly removable
207    /// hooks, and [`GroupId`]s for privately removable hooks:
208    ///
209    /// [adding grouped hooks]: HookBuilder::grouped
210    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
211    pub struct GroupId(usize);
212
213    impl GroupId {
214        /// Returns a new [`GroupId`]
215        #[allow(clippy::new_without_default)]
216        pub fn new() -> Self {
217            static HOOK_GROUPS: AtomicUsize = AtomicUsize::new(0);
218            Self(HOOK_GROUPS.fetch_add(1, Ordering::Relaxed))
219        }
220
221        /// Remove all hooks that belong to this [`GroupId`].
222        ///
223        /// This can be used in order to have hooks remove themselves,
224        /// for example.
225        pub fn remove(self) {
226            remove(self)
227        }
228
229        /// Returns `true` if this `GroupId` has any hooks.
230        pub fn has_hooks(self) -> bool {
231            group_exists(self)
232        }
233    }
234
235    /// A struct used in order to specify more options for [hook]s.
236    ///
237    /// You can set three options currently:
238    ///
239    /// - [`HookBuilder::grouped`]: Groups this hook with others,
240    ///   allowing them to all be [removed] at once.
241    /// - [`HookBuilder::filter`]: Filters when this hook should be
242    ///   called, by giving a struct for which  `H` implements
243    ///   [`PartialEq`]. An example is the [`FocusedOn`] hook, which
244    ///   accepts [`Handle`]s and [`Handle`] pairs.
245    ///
246    /// [hook]: crate::hook
247    /// [`FocusedOn`]: super::FocusedOn
248    /// [`Handle`]: crate::context::Handle
249    /// [removed]: remove
250    pub struct HookBuilder<H: Hookable> {
251        callback: Option<Callback<H>>,
252        group: Option<InnerGroupId>,
253        filter: Option<Box<dyn Fn(&H) -> bool + Send>>,
254    }
255
256    impl<H: Hookable> HookBuilder<H> {
257        /// Add a group to this hook
258        ///
259        /// This makes it so you can call [`hook::remove`] in order to
260        /// remove this hook as well as every other hook added to the
261        /// same group.
262        ///
263        /// There are two types of group, a private [`GroupId`] and
264        /// [`impl ToString`] types, which can be removed by an end
265        /// user.
266        ///
267        /// [`impl ToString`]: ToString
268        /// [`hook::remove`]: super::remove
269        pub fn grouped(mut self, group: impl Into<InnerGroupId>) -> Self {
270            self.group = Some(group.into());
271            self
272        }
273
274        /// Filter when this hook will be called.
275        ///
276        /// This is mostly for convenience's sake, since you _could_
277        /// just add a check inside of the callback itself.
278        ///
279        /// This is useful if for example, you want to trigger a hook
280        /// on only some specific [`Handle`], or some [`Buffer`],
281        /// things of the sort.
282        ///
283        /// [`Handle`]: crate::context::Handle
284        /// [`Buffer`]: crate::buffer::Buffer
285        pub fn filter<T: Send + 'static>(mut self, filter: T) -> Self
286        where
287            H: PartialEq<T>,
288        {
289            self.filter = Some(Box::new(move |hookable| *hookable == filter));
290            self
291        }
292    }
293
294    impl<H: Hookable> Drop for HookBuilder<H> {
295        fn drop(&mut self) {
296            HOOKS.add(
297                self.callback.take().unwrap(),
298                self.group.take(),
299                self.filter.take(),
300            )
301        }
302    }
303
304    /// Adds a hook, which will be called whenever the [`Hookable`] is
305    /// triggered.
306    ///
307    /// [`hook::add`] will return a [`HookBuilder`], which is a struct
308    /// that can be used to further modify the behaviour of the hook,
309    /// and will add said hook when [`Drop`]ped.
310    ///
311    /// You can call [`HookBuilder::grouped`], which will group this
312    /// hook with others of the same group, allowing for
313    /// [`hook::remove`] to remove many hooks a once.
314    ///
315    /// You can also call [`HookBuilder::filter`], which lets you
316    /// filter when your function is actually called, based on the
317    /// input arguments of `H`.
318    ///
319    /// If you want to call a function only once, check out
320    /// [`hook::add_once`], which takes an [`FnOnce`] as input, rather
321    /// than an [`FnMut`].
322    ///
323    /// [`hook::add`]: add
324    /// [`hook::add_once`]: add_once
325    /// [`hook::remove`]: remove
326    #[inline(never)]
327    pub fn add<H: Hookable>(
328        f: impl FnMut(&mut Pass, H::Input<'_>) + Send + 'static,
329    ) -> HookBuilder<H> {
330        HookBuilder {
331            callback: Some(Callback::FnMut(Box::new(f))),
332            group: None,
333            filter: None,
334        }
335    }
336
337    /// Adds a hook, which will be called once when the [`Hookable`]
338    /// is triggered.
339    ///
340    /// This is in contrast to [`hook::add`], whose function is called
341    /// every time the `Hookable` is triggered, the function passed to
342    /// `add_once` will only be triggered one time.
343    ///
344    /// [`hook::add`] will return a [`HookBuilder`], which is a struct
345    /// that can be used to further modify the behaviour of the hook,
346    /// and will add said hook when [`Drop`]ped.
347    ///
348    /// You can call [`HookBuilder::grouped`], which will group this
349    /// hook with others of the same group, allowing for
350    /// [`hook::remove`] to remove many hooks a once.
351    ///
352    /// You can also call [`HookBuilder::filter`], which lets you
353    /// filter when your function is actually called, based on the
354    /// input arguments of `H`. This is especially useful with
355    /// `add_once`, since it prevents the function from being called
356    /// once with the wrong arguments (e.g., it was meant for a
357    /// specific [`Buffer`], but the trigger happened first on
358    /// another).
359    ///
360    /// [`hook::add`]: add
361    /// [`hook::remove`]: remove
362    /// [`Buffer`]: crate::buffer::Buffer
363    #[inline(never)]
364    pub fn add_once<H: Hookable>(
365        f: impl FnOnce(&mut Pass, H::Input<'_>) + Send + 'static,
366    ) -> HookBuilder<H> {
367        HookBuilder {
368            callback: Some(Callback::FnOnce(Some(Box::new(f)))),
369            group: None,
370            filter: None,
371        }
372    }
373
374    /// Removes a [hook] group.
375    ///
376    /// The hook can either be a string type, or a [`GroupId`].
377    ///
378    /// [hook]: Hookable
379    pub fn remove(group: impl Into<InnerGroupId>) {
380        HOOKS.remove(group.into());
381    }
382
383    /// Triggers a hooks for a [`Hookable`] struct.
384    pub fn trigger<H: Hookable>(pa: &mut Pass, hookable: H) -> H {
385        HOOKS.trigger(pa, hookable)
386    }
387
388    /// Checks if a give group exists.
389    ///
390    /// The hook can either be a string type, or a [`GroupId`].
391    ///
392    /// Returns `true` if said group was added via
393    /// [`HookBuilder::grouped`], and no [`hook::remove`]
394    /// followed these additions
395    ///
396    /// [`hook::remove`]: remove
397    pub fn group_exists(group: impl Into<InnerGroupId>) -> bool {
398        HOOKS.group_exists(group.into())
399    }
400}
401
402/// A group can either be created through a name or through a
403/// [`GroupId`].
404#[derive(Clone, Debug, PartialEq, Eq)]
405#[doc(hidden)]
406pub enum InnerGroupId {
407    Numbered(GroupId),
408    Named(String),
409}
410
411impl From<GroupId> for InnerGroupId {
412    fn from(value: GroupId) -> Self {
413        Self::Numbered(value)
414    }
415}
416
417impl<S: ToString> From<S> for InnerGroupId {
418    fn from(value: S) -> Self {
419        Self::Named(value.to_string())
420    }
421}
422
423/// [`Hookable`]: Triggers when Duat opens or reloads.
424///
425/// This trigger will also happen after a few other initial setups of
426/// Duat.
427///
428/// There are no arguments
429pub struct ConfigLoaded(pub(crate) ());
430
431impl Hookable for ConfigLoaded {
432    type Input<'h> = ();
433
434    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {}
435}
436
437/// [`Hookable`]: Triggers when Duat closes or has to reload.
438///
439/// There are no arguments
440pub struct ConfigUnloaded(pub(crate) ());
441
442impl Hookable for ConfigUnloaded {
443    type Input<'h> = ();
444
445    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {}
446}
447
448/// [`Hookable`]: Triggers when Duat closes.
449///
450/// There are no arguments
451pub struct ExitedDuat(pub(crate) ());
452
453impl Hookable for ExitedDuat {
454    type Input<'h> = ();
455
456    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {}
457}
458
459/// [`Hookable`]: Triggers when Duat is refocused.
460///
461/// # Arguments
462///
463/// There are no arguments
464pub struct FocusedOnDuat(pub(crate) ());
465
466impl Hookable for FocusedOnDuat {
467    type Input<'h> = ();
468
469    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {}
470}
471
472/// [`Hookable`]: Triggers when Duat is unfocused.
473///
474/// # Arguments
475///
476/// There are no arguments
477pub struct UnfocusedFromDuat(pub(crate) ());
478
479impl Hookable for UnfocusedFromDuat {
480    type Input<'h> = ();
481
482    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {}
483}
484
485/// [`Hookable`]: Triggers when a [`Widget`] is created.
486///
487/// # Arguments
488///
489/// - The [`Handle<W>`] of said `Widget`.
490///
491/// # Aliases
492///
493/// Since every `Widget` implements the `HookAlias` trait, instead
494/// of writing this in the config crate:
495///
496/// ```rust
497/// # duat_core::doc_duat!(duat);
498/// setup_duat!(setup);
499/// use duat::prelude::*;
500///
501/// fn setup() {
502///     hook::add::<WidgetOpened<LineNumbers>>(|pa, ln| ln.write(pa).relative = true);
503/// }
504/// ```
505///
506/// You can just write this:
507///
508/// ```rust
509/// # duat_core::doc_duat!(duat);
510/// setup_duat!(setup);
511/// use duat::prelude::*;
512///
513/// fn setup() {
514///     hook::add::<WidgetOpened<LineNumbers>>(|pa, ln| ln.write(pa).relative = true);
515/// }
516/// ```
517///
518/// # Changing the layout
519///
520/// Assuming you are using `duat-term`, you could make it so every
521/// [`LineNumbers`] comes with a [`VertRule`] on the right, like this:
522///
523/// ```rust
524/// # duat_core::doc_duat!(duat);
525/// setup_duat!(setup);
526/// use duat::prelude::*;
527///
528/// fn setup() {
529///     hook::add::<WidgetOpened<LineNumbers>>(|pa, handle| {
530///         VertRule::builder().on_the_right().push_on(pa, handle);
531///     });
532/// }
533/// ```
534///
535/// Now, every time a [`LineNumbers`]s `Widget` is inserted in Duat,
536/// a [`VertRule`] will be pushed on the right of it. You could even
537/// further add a [hook] on `VertRule`, that would push further
538/// `Widget`s if you wanted to.
539///
540/// [hook]: self
541/// [direction]: crate::ui::PushSpecs
542/// [`LineNumbers`]: https://docs.rs/duat/latest/duat/widgets/struct.LineNumbers.html
543/// [`VertRule`]: https://docs.rs/duat_term/latest/duat-term/struct.VertRule.html
544pub struct WidgetOpened<W: Widget>(pub(crate) Handle<W>);
545
546impl<W: Widget> Hookable for WidgetOpened<W> {
547    type Input<'h> = &'h Handle<W>;
548
549    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
550        &self.0
551    }
552}
553
554/// An alias for [`WidgetOpened<Buffer>`].
555pub type BufferOpened = WidgetOpened<Buffer>;
556
557/// [`Hookable`]: Triggers when a new window is opened.
558///
559/// # Arguments
560///
561/// - The [`Window`] that was created
562///
563/// One of the main reasons to use this [hook] is to push new
564/// [`Widget`]s to a `Window`. That is, you can push [outer
565/// `Widget`s] or [inner `Widget`s], just like with
566/// [`Handle`]s.
567///
568/// Here's how that works: `Window`s are divided into two main
569/// regions, the inner "[`Buffer`] region", and the outer "master
570/// region". This means that, on every `Window`, you'll have a
571/// collection of `Buffer`s in the middle, with their satellite
572/// `Widget`s, and various `Widget`s on the outer rims of the
573/// `Window`, not necessarily associated with any single `Buffer`.
574///
575/// As an example, this is how the default layout of Duat is layed
576/// out:
577///
578/// ```text
579/// ╭┄┄┬┄┄┄┄┄┄┄┄┬┄┄┬┄┄┄┄┄┄┄┄┬───────╮
580/// ┊  │        │  │        ┊       │
581/// ┊LN│        │LN│        ┊       │
582/// ┊  │ Buffer │  │ Buffer ┊       │
583/// ┊VR│        │VR│        ┊LogBook│
584/// ┊  │        │  │        ┊       │
585/// ├┄┄┴┄┄┄┄┄┄┄┄┴┄┄┴┄┄┄┄┄┄┄┄┤       │
586/// │     FooterWidgets     │       │
587/// ╰───────────────────────┴───────╯
588/// ```
589///
590/// In this configuration, you can see the delineation between the
591/// "`Buffer` region" (surrounded by dotted lines) and the "master
592/// region", where:
593///
594/// - For each [`Buffer`], we are adding a [`LineNumbers`] (LN) and a
595///   [`VertRule`] (VR) `Widget`s. Each of these is related to a
596///   specific `Buffer`, and if that `Buffer` moves around, they will
597///   follow.
598///
599/// - On the outer edges, we have a [`FooterWidgets`], which includes
600///   a [`StatusLine`], [`PromptLine`] and [`Notifications`], as well
601///   as a [`LogBook`] on the side, which is hidden by default. These
602///   [`Widget`]s are not related to any `Buffer`, and will not move
603///   around or be removed, unless directly.
604///
605/// So the distinction here is that, if you call
606/// [`Window::push_inner`], you will be pushing [`Widget`]s _around_
607/// the "`Buffer` region", but _not_ within it. If you want to push to
608/// specific [`Buffer`]s, you should look at
609/// [`Handle::push_inner_widget`] and [`Handle::push_outer_widget`].
610///
611/// On the other hand, by calling [`Window::push_outer`], you will be
612/// pushing [`Widget`]s around the "master region", so they will go on
613/// the edges of the screen.
614///
615/// [hook]: self
616/// [outer `Widget`s]: Window::push_outer
617/// [inner `Widget`s]: Window::push_inner
618/// [`LineNumbers`]: https://docs.rs/duat/duat/latest/widgets/struct.LineNumbers.html
619/// [`VertRule`]: https://docs.rs/duat/duat/latest/widgets/struct.VertRule.html
620/// [`FooterWidgets`]: https://docs.rs/duat/duat/latest/widgets/struct.FooterWidgets.html
621/// [`StatusLine`]: https://docs.rs/duat/duat/latest/widgets/struct.StatusLine.html
622/// [`PromptLine`]: https://docs.rs/duat/duat/latest/widgets/struct.PromptLine.html
623/// [`Notifications`]: https://docs.rs/duat/duat/latest/widgets/struct.Notifications.html
624/// [`LogBook`]: https://docs.rs/duat/duat/latest/widgets/struct.LogBook.html
625pub struct WindowOpened(pub(crate) Window);
626
627impl Hookable for WindowOpened {
628    type Input<'h> = &'h mut Window;
629
630    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
631        &mut self.0
632    }
633}
634
635/// [`Hookable`]: Triggers before closing a [`Buffer`].
636///
637/// # Arguments
638///
639/// - The [`Buffer`]'s [`Handle`].
640///
641/// Note that this does _not_ trigger when reloading the
642/// configuration, since that merely unloads the `Buffer`s. If you
643/// want to handle that scenarion (as well as when bufferes are being
644/// closed, check out [`BufferUnloaded`]
645///
646/// This will also trigger when exiting Duat. You can check if that is
647/// the case with [`context::will_quit`].
648///
649/// [`context::will_quit`]: crate::context::will_quit
650pub struct BufferClosed(pub(crate) Handle);
651
652impl Hookable for BufferClosed {
653    type Input<'h> = &'h Handle;
654
655    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
656        &self.0
657    }
658}
659
660impl PartialEq<Handle> for BufferClosed {
661    fn eq(&self, other: &Handle) -> bool {
662        self.0 == *other
663    }
664}
665
666/// [`Hookable`]: Triggers before unloading a [`Buffer`].
667///
668/// # Arguments
669///
670/// - The [`Buffer`]'s [`Handle`].
671///
672/// This will also trigger if the `Buffer` is closed, including while
673/// exiting Duat. You can check if that is the case with
674/// [`context::will_quit`].
675///
676/// If you want to handle situations where the `Buffer` is being
677/// closed, not just unloaded, checkout [`BufferClosed`].
678///
679/// [`context::will_quit`]: crate::context::will_quit
680pub struct BufferUnloaded(pub(crate) Handle);
681
682impl Hookable for BufferUnloaded {
683    type Input<'h> = &'h Handle;
684
685    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
686        &self.0
687    }
688}
689
690impl PartialEq<Handle> for BufferUnloaded {
691    fn eq(&self, other: &Handle) -> bool {
692        self.0 == *other
693    }
694}
695
696/// [`Hookable`]: Triggers when a [`Buffer`] updates.
697///
698/// This is triggered after a batch of writing calls to the `Buffer`,
699/// once per frame. This can happen after typing a key, calling a
700/// command, triggering hooks, or any other action with access to a
701/// [`Pass`], which could be used to write to the `Buffer`.
702///
703/// Think of this is as a "last pass" on the `Buffer`, right before
704/// printing, where it can be adjusted given the modifications to it,
705/// like [`Change`]s and such.
706///
707/// As an example, here's a hook that will highlight every non ascii
708/// character:
709///
710/// ```rust
711/// # duat_core::doc_duat!(duat);
712/// use duat::{
713///     prelude::*,
714///     text::{Strs, Tags},
715/// };
716///
717/// static TRACKER: BufferTracker = BufferTracker::new();
718///
719/// fn setup() {
720///     let tagger = Tagger::new();
721///     let tag = form::id_of!("non_ascii_char").to_tag(50);
722///
723///     let hl_non_ascii = move |tags: &mut Tags, strs: &Strs, range: Range<Point>| {
724///         for (b, char) in strs[range.clone()].char_indices() {
725///             let b = b + range.start.byte();
726///             if !char.is_ascii() {
727///                 tags.insert(tagger, b..b + char.len_utf8(), tag);
728///             }
729///         }
730///     };
731///
732///     hook::add::<BufferOpened>(move |pa, handle| {
733///         TRACKER.register_buffer(handle.write(pa));
734///
735///         let mut parts = handle.text_parts(pa);
736///         let range = Point::default()..parts.strs.end_point();
737///         hl_non_ascii(&mut parts.tags, parts.strs, range);
738///     });
739///
740///     hook::add::<BufferUpdated>(move |pa, handle| {
741///         let mut parts = TRACKER.parts(handle.write(pa)).unwrap();
742///
743///         for change in parts.changes {
744///             parts.tags.remove(tagger, change.added_range());
745///             hl_non_ascii(&mut parts.tags, parts.strs, change.added_range())
746///         }
747///     });
748/// }
749/// ```
750///
751/// The [`BufferTracker`] will keep track of each registered
752/// [`Buffer`], telling you about every new [`Change`] that took place
753/// since the last call to [`BufferTracker::parts`]. The
754/// `BufferTracker::parts` function works much like [`Text::parts`],
755/// by separating the [`Strs`], [`Tags`] and [`Selections`], letting
756/// you modify the tags, without permitting further edits to the
757/// `Text`.
758///
759/// This is a nice way to automatically keep track of the changes, and
760/// it will work even if the function isn't called frequently.
761///
762/// # Arguments
763///
764/// - The [`Buffer`]'s [`Handle`]
765///
766/// [`Area`]: crate::ui::Area
767/// [`Buffer`]: crate::buffer::Buffer
768/// [`PrintOpts`]: crate::opts::PrintOpts
769/// [`Change`]: crate::buffer::Change
770/// [`Cursor`]: crate::mode::Cursor
771/// [`Tag`]: crate::text::Tag
772/// [`Strs`]: crate::text::Strs
773/// [`Tags`]: crate::text::Tags
774/// [`Selections`]: crate::mode::Selections
775/// [`Text`]: crate::text::Text
776/// [`Text::parts`]: crate::text::Text::parts
777/// [`Text::replace_range`]: crate::text::Text::replace_range
778/// [`BufferTracker`]: crate::buffer::BufferTracker
779/// [`BufferTracker::parts`]: crate::buffer::BufferTracker::parts
780pub struct BufferUpdated(pub(crate) Handle);
781
782impl Hookable for BufferUpdated {
783    type Input<'h> = &'h Handle;
784
785    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
786        &self.0
787    }
788}
789
790impl PartialEq<Handle> for BufferUpdated {
791    fn eq(&self, other: &Handle) -> bool {
792        self.0 == *other
793    }
794}
795
796/// [`Hookable`]: Triggers after a [`Buffer`] is printed.
797///
798/// The primary purpose of this `Widget` is to do cleanup on temporary
799/// changes made during a `BufferUpdated` triggering.
800///
801/// One example of this is with the default `BufferOpts`, which allow
802/// you to hightlight the current cursor line. Since this makes use of
803/// disruptive `Tag`s, it is best to do this only during the printing
804/// process, then get rid of said tags.
805///
806/// # Warning
807///
808/// Any changes done to the [`Buffer`] or [`Area`] from this hook will
809/// _not_ be checked in order for a reprint. This is to avoid
810/// repeatedly printing over and over again.
811///
812/// [`Area`]: crate::ui::Area
813pub struct BufferPrinted(pub(crate) Handle);
814
815impl Hookable for BufferPrinted {
816    type Input<'h> = &'h Handle;
817
818    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
819        &self.0
820    }
821}
822
823/// [`Hookable`]: Triggers whenever the active [`Buffer`] changes.
824///
825/// # Arguments
826///
827/// - The former `Buffer`'s [`Handle`]
828/// - The current `Buffer`'s [`Handle`]
829///
830/// [`Buffer`]: crate::buffer::Buffer
831pub struct BufferSwitched(pub(crate) (Handle, Handle));
832
833impl Hookable for BufferSwitched {
834    type Input<'h> = (&'h Handle, &'h Handle);
835
836    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
837        (&self.0.0, &self.0.1)
838    }
839}
840
841/// [`Hookable`]: Triggers when the [`Widget`] is focused.
842///
843/// # Arguments
844///
845/// - The [`Handle<dyn Widget>`] for the unfocused `Widget`.
846/// - The [`Handle<W>`] for the newly focused `Widget`.
847///
848/// # Filters
849///
850/// This `Hookable` can be filtered in two ways
851///
852/// - By a focused [`Handle<_>`].
853/// - By a `(Handle<_>, Handle<_>)` pair.
854pub struct FocusedOn<W: Widget>(pub(crate) (Handle<dyn Widget>, Handle<W>));
855
856impl<W: Widget> Hookable for FocusedOn<W> {
857    type Input<'h> = &'h (Handle<dyn Widget>, Handle<W>);
858
859    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
860        &self.0
861    }
862}
863
864impl<W1: Widget, W2: Widget + ?Sized> PartialEq<Handle<W2>> for FocusedOn<W1> {
865    fn eq(&self, other: &Handle<W2>) -> bool {
866        self.0.1 == *other
867    }
868}
869
870impl<W1: Widget, W2: Widget + ?Sized, W3: Widget + ?Sized> PartialEq<(Handle<W2>, Handle<W3>)>
871    for FocusedOn<W1>
872{
873    fn eq(&self, other: &(Handle<W2>, Handle<W3>)) -> bool {
874        self.0.0 == other.0 && self.0.1 == other.1
875    }
876}
877
878/// [`Hookable`]: Triggers when the [`Widget`] is unfocused.
879///
880/// # Arguments
881///
882/// - The [`Handle<W>`] for the unfocused `Widget`
883/// - The [`Handle<dyn Widget>`] for the newly focused `Widget`
884pub struct UnfocusedFrom<W: Widget>(pub(crate) (Handle<W>, Handle<dyn Widget>));
885
886impl<W: Widget> Hookable for UnfocusedFrom<W> {
887    type Input<'h> = &'h (Handle<W>, Handle<dyn Widget>);
888
889    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
890        &self.0
891    }
892}
893
894impl<W1: Widget, W2: Widget + ?Sized> PartialEq<Handle<W2>> for UnfocusedFrom<W1> {
895    fn eq(&self, other: &Handle<W2>) -> bool {
896        self.0.0 == *other
897    }
898}
899
900impl<W1: Widget, W2: Widget + ?Sized, W3: Widget + ?Sized> PartialEq<(Handle<W2>, Handle<W3>)>
901    for UnfocusedFrom<W1>
902{
903    fn eq(&self, other: &(Handle<W2>, Handle<W3>)) -> bool {
904        self.0.0 == other.0 && self.0.1 == other.1
905    }
906}
907
908/// [`Hookable`]: Triggers when focus changes between two [`Widget`]s.
909///
910/// # Arguments
911///
912/// - The [`Handle<dyn Widget>`] for the unfocused `Widget`
913/// - The [`Handle<dyn Widget>`] for the newly focused `Widget`
914///
915/// This `Hookable` is triggered _before_ [`FocusedOn`] and
916/// [`UnfocusedFrom`] are triggered.
917pub struct FocusChanged(pub(crate) (Handle<dyn Widget>, Handle<dyn Widget>));
918
919impl Hookable for FocusChanged {
920    type Input<'h> = &'h (Handle<dyn Widget>, Handle<dyn Widget>);
921
922    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
923        &self.0
924    }
925}
926
927/// [`Hookable`]: Triggers when the [`Mode`] is changed.
928///
929/// # Arguments
930///
931/// - The previous mode.
932/// - The current mode.
933pub struct ModeSwitched(pub(crate) (&'static str, &'static str));
934
935impl Hookable for ModeSwitched {
936    type Input<'h> = (&'static str, &'static str);
937
938    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
939        self.0
940    }
941}
942
943/// [`Hookable`]: Triggers whenever a [key] is sent.
944///
945/// [`KeyEvent`]s are "sent" when you type [unmapped] keys _or_ with
946/// the keys that were mapped, this is in contrast with [`KeyTyped`],
947/// which triggers when you type or when calling [`mode::type_keys`].
948/// For example, if `jk` is mapped to `<Esc>`, [`KeyTyped`] will
949/// trigger once for `j` and once for `k`, while [`KeySent`] will
950/// trigger once for `<Esc>`.
951///
952/// # Arguments
953///
954/// - The sent [key].
955///
956/// [key]: KeyEvent
957/// [unmapped]: crate::mode::map
958/// [`mode::type_keys`]: crate::mode::type_keys
959pub struct KeySent(pub(crate) KeyEvent);
960
961impl Hookable for KeySent {
962    type Input<'h> = KeyEvent;
963
964    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
965        self.0
966    }
967}
968
969/// [`Hookable`]: Triggers whenever a [key] is sent to the [`Widget`].
970///
971/// # Arguments
972///
973/// - The sent [key].
974/// - An [`Handle<W>`] for the widget.
975///
976/// [key]: KeyEvent
977pub struct KeySentTo<M: Mode>(pub(crate) (KeyEvent, Handle<M::Widget>));
978
979impl<M: Mode> Hookable for KeySentTo<M> {
980    type Input<'h> = (KeyEvent, &'h Handle<M::Widget>);
981
982    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
983        (self.0.0, &self.0.1)
984    }
985}
986
987/// [`Hookable`]: Triggers whenever a [key] is typed.
988///
989/// [`KeyEvent`]s are "typed" when typing keys _or_ when calling the
990/// [`mode::type_keys`] function, this is in contrast with
991/// [`KeySent`], which triggers when you type [unmapped] keys or with
992/// the remapped keys. For example, if `jk` is mapped to `<Esc>`,
993/// [`KeyTyped`] will trigger once for `j` and once for `k`, while
994/// [`KeySent`] will trigger once for `<Esc>`.
995///
996/// # Arguments
997///
998/// - The typed [key].
999///
1000/// [key]: KeyEvent
1001/// [unmapped]: crate::mode::map
1002/// [`mode::type_keys`]: crate::mode::type_keys
1003pub struct KeyTyped(pub(crate) KeyEvent);
1004
1005impl Hookable for KeyTyped {
1006    type Input<'h> = KeyEvent;
1007
1008    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1009        self.0
1010    }
1011}
1012
1013impl PartialEq<KeyEvent> for KeyTyped {
1014    fn eq(&self, other: &KeyEvent) -> bool {
1015        self.0 == *other
1016    }
1017}
1018
1019/// [`Hookable`]: Triggers on every [`MouseEvent`].
1020///
1021/// # Arguments
1022///
1023/// - The [`Handle<dyn Widget>`] under the mouse.
1024/// - The [`MouseEvent`] itself.
1025pub struct OnMouseEvent(pub(crate) (Handle<dyn Widget>, MouseEvent));
1026
1027impl Hookable for OnMouseEvent {
1028    type Input<'h> = (&'h Handle<dyn Widget>, MouseEvent);
1029
1030    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1031        (&self.0.0, self.0.1)
1032    }
1033}
1034
1035impl PartialEq<MouseEvent> for OnMouseEvent {
1036    fn eq(&self, other: &MouseEvent) -> bool {
1037        self.0.1 == *other
1038    }
1039}
1040
1041impl PartialEq<MouseEventKind> for OnMouseEvent {
1042    fn eq(&self, other: &MouseEventKind) -> bool {
1043        self.0.1.kind == *other
1044    }
1045}
1046
1047impl<W: Widget> PartialEq<Handle<W>> for OnMouseEvent {
1048    fn eq(&self, other: &Handle<W>) -> bool {
1049        self.0.0.ptr_eq(other.widget())
1050    }
1051}
1052
1053/// [`Hookable`]: Triggers whenever a [`Form`] is set.
1054///
1055/// This can be a creation or alteration of a `Form`.
1056/// If the `Form` is a reference to another, the reference's
1057/// `Form` will be returned instead.
1058///
1059/// # Arguments
1060///
1061/// - The `Form`'s name.
1062/// - Its [`FormId`].
1063/// - Its new value.
1064pub struct FormSet(pub(crate) (String, FormId, Form));
1065
1066impl Hookable for FormSet {
1067    type Input<'h> = (&'h str, FormId, Form);
1068
1069    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1070        (&self.0.0, self.0.1, self.0.2)
1071    }
1072}
1073
1074/// [`Hookable`]: Triggers when a colorscheme is set.
1075///
1076/// Since [`Form`]s are set asynchronously, this may happen before the
1077/// `ColorScheme` is done with its changes.
1078///
1079/// # Arguments
1080///
1081/// - The name of the `ColorScheme`.
1082/// - The list of form name/form value pairs.
1083pub struct ColorSchemeSet(pub(crate) (String, Vec<(String, Form)>));
1084
1085impl Hookable for ColorSchemeSet {
1086    type Input<'h> = (&'h str, &'h [(String, Form)]);
1087
1088    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1089        (&self.0.0, &self.0.1)
1090    }
1091}
1092
1093/// [`Hookable`]: Triggers after [`Handle::save`] or [`Handle::save_to`].
1094///
1095/// Only triggers if the buffer was actually updated.
1096///
1097/// # Arguments
1098///
1099/// - The [`Handle`] of said [`Buffer`]
1100/// - Wether the `Buffer` will be closed (happens when calling the
1101///   `wq` or `waq` commands)
1102pub struct BufferSaved(pub(crate) (Handle, bool));
1103
1104impl Hookable for BufferSaved {
1105    type Input<'h> = (&'h Handle, bool);
1106
1107    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1108        (&self.0.0, self.0.1)
1109    }
1110}
1111
1112/// A hookable struct, for hooks taking [`Hookable::Input`].
1113///
1114/// Through this trait, Duat allows for custom hookable structs. With
1115/// these structs, plugin creators can create their own custom hooks,
1116/// and trigger them via [`hook::trigger`].
1117///
1118/// This further empowers an end user to customize the behaviour of
1119/// Duat in the configuration crate.
1120///
1121/// [`hook::trigger`]: trigger
1122pub trait Hookable: Sized + 'static {
1123    /// The arguments that are passed to each hook.
1124    type Input<'h>;
1125    /// How to get the arguments from the [`Hookable`].
1126    ///
1127    /// This function is triggered once on every call that was added
1128    /// via [`hook::add`]. So if three hooks were added to
1129    /// [`BufferSaved`], for example, [`BufferSaved::get_input`]
1130    /// will be called three times, once before each hook.
1131    ///
1132    /// The vast majority of the time, this function is just a
1133    /// "getter", as it should take a copy, clone, or reference to the
1134    /// input type, which should be owned by the `Hookable`. For
1135    /// example, here's the definition of the [`KeyTyped`] hook:
1136    ///
1137    /// ```rust
1138    /// # duat_core::doc_duat!(duat);
1139    /// use duat::{hook::Hookable, mode::KeyEvent, prelude::*};
1140    ///
1141    /// struct KeyTyped(pub(crate) KeyEvent);
1142    ///
1143    /// impl Hookable for KeyTyped {
1144    ///     type Input<'h> = KeyEvent;
1145    ///
1146    ///     fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h> {
1147    ///         self.0
1148    ///     }
1149    /// }
1150    /// ```
1151    ///
1152    /// However, given the `&mut self` and `&mut Pass`, you can also
1153    /// do "inter hook mutations", in order to prepare for future hook
1154    /// calls. An example of this is on [`BufferUpdated`].
1155    ///
1156    /// Note that the [`Pass`] here is purely for internal use, you
1157    /// are not allowed to return something that borrows from it, as
1158    /// the borrow checker will prevent you.
1159    ///
1160    /// [`hook::add`]: add
1161    fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h>;
1162}
1163
1164/// Where all hooks of Duat are stored.
1165#[derive(Clone, Copy)]
1166struct InnerHooks {
1167    types: &'static Mutex<HashMap<TypeId, Box<dyn HookHolder>>>,
1168    groups: &'static Mutex<Vec<InnerGroupId>>,
1169}
1170
1171impl InnerHooks {
1172    /// Adds a hook for a [`Hookable`]
1173    fn add<H: Hookable>(
1174        &self,
1175        callback: Callback<H>,
1176        group: Option<InnerGroupId>,
1177        filter: Option<Box<dyn Fn(&H) -> bool + Send + 'static>>,
1178    ) {
1179        let mut map = self.types.lock().unwrap();
1180
1181        if let Some(group_id) = group.clone() {
1182            let mut groups = self.groups.lock().unwrap();
1183            if !groups.contains(&group_id) {
1184                groups.push(group_id)
1185            }
1186        }
1187
1188        if let Some(holder) = map.get(&TypeId::of::<H>()) {
1189            let hooks_of = unsafe {
1190                let ptr = (&**holder as *const dyn HookHolder).cast::<HooksOf<H>>();
1191                ptr.as_ref().unwrap()
1192            };
1193
1194            let mut hooks = hooks_of.0.borrow_mut();
1195            hooks.push(Hook { callback, group, filter });
1196        } else {
1197            let hooks_of = HooksOf::<H>(RefCell::new(vec![Hook { callback, group, filter }]));
1198
1199            map.insert(TypeId::of::<H>(), Box::new(hooks_of));
1200        }
1201    }
1202
1203    /// Removes hooks with said group.
1204    fn remove(&self, group_id: InnerGroupId) {
1205        self.groups.lock().unwrap().retain(|g| *g != group_id);
1206        let map = self.types.lock().unwrap();
1207        for holder in map.iter() {
1208            holder.1.remove(&group_id)
1209        }
1210    }
1211
1212    /// Triggers hooks with args of the [`Hookable`].
1213    fn trigger<H: Hookable>(&self, pa: &mut Pass, mut hookable: H) -> H {
1214        let holder = self.types.lock().unwrap().remove(&TypeId::of::<H>());
1215
1216        let Some(holder) = holder else {
1217            return hookable;
1218        };
1219
1220        // SAFETY: HooksOf<H> is the only type that this HookHolder could be.
1221        let hooks_of = unsafe {
1222            let ptr = Box::into_raw(holder) as *mut HooksOf<H>;
1223            Box::from_raw(ptr)
1224        };
1225
1226        hooks_of.0.borrow_mut().retain_mut(|hook| {
1227            if let Some(filter) = hook.filter.as_ref()
1228                && !filter(&hookable)
1229            {
1230                return true;
1231            }
1232
1233            let input = hookable.get_input(pa);
1234
1235            match &mut hook.callback {
1236                Callback::FnMut(fn_mut) => {
1237                    catch_panic(|| fn_mut(pa, input));
1238                    true
1239                }
1240                Callback::FnOnce(fn_once) => {
1241                    catch_panic(|| fn_once.take().unwrap()(pa, input));
1242                    false
1243                }
1244            }
1245        });
1246
1247        let mut types = self.types.lock().unwrap();
1248        if let Some(new_holder) = types.remove(&TypeId::of::<H>()) {
1249            let new_hooks_of = unsafe {
1250                let ptr = Box::into_raw(new_holder) as *mut HooksOf<H>;
1251                Box::from_raw(ptr)
1252            };
1253
1254            hooks_of
1255                .0
1256                .borrow_mut()
1257                .extend(new_hooks_of.0.borrow_mut().drain(..));
1258
1259            types.insert(TypeId::of::<H>(), unsafe {
1260                Box::from_raw(Box::into_raw(hooks_of) as *mut dyn HookHolder)
1261            });
1262        } else {
1263            types.insert(TypeId::of::<H>(), unsafe {
1264                Box::from_raw(Box::into_raw(hooks_of) as *mut dyn HookHolder)
1265            });
1266        }
1267
1268        hookable
1269    }
1270
1271    /// Checks if a hook group exists.
1272    fn group_exists(&self, group: InnerGroupId) -> bool {
1273        self.groups.lock().unwrap().contains(&group)
1274    }
1275}
1276
1277impl Default for InnerHooks {
1278    fn default() -> Self {
1279        Self {
1280            types: Box::leak(Box::default()),
1281            groups: Box::leak(Box::default()),
1282        }
1283    }
1284}
1285
1286unsafe impl Send for InnerHooks {}
1287unsafe impl Sync for InnerHooks {}
1288
1289/// An intermediary trait, meant for group removal.
1290trait HookHolder {
1291    /// Remove the given group from hooks of this holder
1292    fn remove(&self, group_id: &InnerGroupId);
1293}
1294
1295/// An intermediary struct, meant to hold the hooks of a [`Hookable`].
1296struct HooksOf<H: Hookable>(RefCell<Vec<Hook<H>>>);
1297
1298impl<H: Hookable> HookHolder for HooksOf<H> {
1299    fn remove(&self, group_id: &InnerGroupId) {
1300        let mut hooks = self.0.borrow_mut();
1301        hooks.retain(|hook| hook.group.as_ref().is_none_or(|g| g != group_id));
1302    }
1303}
1304
1305struct Hook<H: Hookable> {
1306    callback: Callback<H>,
1307    group: Option<InnerGroupId>,
1308    filter: Option<Box<dyn Fn(&H) -> bool + Send + 'static>>,
1309}
1310
1311enum Callback<H: Hookable> {
1312    FnMut(Box<dyn FnMut(&mut Pass, <H as Hookable>::Input<'_>)>),
1313    FnOnce(Option<Box<dyn FnOnce(&mut Pass, <H as Hookable>::Input<'_>)>>),
1314}