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}