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//! - [`FocusedOnDuat`] triggers when Duat gains focus.
44//! - [`UnfocusedFromDuat`] triggers when Duat loses focus.
45//! - [`WidgetOpened`] triggers when a [`Widget`] is opened.
46//! - [`WindowOpened`] triggers when a [`Window`] is created.
47//! - [`FocusedOn`] triggers when a [widget] is focused.
48//! - [`UnfocusedFrom`] triggers when a [widget] is unfocused.
49//! - [`FocusChanged`] is like [`FocusedOn`], but on [dyn `Widget`]s.
50//! - [`ModeSwitched`] triggers when you change [`Mode`].
51//! - [`KeySent`] triggers when a keys are sent.
52//! - [`KeyTyped`] triggers when keys are _typed_, not _sent_.
53//! - [`OnMouseEvent`] triggers with mouse events.
54//! - [`FormSet`] triggers whenever a [`Form`] is added/altered.
55//! - [`ColorschemeSet`] triggers whenever a [colorscheme is set].
56//! - [`MsgLogged`] triggers after logginng macros like [`debug!`].
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
176//! [`debug!`]: crate::context::debug
177//! [colorscheme is set]: crate::form::set_colorscheme
178use std::{
179    any::{Any, TypeId},
180    cell::RefCell,
181    collections::HashMap,
182    sync::Mutex,
183};
184
185use crossterm::event::MouseEventKind;
186
187pub use self::global::*;
188use crate::{
189    Ns,
190    buffer::Buffer,
191    context::Handle,
192    data::Pass,
193    form::{Form, FormId},
194    mode::{KeyEvent, KeyMod, MouseEvent, TwoPointsPlace},
195    ui::{Coord, Widget, Window},
196    utils::catch_panic,
197};
198
199/// Hook functions.
200mod global {
201    use std::sync::LazyLock;
202
203    use super::{Hookable, InnerHooks};
204    use crate::{Ns, data::Pass, hook::Callback};
205
206    static HOOKS: LazyLock<InnerHooks> = LazyLock::new(InnerHooks::default);
207
208    /// A struct used in order to specify more options for [hook]s.
209    ///
210    /// You can set three options currently:
211    ///
212    /// - [`HookBuilder::grouped`]: Groups this hook with others,
213    ///   allowing them to all be [removed] at once.
214    /// - [`HookBuilder::filter`]: Filters when this hook should be
215    ///   called, by giving a struct for which  `H` implements
216    ///   [`PartialEq`]. An example is the [`FocusedOn`] hook, which
217    ///   accepts [`Handle`]s and [`Handle`] pairs.
218    ///
219    /// [hook]: crate::hook
220    /// [`FocusedOn`]: super::FocusedOn
221    /// [`Handle`]: crate::context::Handle
222    /// [removed]: remove
223    pub struct HookBuilder<H: Hookable> {
224        callback: Option<Callback<H>>,
225        ns: Option<Ns>,
226        filter: Option<Box<dyn Fn(&H) -> bool + Send>>,
227        lateness: usize,
228    }
229
230    impl<H: Hookable> HookBuilder<H> {
231        /// Groups this hook with a [namespace].
232        ///
233        /// By adding a namespace to this group, you are then able to
234        /// remove a group of hooks by calling [`hook::remove`].
235        ///
236        /// [namespace]: Ns
237        /// [`hook::remove`]: remove
238        pub fn grouped(mut self, ns: Ns) -> Self {
239            self.ns = Some(ns);
240            self
241        }
242
243        /// Filter when this hook will be called.
244        ///
245        /// This is mostly for convenience's sake, since you _could_
246        /// just add a check inside of the callback itself.
247        ///
248        /// This is useful if for example, you want to trigger a hook
249        /// on only some specific [`Handle`], or some [`Buffer`],
250        /// things of the sort.
251        ///
252        /// [`Handle`]: crate::context::Handle
253        /// [`Buffer`]: crate::buffer::Buffer
254        pub fn filter<T: Send + 'static>(mut self, filter: T) -> Self
255        where
256            H: PartialEq<T>,
257        {
258            self.filter = Some(Box::new(move |hookable| *hookable == filter));
259            self
260        }
261
262        /// Set the "lateness" of this [`hook`].
263        ///
264        /// The lateness determines how "late" a `hook` should be
265        /// applied. For example, if a hook has a lateness of `0` and
266        /// another has a lateness of [`usize::MAX`], the latter one
267        /// will be triggered after the first one.
268        ///
269        /// An example of where this is useful is with hooks that need
270        /// to know which lines were printed. For example, if I print
271        /// the line numbers, and then in a later hook in the same
272        /// [`BufferUpdated`] trigger, an edit is made, the printed
273        /// line numbers may now be wrong.
274        ///
275        /// The general wisdom is that, in order to not break things,
276        /// if your hook relies on meta `Tag`s ([`Inlay`], [`Conceal`]
277        /// or [`Spacer`]) or [edits the `Text`] of the [`Buffer`],
278        /// you should make use of a lower lateness.
279        ///
280        /// If it only makes use of "light" `Tag`s (like [`FormTag`])
281        /// or relies on no future changes on the same `BufferUpdated`
282        /// trigger (by e.g. getting the [printed lines]), then it
283        /// should have a higher lateness.
284        ///
285        /// By default, every hook has a lateness of 100.
286        ///
287        /// [`hook`]: super
288        /// [`Tag`]: crate::text::Tag
289        /// [`BufferUpdated`]: super::BufferUpdated
290        /// [`Inlay`]: crate::text::Inlay
291        /// [`Conceal`]: crate::text::Conceal
292        /// [`Spacer`]: crate::text::Spacer
293        /// [`FormTag`]: crate::text::FormTag
294        /// [edits the `Text`]: crate::text::Text::replace_range
295        /// [printed lines]: crate::context::Handle::printed_lines
296        /// [`Buffer`]: crate::buffer::Buffer
297        pub fn lateness(mut self, lateness: usize) -> Self {
298            self.lateness = lateness;
299            self
300        }
301    }
302
303    impl<H: Hookable> Drop for HookBuilder<H> {
304        fn drop(&mut self) {
305            HOOKS.add(
306                self.callback.take().unwrap(),
307                self.ns,
308                self.filter.take(),
309                self.lateness,
310            )
311        }
312    }
313
314    /// Adds a hook, which will be called whenever the [`Hookable`] is
315    /// triggered.
316    ///
317    /// [`hook::add`] will return a [`HookBuilder`], which is a struct
318    /// that can be used to further modify the behaviour of the hook,
319    /// and will add said hook when [`Drop`]ped.
320    ///
321    /// You can call [`HookBuilder::grouped`], which will group this
322    /// hook with others of the same group, allowing for
323    /// [`hook::remove`] to remove many hooks a once.
324    ///
325    /// You can also call [`HookBuilder::filter`], which lets you
326    /// filter when your function is actually called, based on the
327    /// input arguments of `H`.
328    ///
329    /// If you want to call a function only once, check out
330    /// [`hook::add_once`], which takes an [`FnOnce`] as input, rather
331    /// than an [`FnMut`].
332    ///
333    /// [`hook::add`]: add
334    /// [`hook::add_once`]: add_once
335    /// [`hook::remove`]: remove
336    #[inline(never)]
337    pub fn add<H: Hookable>(
338        f: impl FnMut(&mut Pass, H::Input<'_>) + Send + 'static,
339    ) -> HookBuilder<H> {
340        HookBuilder {
341            callback: Some(Callback::FnMut(Box::new(f))),
342            ns: None,
343            filter: None,
344            lateness: 100,
345        }
346    }
347
348    /// Adds a hook, which will be called once when the [`Hookable`]
349    /// is triggered.
350    ///
351    /// This is in contrast to [`hook::add`], whose function is called
352    /// every time the `Hookable` is triggered, the function passed to
353    /// `add_once` will only be triggered one time.
354    ///
355    /// [`hook::add`] will return a [`HookBuilder`], which is a struct
356    /// that can be used to further modify the behaviour of the hook,
357    /// and will add said hook when [`Drop`]ped.
358    ///
359    /// You can call [`HookBuilder::grouped`], which will group this
360    /// hook with others of the same group, allowing for
361    /// [`hook::remove`] to remove many hooks a once.
362    ///
363    /// You can also call [`HookBuilder::filter`], which lets you
364    /// filter when your function is actually called, based on the
365    /// input arguments of `H`. This is especially useful with
366    /// `add_once`, since it prevents the function from being called
367    /// once with the wrong arguments (e.g., it was meant for a
368    /// specific [`Buffer`], but the trigger happened first on
369    /// another).
370    ///
371    /// [`hook::add`]: add
372    /// [`hook::remove`]: remove
373    /// [`Buffer`]: crate::buffer::Buffer
374    #[inline(never)]
375    pub fn add_once<H: Hookable>(
376        f: impl FnOnce(&mut Pass, H::Input<'_>) + Send + 'static,
377    ) -> HookBuilder<H> {
378        HookBuilder {
379            callback: Some(Callback::FnOnce(Some(Box::new(f)))),
380            ns: None,
381            filter: None,
382            lateness: 100,
383        }
384    }
385
386    /// Removes all [hooks] grouped with a [namespace]
387    ///
388    /// Any hook that was previously added with
389    /// [`HookBuilder::grouped`] with the same `Ns` will be removed.
390    /// This also includes hooks added via [`hook::add_once`]
391    ///
392    /// [hooks]: super
393    /// [`Ns`]: crate::Ns
394    /// [namespace]: Ns
395    /// [`hook::add_once`]: add_once
396    pub fn remove(ns: Ns) {
397        HOOKS.remove(ns);
398    }
399
400    /// Triggers a hooks for a [`Hookable`] struct.
401    pub fn trigger<H: Hookable>(pa: &mut Pass, hookable: H) -> H {
402        HOOKS.trigger(pa, hookable)
403    }
404
405    /// Checks if a give group exists.
406    ///
407    /// The hook can either be a string type, or a [`GroupId`].
408    ///
409    /// Returns `true` if said group was added via
410    /// [`HookBuilder::grouped`], and no [`hook::remove`]
411    /// followed these additions
412    ///
413    /// [`hook::remove`]: remove
414    pub fn group_exists(ns: Ns) -> bool {
415        HOOKS.group_exists(ns)
416    }
417}
418
419/// [`Hookable`]: Triggers when a [`Widget`] is created.
420///
421/// # Arguments
422///
423/// - The [`Handle<W>`] of said `Widget`.
424///
425/// # Aliases
426///
427/// Since every `Widget` implements the `HookAlias` trait, instead
428/// of writing this in the config crate:
429///
430/// ```rust
431/// # duat_core::doc_duat!(duat);
432/// setup_duat!(setup);
433/// use duat::prelude::*;
434///
435/// fn setup() {
436///     hook::add::<WidgetOpened<LineNumbers>>(|pa, ln| ln.write(pa).relative = true);
437/// }
438/// ```
439///
440/// You can just write this:
441///
442/// ```rust
443/// # duat_core::doc_duat!(duat);
444/// setup_duat!(setup);
445/// use duat::prelude::*;
446///
447/// fn setup() {
448///     hook::add::<WidgetOpened<LineNumbers>>(|pa, ln| ln.write(pa).relative = true);
449/// }
450/// ```
451///
452/// # Changing the layout
453///
454/// Assuming you are using `duat-term`, you could make it so every
455/// [`LineNumbers`] comes with a [`VertRule`] on the right, like this:
456///
457/// ```rust
458/// # duat_core::doc_duat!(duat);
459/// setup_duat!(setup);
460/// use duat::prelude::*;
461///
462/// fn setup() {
463///     hook::add::<WidgetOpened<LineNumbers>>(|pa, handle| {
464///         VertRule::builder().on_the_right().push_on(pa, handle);
465///     });
466/// }
467/// ```
468///
469/// Now, every time a [`LineNumbers`]s `Widget` is inserted in Duat,
470/// a [`VertRule`] will be pushed on the right of it. You could even
471/// further add a [hook] on `VertRule`, that would push further
472/// `Widget`s if you wanted to.
473///
474/// [hook]: self
475/// [direction]: crate::ui::PushSpecs
476/// [`LineNumbers`]: https://docs.rs/duat/latest/duat/widgets/struct.LineNumbers.html
477/// [`VertRule`]: https://docs.rs/duat_term/latest/duat-term/struct.VertRule.html
478pub struct WidgetOpened<W>(pub(crate) Handle<W>);
479
480impl<W: 'static> Hookable for WidgetOpened<W> {
481    type Input<'h> = &'h Handle<W>;
482
483    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
484        &self.0
485    }
486}
487
488/// An alias for [`WidgetOpened<Buffer>`].
489pub type BufferOpened = WidgetOpened<Buffer>;
490
491/// [`Hookable`]: Triggers after [`Handle::save`] or [`Handle::save_to`].
492///
493/// Only triggers if the buffer was actually updated.
494///
495/// # Arguments
496///
497/// - The [`Handle`] of said [`Buffer`]
498/// - Wether the `Buffer` will be closed (happens when calling the
499///   `wq` or `waq` commands)
500pub struct BufferSaved(pub(crate) (Handle, bool));
501
502impl Hookable for BufferSaved {
503    type Input<'h> = (&'h Handle, bool);
504
505    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
506        (&self.0.0, self.0.1)
507    }
508}
509
510/// [`Hookable`]: Triggers when a new window is opened.
511///
512/// # Arguments
513///
514/// - The [`Window`] that was created
515///
516/// One of the main reasons to use this [hook] is to push new
517/// [`Widget`]s to a `Window`. That is, you can push [outer
518/// `Widget`s] or [inner `Widget`s], just like with
519/// [`Handle`]s.
520///
521/// Here's how that works: `Window`s are divided into two main
522/// regions, the inner "[`Buffer`] region", and the outer "master
523/// region". This means that, on every `Window`, you'll have a
524/// collection of `Buffer`s in the middle, with their satellite
525/// `Widget`s, and various `Widget`s on the outer rims of the
526/// `Window`, not necessarily associated with any single `Buffer`.
527///
528/// As an example, this is how the default layout of Duat is layed
529/// out:
530///
531/// ```text
532/// ╭┄┄┬┄┄┄┄┄┄┄┄┬┄┄┬┄┄┄┄┄┄┄┄┬───────╮
533/// ┊  │        │  │        ┊       │
534/// ┊LN│        │LN│        ┊       │
535/// ┊  │ Buffer │  │ Buffer ┊       │
536/// ┊VR│        │VR│        ┊LogBook│
537/// ┊  │        │  │        ┊       │
538/// ├┄┄┴┄┄┄┄┄┄┄┄┴┄┄┴┄┄┄┄┄┄┄┄┤       │
539/// │     FooterWidgets     │       │
540/// ╰───────────────────────┴───────╯
541/// ```
542///
543/// In this configuration, you can see the delineation between the
544/// "`Buffer` region" (surrounded by dotted lines) and the "master
545/// region", where:
546///
547/// - For each [`Buffer`], we are adding a [`LineNumbers`] (LN) and a
548///   [`VertRule`] (VR) `Widget`s. Each of these is related to a
549///   specific `Buffer`, and if that `Buffer` moves around, they will
550///   follow.
551///
552/// - On the outer edges, we have a [`FooterWidgets`], which includes
553///   a [`StatusLine`], [`PromptLine`] and [`Notifications`], as well
554///   as a [`LogBook`] on the side, which is hidden by default. These
555///   [`Widget`]s are not related to any `Buffer`, and will not move
556///   around or be removed, unless directly.
557///
558/// So the distinction here is that, if you call
559/// [`Window::push_inner`], you will be pushing [`Widget`]s _around_
560/// the "`Buffer` region", but _not_ within it. If you want to push to
561/// specific [`Buffer`]s, you should look at
562/// [`Handle::push_inner_widget`] and [`Handle::push_outer_widget`].
563///
564/// On the other hand, by calling [`Window::push_outer`], you will be
565/// pushing [`Widget`]s around the "master region", so they will go on
566/// the edges of the screen.
567///
568/// [hook]: self
569/// [outer `Widget`s]: Window::push_outer
570/// [inner `Widget`s]: Window::push_inner
571/// [`LineNumbers`]: https://docs.rs/duat/duat/latest/widgets/struct.LineNumbers.html
572/// [`VertRule`]: https://docs.rs/duat/duat/latest/widgets/struct.VertRule.html
573/// [`FooterWidgets`]: https://docs.rs/duat/duat/latest/widgets/struct.FooterWidgets.html
574/// [`StatusLine`]: https://docs.rs/duat/duat/latest/widgets/struct.StatusLine.html
575/// [`PromptLine`]: https://docs.rs/duat/duat/latest/widgets/struct.PromptLine.html
576/// [`Notifications`]: https://docs.rs/duat/duat/latest/widgets/struct.Notifications.html
577/// [`LogBook`]: https://docs.rs/duat/duat/latest/widgets/struct.LogBook.html
578pub struct WindowOpened(pub(crate) Window);
579
580impl Hookable for WindowOpened {
581    type Input<'h> = &'h mut Window;
582
583    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
584        &mut self.0
585    }
586}
587
588/// [`Hookable`]: Triggers before closing a [`Buffer`].
589///
590/// # Arguments
591///
592/// - The [`Buffer`]'s [`Handle`].
593///
594/// This will be triggered whenever a `Buffer` is closed. Note that
595/// this does not count when a `Buffer` is closed in order to be
596/// reopened in a reloaded config.
597///
598/// If you want to handle that scenario, check out [`BufferUnloaded`].
599pub struct BufferClosed(pub(crate) Handle);
600
601impl Hookable for BufferClosed {
602    type Input<'h> = &'h Handle;
603
604    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
605        &self.0
606    }
607}
608
609impl PartialEq<Handle> for BufferClosed {
610    fn eq(&self, other: &Handle) -> bool {
611        self.0 == *other
612    }
613}
614
615/// [`Hookable`]: Triggers before unloading a [`Buffer`].
616///
617/// # Arguments
618///
619/// - The `Buffer`'s [`Handle`].
620///
621/// This will happen before reloading Duat, on every `Buffer` that is
622/// open at that point.
623///
624/// If you want to handle the closure of buffers, see the hook for
625/// [`BufferClosed`].
626pub struct BufferUnloaded(pub(crate) Handle);
627
628impl Hookable for BufferUnloaded {
629    type Input<'h> = &'h Handle;
630
631    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
632        &self.0
633    }
634}
635
636impl PartialEq<Handle> for BufferUnloaded {
637    fn eq(&self, other: &Handle) -> bool {
638        self.0 == *other
639    }
640}
641
642/// [`Hookable`]: Triggers when a [`Buffer`] updates.
643///
644/// This is triggered after a batch of writing calls to the `Buffer`,
645/// once per frame. This can happen after typing a key, calling a
646/// command, triggering hooks, or any other action with access to a
647/// [`Pass`], which could be used to write to the `Buffer`.
648///
649/// Think of this is as a "last pass" on the `Buffer`, right before
650/// printing, where it can be adjusted given the modifications to it,
651/// like [`Change`]s and such.
652///
653/// As an example, here's a hook that will highlight every non ascii
654/// character:
655///
656/// ```rust
657/// # duat_core::doc_duat!(duat);
658/// use duat::{
659///     prelude::*,
660///     text::{Strs, Tags},
661/// };
662///
663/// fn setup() {
664///     let ns = Ns::new();
665///     let tag = form::id_of!("non_ascii_char").to_tag(50);
666///
667///     let hl_non_ascii = move |tags: &mut Tags, strs: &Strs, range: Range<Point>| {
668///         for (b, char) in strs[range.clone()].char_indices() {
669///             let b = b + range.start.byte();
670///             if !char.is_ascii() {
671///                 tags.insert(ns, b..b + char.len_utf8(), tag);
672///             }
673///         }
674///     };
675///
676///     hook::add::<BufferOpened>(move |pa, buffer| {
677///         let _ = buffer.read(pa).moment_for(ns);
678///
679///         let mut parts = buffer.text_parts(pa);
680///         let range = Point::default()..parts.strs.end_point();
681///         hl_non_ascii(&mut parts.tags, parts.strs, range);
682///     });
683///
684///     hook::add::<BufferUpdated>(move |pa, buffer| {
685///         let moment = buffer.read(pa).moment_for(ns);
686///
687///         let mut parts = buffer.text_parts(pa);
688///         for change in moment.iter() {
689///             parts.tags.remove(ns, change.added_range());
690///             hl_non_ascii(&mut parts.tags, parts.strs, change.added_range())
691///         }
692///     });
693/// }
694/// ```
695///
696/// You can see here that I called [`Buffer::moment_for`]. This
697/// function will give, for a given [namespace], a list of all
698/// [`Change`]s that have taken place since the last call of that
699/// function with the same namespace. This is very useful for tracking
700/// alterations to the `Buffer` and acting on them accordingly.
701///
702/// In this case, I'm just taking the added range of changes and
703/// adding tags to every non ascii character in them.
704///
705/// Note that the `moment_for` function will work anywhere (that you
706/// have a [`Pass`], this is just a common situation where you'd use
707/// it.
708///
709/// # Arguments
710///
711/// - The [`Buffer`]'s [`Handle`]
712///
713/// [`Area`]: crate::ui::Area
714/// [`Buffer`]: crate::buffer::Buffer
715/// [`PrintOpts`]: crate::opts::PrintOpts
716/// [`Change`]: crate::buffer::Change
717/// [`Cursor`]: crate::mode::Cursor
718/// [`Tag`]: crate::text::Tag
719/// [`Strs`]: crate::text::Strs
720/// [`Tags`]: crate::text::Tags
721/// [`Selections`]: crate::mode::Selections
722/// [`Text`]: crate::text::Text
723/// [`Text::parts`]: crate::text::Text::parts
724/// [`Text::replace_range`]: crate::text::Text::replace_range
725/// [namespace]: crate::Ns
726pub struct BufferUpdated(pub(crate) Handle);
727
728impl Hookable for BufferUpdated {
729    type Input<'h> = &'h Handle;
730
731    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
732        &self.0
733    }
734}
735
736impl PartialEq<Handle> for BufferUpdated {
737    fn eq(&self, other: &Handle) -> bool {
738        self.0 == *other
739    }
740}
741
742/// [`Hookable`]: Triggers after a [`Buffer`] is printed.
743///
744/// The primary purpose of this `Widget` is to do cleanup on temporary
745/// changes made during a `BufferUpdated` triggering.
746///
747/// One example of this is with the default `BufferOpts`, which allow
748/// you to hightlight the current cursor line. Since this makes use of
749/// disruptive `Tag`s, it is best to do this only during the printing
750/// process, then get rid of said tags.
751///
752/// # Warning
753///
754/// Any changes done to the [`Buffer`] or [`Area`] from this hook will
755/// _not_ be checked in order for a reprint. This is to avoid
756/// repeatedly printing over and over again.
757///
758/// [`Area`]: crate::ui::Area
759pub struct BufferPrinted(pub(crate) Handle);
760
761impl Hookable for BufferPrinted {
762    type Input<'h> = &'h Handle;
763
764    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
765        &self.0
766    }
767}
768
769/// [`Hookable`]: Triggers whenever the active [`Buffer`] changes.
770///
771/// # Arguments
772///
773/// - The former `Buffer`'s [`Handle`]
774/// - The current `Buffer`'s [`Handle`]
775///
776/// [`Buffer`]: crate::buffer::Buffer
777pub struct BufferSwitched(pub(crate) (Handle, Handle));
778
779impl Hookable for BufferSwitched {
780    type Input<'h> = (&'h Handle, &'h Handle);
781
782    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
783        (&self.0.0, &self.0.1)
784    }
785}
786
787/// [`Hookable`]: Triggers when Duat opens or reloads.
788///
789/// This trigger will also happen after a few other initial setups of
790/// Duat. Notably, it takes place after all [`Buffer`]s are
791/// registered, and their [`BufferOpened`] hooks are triggered.
792///
793/// # Arguments
794///
795/// - Wether duat has just opened.
796pub struct ConfigLoaded(pub(crate) bool);
797
798impl Hookable for ConfigLoaded {
799    type Input<'h> = bool;
800
801    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
802        self.0
803    }
804}
805
806/// [`Hookable`]: Triggers when Duat closes or has to reload.
807///
808/// # Arguments
809///
810/// - Wether duat is in the process of quitting.
811pub struct ConfigUnloaded(pub(crate) bool);
812
813impl Hookable for ConfigUnloaded {
814    type Input<'h> = bool;
815
816    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
817        self.0
818    }
819}
820
821/// [`Hookable`]: Triggers when Duat is refocused.
822///
823/// # Arguments
824///
825/// There are no arguments
826pub struct FocusedOnDuat(pub(crate) ());
827
828impl Hookable for FocusedOnDuat {
829    type Input<'h> = ();
830
831    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {}
832}
833
834/// [`Hookable`]: Triggers when Duat is unfocused.
835///
836/// # Arguments
837///
838/// There are no arguments
839pub struct UnfocusedFromDuat(pub(crate) ());
840
841impl Hookable for UnfocusedFromDuat {
842    type Input<'h> = ();
843
844    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {}
845}
846
847/// [`Hookable`]: Triggers when the [`Widget`] is focused.
848///
849/// # Arguments
850///
851/// - The [`Handle<dyn Widget>`] for the unfocused `Widget`.
852/// - The [`Handle<W>`] for the newly focused `Widget`.
853///
854/// # Filters
855///
856/// This `Hookable` can be filtered in two ways
857///
858/// - By a focused [`Handle<_>`].
859/// - By a `(Handle<_>, Handle<_>)` pair.
860pub struct FocusedOn<W>(pub(crate) (Handle<dyn Widget>, Handle<W>));
861
862impl<W: 'static> Hookable for FocusedOn<W> {
863    type Input<'h> = &'h (Handle<dyn Widget>, Handle<W>);
864
865    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
866        &self.0
867    }
868}
869
870impl<W1, W2: ?Sized> PartialEq<Handle<W2>> for FocusedOn<W1> {
871    fn eq(&self, other: &Handle<W2>) -> bool {
872        self.0.1 == *other
873    }
874}
875
876impl<W1: Widget, W2: Widget + ?Sized, W3: Widget + ?Sized> PartialEq<(Handle<W2>, Handle<W3>)>
877    for FocusedOn<W1>
878{
879    fn eq(&self, other: &(Handle<W2>, Handle<W3>)) -> bool {
880        self.0.0 == other.0 && self.0.1 == other.1
881    }
882}
883
884/// [`Hookable`]: Triggers when the [`Widget`] is unfocused.
885///
886/// # Arguments
887///
888/// - The [`Handle<W>`] for the unfocused `Widget`
889/// - The [`Handle<dyn Widget>`] for the newly focused `Widget`
890pub struct UnfocusedFrom<W: Widget>(pub(crate) (Handle<W>, Handle<dyn Widget>));
891
892impl<W: Widget> Hookable for UnfocusedFrom<W> {
893    type Input<'h> = &'h (Handle<W>, Handle<dyn Widget>);
894
895    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
896        &self.0
897    }
898}
899
900impl<W1: Widget, W2: Widget + ?Sized> PartialEq<Handle<W2>> for UnfocusedFrom<W1> {
901    fn eq(&self, other: &Handle<W2>) -> bool {
902        self.0.0 == *other
903    }
904}
905
906impl<W1: Widget, W2: Widget + ?Sized, W3: Widget + ?Sized> PartialEq<(Handle<W2>, Handle<W3>)>
907    for UnfocusedFrom<W1>
908{
909    fn eq(&self, other: &(Handle<W2>, Handle<W3>)) -> bool {
910        self.0.0 == other.0 && self.0.1 == other.1
911    }
912}
913
914/// [`Hookable`]: Triggers when focus changes between two [`Widget`]s.
915///
916/// # Arguments
917///
918/// - The [`Handle<dyn Widget>`] for the unfocused `Widget`
919/// - The [`Handle<dyn Widget>`] for the newly focused `Widget`
920///
921/// This `Hookable` is triggered _before_ [`FocusedOn`] and
922/// [`UnfocusedFrom`] are triggered.
923pub struct FocusChanged(pub(crate) (Handle<dyn Widget>, Handle<dyn Widget>));
924
925impl Hookable for FocusChanged {
926    type Input<'h> = &'h (Handle<dyn Widget>, Handle<dyn Widget>);
927
928    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
929        &self.0
930    }
931}
932
933/// [`Hookable`]: Triggers when the [`Mode`] is changed.
934///
935/// # Arguments
936///
937/// - The previous mode.
938/// - The current mode.
939///
940/// [`Mode`]: crate::mode::Mode
941pub struct ModeSwitched {
942    pub(crate) old: (&'static str, Box<dyn Any + Send>),
943    pub(crate) new: (&'static str, Box<dyn Any + Send>),
944}
945
946impl Hookable for ModeSwitched {
947    type Input<'h> = ModeSwitch<'h>;
948
949    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
950        ModeSwitch {
951            old: ModeParts {
952                name: self.old.0,
953                mode: self.old.1.as_mut(),
954            },
955            new: ModeParts {
956                name: self.new.0,
957                mode: self.new.1.as_mut(),
958            },
959        }
960    }
961}
962
963/// An event representing a [`Mode`] switch, is the argument for the
964/// [`ModeSwitched`] [`hook`].
965///
966/// [`hook`]: self
967/// [`Mode`]: crate::mode::Mode
968pub struct ModeSwitch<'h> {
969    /// The name of the previous [`Mode`].
970    ///
971    /// [`Mode`]: crate::mode::Mode
972    pub old: ModeParts<'h>,
973    /// The name of the current [`Mode`].
974    ///
975    /// [`Mode`]: crate::mode::Mode
976    pub new: ModeParts<'h>,
977}
978
979/// Parts of a [`Mode`], used on the [`ModeSwitched`] [`hook`].
980///
981/// [`hook`]: self
982/// [`Mode`]: crate::mode::Mode
983pub struct ModeParts<'h> {
984    /// The name of this [`Mode`].
985    ///
986    /// [`Mode`]: crate::mode::Mode
987    pub name: &'static str,
988    pub(crate) mode: &'h mut dyn Any,
989}
990
991impl ModeParts<'_> {
992    /// Try to downcast the [`Mode`].
993    ///
994    /// Returns [`Some`] if the mode is of the correct type.
995    ///
996    /// [`Mode`]: crate::mode::Mode
997    pub fn get_as<M: 'static>(&mut self) -> Option<&mut M> {
998        self.mode.downcast_mut()
999    }
1000
1001    /// Check if the [`Mode`] is of a given type.
1002    pub fn is<M: 'static>(&self) -> bool {
1003        self.mode.is::<M>()
1004    }
1005}
1006
1007/// [`Hookable`]: Triggers whenever a [key] is sent.
1008///
1009/// [`KeyEvent`]s are "sent" when you type [unmapped] keys _or_ with
1010/// the keys that were mapped, this is in contrast with [`KeyTyped`],
1011/// which triggers when you type or when calling [`mode::type_keys`].
1012/// For example, if `jk` is mapped to `<Esc>`, [`KeyTyped`] will
1013/// trigger once for `j` and once for `k`, while [`KeySent`] will
1014/// trigger once for `<Esc>`.
1015///
1016/// # Arguments
1017///
1018/// - The sent [key].
1019///
1020/// [key]: KeyEvent
1021/// [unmapped]: crate::mode::map
1022/// [`mode::type_keys`]: crate::mode::type_keys
1023pub struct KeySent(pub(crate) KeyEvent);
1024
1025impl Hookable for KeySent {
1026    type Input<'h> = KeyEvent;
1027
1028    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1029        self.0
1030    }
1031}
1032
1033/// [`Hookable`]: Triggers whenever a [key] is typed.
1034///
1035/// [`KeyEvent`]s are "typed" when typing keys _or_ when calling the
1036/// [`mode::type_keys`] function, this is in contrast with
1037/// [`KeySent`], which triggers when you type [unmapped] keys or with
1038/// the remapped keys. For example, if `jk` is mapped to `<Esc>`,
1039/// [`KeyTyped`] will trigger once for `j` and once for `k`, while
1040/// [`KeySent`] will trigger once for `<Esc>`.
1041///
1042/// # Arguments
1043///
1044/// - The typed [key].
1045///
1046/// [key]: KeyEvent
1047/// [unmapped]: crate::mode::map
1048/// [`mode::type_keys`]: crate::mode::type_keys
1049pub struct KeyTyped(pub(crate) KeyEvent);
1050
1051impl Hookable for KeyTyped {
1052    type Input<'h> = KeyEvent;
1053
1054    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1055        self.0
1056    }
1057}
1058
1059impl PartialEq<KeyEvent> for KeyTyped {
1060    fn eq(&self, other: &KeyEvent) -> bool {
1061        self.0 == *other
1062    }
1063}
1064
1065/// [`Hookable`]: Triggers on every [`MouseEvent`].
1066///
1067/// # Arguments
1068///
1069/// - The [`Handle<W>`] under the mouse.
1070/// - The [`MouseEvent`] itself.
1071pub struct OnMouseEvent<W: ?Sized = dyn Widget> {
1072    pub(crate) handle: Handle<W>,
1073    pub(crate) points: Option<TwoPointsPlace>,
1074    pub(crate) coord: Coord,
1075    pub(crate) kind: MouseEventKind,
1076    pub(crate) modifiers: KeyMod,
1077}
1078
1079impl<W: 'static + ?Sized> Hookable for OnMouseEvent<W> {
1080    type Input<'h> = MouseEvent<'h, W>;
1081
1082    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1083        MouseEvent {
1084            handle: &self.handle,
1085            points: self.points,
1086            coord: self.coord,
1087            kind: self.kind,
1088            modifiers: self.modifiers,
1089        }
1090    }
1091}
1092
1093impl<W: 'static + ?Sized> PartialEq<MouseEvent<'_, W>> for OnMouseEvent<W> {
1094    fn eq(&self, other: &MouseEvent<W>) -> bool {
1095        self.points == other.points
1096            && self.coord == other.coord
1097            && self.kind == other.kind
1098            && self.modifiers == other.modifiers
1099    }
1100}
1101
1102impl<W: 'static + ?Sized> PartialEq<MouseEventKind> for OnMouseEvent<W> {
1103    fn eq(&self, other: &MouseEventKind) -> bool {
1104        self.kind == *other
1105    }
1106}
1107
1108impl<W: 'static + ?Sized> PartialEq<Handle<W>> for OnMouseEvent<W> {
1109    fn eq(&self, other: &Handle<W>) -> bool {
1110        self.handle.ptr_eq(other.widget())
1111    }
1112}
1113
1114/// [`Hookable`]: Triggers whenever a [`Form`] is set.
1115///
1116/// This can be a creation or alteration of a `Form`.
1117/// If the `Form` is a reference to another, the reference's
1118/// `Form` will be returned instead.
1119///
1120/// # Arguments
1121///
1122/// - The `Form`'s name.
1123/// - Its [`FormId`].
1124/// - Its new value.
1125pub struct FormSet(pub(crate) (&'static str, FormId, Form));
1126
1127impl Hookable for FormSet {
1128    type Input<'h> = (&'static str, FormId, Form);
1129
1130    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1131        (self.0.0, self.0.1, self.0.2)
1132    }
1133}
1134
1135/// [`Hookable`]: Triggers when a colorscheme is set.
1136///
1137/// # Arguments
1138///
1139/// - The name of the `ColorScheme`.
1140/// - The list of form name/form value pairs.
1141pub struct ColorschemeSet(pub(crate) (String, Vec<(String, Form)>));
1142
1143impl Hookable for ColorschemeSet {
1144    type Input<'h> = (&'h str, &'h [(String, Form)]);
1145
1146    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1147        (&self.0.0, &self.0.1)
1148    }
1149}
1150
1151/// [`Hookable`]: Triggers when messages are logged (e.g. via [`warn!`].
1152///
1153/// # Arguments
1154///
1155/// - The [`Record`] that was logged.
1156///
1157/// [`warn!`]: crate::context::warn
1158/// [`Record`]: crate::context::Record
1159pub struct MsgLogged(pub(crate) crate::context::Record);
1160
1161impl Hookable for MsgLogged {
1162    type Input<'h> = crate::context::Record;
1163
1164    fn get_input<'h>(&'h mut self, _: &mut Pass) -> Self::Input<'h> {
1165        self.0.clone()
1166    }
1167}
1168
1169/// A hookable struct, for hooks taking [`Hookable::Input`].
1170///
1171/// Through this trait, Duat allows for custom hookable structs. With
1172/// these structs, plugin creators can create their own custom hooks,
1173/// and trigger them via [`hook::trigger`].
1174///
1175/// This further empowers an end user to customize the behaviour of
1176/// Duat in the configuration crate.
1177///
1178/// [`hook::trigger`]: trigger
1179pub trait Hookable: Sized + 'static {
1180    /// The arguments that are passed to each hook.
1181    type Input<'h>;
1182    /// How to get the arguments from the [`Hookable`].
1183    ///
1184    /// This function is triggered once on every call that was added
1185    /// via [`hook::add`]. So if three hooks were added to
1186    /// [`BufferSaved`], for example, [`BufferSaved::get_input`]
1187    /// will be called three times, once before each hook.
1188    ///
1189    /// The vast majority of the time, this function is just a
1190    /// "getter", as it should take a copy, clone, or reference to the
1191    /// input type, which should be owned by the `Hookable`. For
1192    /// example, here's the definition of the [`KeyTyped`] hook:
1193    ///
1194    /// ```rust
1195    /// # duat_core::doc_duat!(duat);
1196    /// use duat::{hook::Hookable, mode::KeyEvent, prelude::*};
1197    ///
1198    /// struct KeyTyped(pub(crate) KeyEvent);
1199    ///
1200    /// impl Hookable for KeyTyped {
1201    ///     type Input<'h> = KeyEvent;
1202    ///
1203    ///     fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h> {
1204    ///         self.0
1205    ///     }
1206    /// }
1207    /// ```
1208    ///
1209    /// However, given the `&mut self` and `&mut Pass`, you can also
1210    /// do "inter hook mutations", in order to prepare for future hook
1211    /// calls. An example of this is on [`BufferUpdated`].
1212    ///
1213    /// Note that the [`Pass`] here is purely for internal use, you
1214    /// are not allowed to return something that borrows from it, as
1215    /// the borrow checker will prevent you.
1216    ///
1217    /// [`hook::add`]: add
1218    fn get_input<'h>(&'h mut self, pa: &mut Pass) -> Self::Input<'h>;
1219}
1220
1221/// Where all hooks of Duat are stored.
1222#[derive(Clone, Copy)]
1223struct InnerHooks {
1224    types: &'static Mutex<HashMap<TypeId, Box<dyn HookHolder>>>,
1225    namespaces: &'static Mutex<Vec<Ns>>,
1226}
1227
1228impl InnerHooks {
1229    /// Adds a hook for a [`Hookable`]
1230    fn add<H: Hookable>(
1231        &self,
1232        callback: Callback<H>,
1233        ns: Option<Ns>,
1234        filter: Option<Box<dyn Fn(&H) -> bool + Send + 'static>>,
1235        lateness: usize,
1236    ) {
1237        let mut map = self.types.lock().unwrap();
1238
1239        if let Some(ns) = ns {
1240            let mut namespaces = self.namespaces.lock().unwrap();
1241            if !namespaces.contains(&ns) {
1242                namespaces.push(ns)
1243            }
1244        }
1245
1246        let new_hook = Hook { callback, ns, filter, lateness };
1247
1248        if let Some(holder) = map.get(&TypeId::of::<H>()) {
1249            let hooks_of = unsafe {
1250                let ptr = (&**holder as *const dyn HookHolder).cast::<HooksOf<H>>();
1251                ptr.as_ref().unwrap()
1252            };
1253
1254            let mut hooks = hooks_of.0.borrow_mut();
1255            if let Some(i) = hooks.iter().position(|hook| hook.lateness >= lateness) {
1256                hooks.insert(i, new_hook);
1257            } else {
1258                hooks.push(new_hook)
1259            }
1260        } else {
1261            map.insert(
1262                TypeId::of::<H>(),
1263                Box::new(HooksOf::<H>(RefCell::new(vec![new_hook]))),
1264            );
1265        }
1266    }
1267
1268    /// Removes hooks with said group.
1269    fn remove(&self, ns: Ns) {
1270        self.namespaces.lock().unwrap().retain(|g| *g != ns);
1271        let map = self.types.lock().unwrap();
1272        for holder in map.iter() {
1273            holder.1.remove(ns)
1274        }
1275    }
1276
1277    /// Triggers hooks with args of the [`Hookable`].
1278    fn trigger<H: Hookable>(&self, pa: &mut Pass, mut hookable: H) -> H {
1279        let holder = self.types.lock().unwrap().remove(&TypeId::of::<H>());
1280
1281        let Some(holder) = holder else {
1282            return hookable;
1283        };
1284
1285        // SAFETY: HooksOf<H> is the only type that this HookHolder could be.
1286        let hooks_of = unsafe {
1287            let ptr = Box::into_raw(holder) as *mut HooksOf<H>;
1288            Box::from_raw(ptr)
1289        };
1290
1291        hooks_of.0.borrow_mut().retain_mut(|hook| {
1292            let has_been_removed = || matches!(hook.ns, Some(ns) if !self.group_exists(ns));
1293
1294            if has_been_removed() {
1295                return false;
1296            }
1297
1298            if let Some(filter) = hook.filter.as_ref()
1299                && !filter(&hookable)
1300            {
1301                return true;
1302            }
1303
1304            let input = hookable.get_input(pa);
1305
1306            match &mut hook.callback {
1307                Callback::FnMut(fn_mut) => {
1308                    catch_panic(|| fn_mut(pa, input));
1309                    !has_been_removed()
1310                }
1311                Callback::FnOnce(fn_once) => {
1312                    catch_panic(|| fn_once.take().unwrap()(pa, input));
1313                    false
1314                }
1315            }
1316        });
1317
1318        let mut types = self.types.lock().unwrap();
1319        if let Some(new_holder) = types.remove(&TypeId::of::<H>()) {
1320            let new_hooks_of = unsafe {
1321                let ptr = Box::into_raw(new_holder) as *mut HooksOf<H>;
1322                Box::from_raw(ptr)
1323            };
1324
1325            hooks_of
1326                .0
1327                .borrow_mut()
1328                .extend(new_hooks_of.0.borrow_mut().drain(..));
1329
1330            types.insert(TypeId::of::<H>(), unsafe {
1331                Box::from_raw(Box::into_raw(hooks_of) as *mut dyn HookHolder)
1332            });
1333        } else {
1334            types.insert(TypeId::of::<H>(), unsafe {
1335                Box::from_raw(Box::into_raw(hooks_of) as *mut dyn HookHolder)
1336            });
1337        }
1338
1339        hookable
1340    }
1341
1342    /// Checks if a hook group exists.
1343    fn group_exists(&self, ns: Ns) -> bool {
1344        self.namespaces.lock().unwrap().contains(&ns)
1345    }
1346}
1347
1348impl Default for InnerHooks {
1349    fn default() -> Self {
1350        Self {
1351            types: Box::leak(Box::default()),
1352            namespaces: Box::leak(Box::default()),
1353        }
1354    }
1355}
1356
1357unsafe impl Send for InnerHooks {}
1358unsafe impl Sync for InnerHooks {}
1359
1360/// An intermediary trait, meant for group removal.
1361trait HookHolder {
1362    /// Remove the given group from hooks of this holder
1363    fn remove(&self, ns: Ns);
1364}
1365
1366/// An intermediary struct, meant to hold the hooks of a [`Hookable`].
1367struct HooksOf<H: Hookable>(RefCell<Vec<Hook<H>>>);
1368
1369impl<H: Hookable> HookHolder for HooksOf<H> {
1370    fn remove(&self, ns: Ns) {
1371        let mut hooks = self.0.borrow_mut();
1372        hooks.retain(|hook| hook.ns.is_none_or(|other| other != ns));
1373    }
1374}
1375
1376struct Hook<H: Hookable> {
1377    callback: Callback<H>,
1378    ns: Option<Ns>,
1379    filter: Option<Box<dyn Fn(&H) -> bool + Send + 'static>>,
1380    lateness: usize,
1381}
1382
1383enum Callback<H: Hookable> {
1384    FnMut(Box<dyn FnMut(&mut Pass, <H as Hookable>::Input<'_>)>),
1385    FnOnce(Option<Box<dyn FnOnce(&mut Pass, <H as Hookable>::Input<'_>)>>),
1386}