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