content: {-# LANGUAGE Rank2Types #-}
  module Vgrep.Event (
      -- * Event handling
      -- | An event handler is a function
      --
      -- @
      -- handleEvent :: 'Control.Monad.State.MonadState' s m => e -> s -> 'Next' (m 'Redraw')
      -- @
      --
      -- where @e@ is the event type and @s@ is the state of the handler. The
      -- 'Next' type determines the type of action to be performed. The state
      -- @s@ is passed as a parameter so the handler can decide which type of
      -- action to perform, while not being able to modify the state.
      --
      -- Event handlers form a 'Monoid' where the first handler that triggers
      -- will perform the action:
      --
      -- @
      -- (handleSome <> handleOther) event state
      -- @
      --
      -- is identical to
      --
      -- @
      -- case handleSome event state of
      --     Skip -> handleOther event state
      --     action -> action
      -- @
        Next (..)
      , Redraw (..)
      , Interrupt (..)
  
      -- * Dispatching Events
      , dispatch
      , dispatchMap
  
      -- ** Re-exports
      , module Data.Map
      ) where
  
  import           Control.Monad.IO.Class
  import           Data.Map               (Map, fromList)
  import qualified Data.Map               as M
  
  import Vgrep.Environment
  
  
  -- | The type of action to be performed on an event.
  data Next a
      = Skip
      -- ^ Do not handle the event (fall-through to other event handlers)
  
      | Continue a
      -- ^ Handle the event by performing an action
  
      | Interrupt Interrupt
      -- ^ Interrupt the application
  
  -- | The first event handler that triggers (i. e. does not return 'Skip')
  -- handles the event.
  instance Semigroup (Next a) where
      Skip        <> next       = next
      next        <> _other     = next
  
  instance Monoid (Next a) where
      mempty = Skip
  
  instance Functor Next where
      fmap f = \case Skip        -> Skip
                     Continue a  -> Continue (f a)
                     Interrupt i -> Interrupt i
  
  data Redraw
      = Redraw
      -- ^ Indicates that the state has been changed visibly, so the screen
      -- should be refreshed.
  
      | Unchanged
      -- ^ The state has not changed or the change would not be visible, so
      -- refreshing the screen is not required.
  
  instance Semigroup Redraw where
      Unchanged <> Unchanged = Unchanged
      _         <> _         = Redraw
  
  instance Monoid Redraw where
      mempty = Unchanged
  
  
  data Interrupt
      = Suspend (forall m. MonadIO m => Environment -> m ())
      -- ^ Suspend the application and run the action, e. g. invoking an
      -- external process, then resume the application.
  
      | Halt
      -- ^ Shut down.
  
  
  
  -- | If the lookup returns @'Just' action@, then handle it with
  -- @'Continue' action'@, otherwise 'Skip' this event handler.
  dispatch :: (e -> Maybe a) -> e -> Next a
  dispatch f = maybe Skip Continue . f
  
  -- | Special case of 'dispatch' where actions are looked up from a map.
  dispatchMap :: Ord e => Map e a -> e -> Next a
  dispatchMap m = dispatch (`M.lookup` m)
  