{-# LANGUAGE ExistentialQuantification, ScopedTypeVariables #-} module Rasa.Internal.Listeners ( Listener , Listeners , onEveryTrigger , onEveryTrigger_ , onNextEvent , onInit , beforeEveryEvent , beforeEveryEvent_ , beforeNextEvent , beforeEveryRender , beforeEveryRender_ , beforeNextRender , onEveryRender , onEveryRender_ , onNextRender , afterEveryRender , afterEveryRender_ , afterNextRender , dispatchEvent , onExit , removeListener , matchingListeners , onBufAdded , onBufTextChanged ) where import Rasa.Internal.Action import Rasa.Internal.Events import Rasa.Internal.Editor import Rasa.Internal.Range import Control.Lens import Control.Monad import Data.Dynamic import Data.Foldable import Data.Map hiding (filter) import Unsafe.Coerce import qualified Yi.Rope as Y -- | Use this to dispatch an event of any type, any listeners which are listening for this event will be triggered -- with the provided event. Use this within an Action. dispatchEvent :: Typeable a => a -> Action () dispatchEvent evt = do listeners' <- use listeners traverse_ ($ evt) (matchingListeners listeners') -- | This is a helper which extracts and coerces a listener from its wrapper back into the proper event handler type. getListener :: forall a. Listener -> (a -> Action ()) getListener = coerce where coerce :: Listener -> (a -> Action ()) coerce (Listener _ x) = unsafeCoerce x makeListener :: forall a b. Typeable a => (a -> Action b) -> Action (ListenerId, Listener) makeListener listenerFunc = do n <- nextListenerId <<+= 1 let listenerId = ListenerId n (typeRep (Proxy :: Proxy a)) listenerFunc' = void . listenerFunc return (listenerId, Listener listenerId listenerFunc') extendListener :: Listener -> Action () -> Listener extendListener (Listener listenerId listenerFunc) act = Listener listenerId (\a -> listenerFunc a >> act) -- | This extracts all event listeners from a map of listeners which match the type of the provided event. matchingListeners :: forall a. Typeable a => Listeners -> [a -> Action ()] matchingListeners listeners' = getListener <$> (listeners'^.at (typeRep (Proxy :: Proxy a))._Just) -- | This registers an event listener, as long as the listener is well-typed similar to this: -- -- @MyEventType -> Action ()@ then it will be triggered on all dispatched events of type @MyEventType@. -- It returns an ID which may be used with 'removeListener' to cancel the listener onEveryTrigger :: forall a b. Typeable a => (a -> Action b) -> Action ListenerId onEveryTrigger listenerFunc = do (listenerId, listener) <- makeListener listenerFunc listeners %= insertWith mappend (typeRep (Proxy :: Proxy a)) [listener] return listenerId onEveryTrigger_ :: forall a b. Typeable a => (a -> Action b) -> Action () onEveryTrigger_ = void . onEveryTrigger -- | This acts as 'onEveryTrigger' but listens only for the first event of a given type. onNextEvent :: forall a b. Typeable a => (a -> Action b) -> Action () onNextEvent listenerFunc = do (listenerId, listener) <- makeListener listenerFunc let selfCancellingListener = extendListener listener (removeListener listenerId) listeners %= insertWith mappend (typeRep (Proxy :: Proxy a)) [selfCancellingListener] -- | This removes a listener and prevents it from responding to any more events. removeListener :: ListenerId -> Action () removeListener hkIdA@(ListenerId _ typ) = listeners.at typ._Just %= filter listenerMatches where listenerMatches (Listener hkIdB _) = hkIdA /= hkIdB -- | Registers an action to be performed during the Initialization phase. -- -- This phase occurs exactly ONCE when the editor starts up. -- Though arbitrary actions may be performed in the configuration block; -- it's recommended to embed such actions in the onInit event listener -- so that all event listeners are registered before anything Actions occur. onInit :: forall a. Action a -> Action () onInit action = onNextEvent (const action :: Init -> Action a) -- | Registers an action to be performed BEFORE each event phase. beforeEveryEvent :: forall a. Action a -> Action ListenerId beforeEveryEvent action = onEveryTrigger (const action :: BeforeEvent -> Action a) beforeEveryEvent_ :: forall a. Action a -> Action () beforeEveryEvent_ = void . beforeEveryEvent -- | Registers an action to be performed ONCE before only the NEXT event phase. beforeNextEvent :: forall a. Action a -> Action () beforeNextEvent action = onNextEvent (const action :: BeforeEvent -> Action a) -- | Registers an action to be performed BEFORE each render phase. -- -- This is a good spot to add information useful to the renderer -- since all actions have been performed. Only cosmetic changes should -- occur during this phase. beforeEveryRender :: forall a. Action a -> Action ListenerId beforeEveryRender action = onEveryTrigger (const action :: BeforeRender -> Action a) beforeEveryRender_ :: forall a. Action a -> Action () beforeEveryRender_ = void . beforeEveryRender -- | Registers an action to be performed ONCE before only the NEXT render phase. beforeNextRender :: forall a. Action a -> Action () beforeNextRender action = onNextEvent (const action :: BeforeRender -> Action a) -- | Registers an action to be performed during each render phase. -- -- This phase should only be used by extensions which actually render something. onEveryRender :: forall a. Action a -> Action ListenerId onEveryRender action = onEveryTrigger (const action :: OnRender -> Action a) onEveryRender_ :: forall a. Action a -> Action () onEveryRender_ = void . onEveryRender -- | Registers an action to be performed ONCE before only the NEXT render phase. -- -- This phase should only be used by extensions which actually render something. onNextRender :: forall a. Action a -> Action () onNextRender action = onNextEvent (const action :: OnRender -> Action a) -- | Registers an action to be performed AFTER each render phase. -- -- This is useful for cleaning up extension state that was registered for the -- renderer, but needs to be cleared before the next iteration. afterEveryRender :: forall a. Action a -> Action ListenerId afterEveryRender action = onEveryTrigger (const action :: AfterRender -> Action a) afterEveryRender_ :: forall a. Action a -> Action () afterEveryRender_ = void . afterEveryRender -- | Registers an action to be performed after the NEXT render phase. afterNextRender :: forall a. Action a -> Action () afterNextRender action = onNextEvent (const action :: AfterRender -> Action a) -- | Registers an action to be performed during the exit phase. -- -- This is only triggered exactly once when the editor is shutting down. It -- allows an opportunity to do clean-up, kill any processes you've started, or -- save any data before the editor terminates. onExit :: forall a. Action a -> Action () onExit action = onNextEvent (const action :: Exit -> Action a) -- | Registers an action to be performed after a new buffer is added. -- -- The supplied function will be called with a 'BufRef' to the new buffer, and the resulting 'Action' will be run. onBufAdded :: forall a. (BufRef -> Action a) -> Action ListenerId onBufAdded f = onEveryTrigger listener where listener (BufAdded bRef) = f bRef -- | This is fired every time text in a buffer changes. -- -- The range of text which was altered and the new value of that text are provided inside a 'BufTextChanged' event. onBufTextChanged :: forall a. (CrdRange -> Y.YiString -> Action a) -> Action ListenerId onBufTextChanged f = onEveryTrigger listener where listener (BufTextChanged r newText) = f r newText