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