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