{-# language DeriveFunctor , MultiParamTypeClasses , FlexibleInstances , GeneralizedNewtypeDeriving , Rank2Types , ExistentialQuantification , TemplateHaskell #-} module Rasa.Internal.Action ( Action(..) , runAction , evalAction , execAction , ActionState , mkActionState , Listener(..) , ListenerId(..) , Listeners , listeners , nextListenerId , actionQueue ) where import Rasa.Internal.Editor import Rasa.Internal.Extensions import Control.Lens import Control.Monad.Free import Control.Monad.State import Data.Default import Data.Map import Data.Typeable import Pipes.Concurrent hiding (Buffer) -- | A wrapper around event listeners so they can be stored in 'Listeners'. data Listener = forall a. Listener ListenerId (a -> Action ()) -- | An opaque reverence to a specific registered event-listener. -- A ListenerId is used only to remove listeners later with 'Rasa.Internal.Listeners.removeListener'. data ListenerId = ListenerId Int TypeRep instance Eq ListenerId where ListenerId a _ == ListenerId b _ = a == b -- | A map of Event types to a list of listeners for that event type Listeners = Map TypeRep [Listener] -- | Free Monad Actions for Action data ActionF state next = LiftState (state -> (next, state)) | LiftIO (IO next) deriving (Functor) -- | This is a monad for performing actions against the editor. -- You can register Actions to be run in response to events using 'Rasa.Internal.Listeners.onEveryTrigger' -- -- Within an Action you can: -- -- * Use liftIO for IO -- * Access/edit extensions that are stored globally, see 'ext' -- * Embed any 'Action's exported other extensions -- * Embed buffer actions using 'Rasa.Internal.Actions.bufDo' or 'Rasa.Internal.Actions.buffersDo' -- * Add\/Edit\/Focus buffers and a few other Editor-level things, see the "Rasa.Internal.Actions" module. newtype Action a = Action { getAction :: Free (ActionF ActionState) a } deriving (Functor, Applicative, Monad) -- | This contains all data representing the editor's state. It acts as the state object for an 'Action data ActionState = ActionState { _ed :: Editor , _listeners :: Listeners , _nextListenerId :: Int , _actionQueue :: Output (Action ()) } makeLenses ''ActionState instance Show ActionState where show as = show (_ed as) mkActionState :: Output (Action ()) -> ActionState mkActionState out = ActionState { _ed=def , _listeners=def , _nextListenerId=0 , _actionQueue=out } instance HasEditor ActionState where editor = ed instance HasExts ActionState where exts = ed.exts -- | Embeds a ActionF type into the Action Monad liftActionF :: ActionF ActionState a -> Action a liftActionF = Action . liftF -- | Allows running state actions over ActionState; used to lift mtl state functions liftState :: (ActionState -> (a, ActionState)) -> Action a liftState = liftActionF . LiftState -- | Allows running IO in BufAction. liftFIO :: IO r -> Action r liftFIO = liftActionF . LiftIO instance (MonadState ActionState) Action where state = liftState instance MonadIO Action where liftIO = liftFIO -- | Runs an Action into an IO runAction :: ActionState -> Action a -> IO (a, ActionState) runAction actionState (Action actionF) = actionInterpreter actionState actionF -- | Evals an Action into an IO evalAction :: ActionState -> Action a -> IO a evalAction actionState action = fst <$> runAction actionState action -- | Execs an Action into an IO execAction :: ActionState -> Action a -> IO ActionState execAction actionState action = snd <$> runAction actionState action -- | Interpret the Free Monad; in this case it interprets it down to an IO actionInterpreter :: ActionState -> Free (ActionF ActionState) r -> IO (r, ActionState) actionInterpreter actionState (Free actionF) = case actionF of (LiftState stateFunc) -> let (next, newState) = stateFunc actionState in actionInterpreter newState next (LiftIO ioNext) -> ioNext >>= actionInterpreter actionState actionInterpreter actionState (Pure res) = return (res, actionState)