yi-0.4.6: Yi/Keymap.hs
{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances, ExistentialQuantification, MultiParamTypeClasses, FunctionalDependencies, DeriveDataTypeable, StandaloneDeriving, GeneralizedNewtypeDeriving, Rank2Types #-}
-- Copyright (c) Jean-Philippe Bernardy 2007,8.
module Yi.Keymap where
import Prelude hiding (error)
import Yi.UI.Common
import qualified Yi.Editor as Editor
import Yi.Editor (EditorM, Editor, runEditor, MonadEditor(..))
import qualified Data.Map as M
import Control.Monad.Reader
import Data.Typeable
import Control.Exception
import Control.Concurrent
import Yi.Buffer
import Yi.Config
import qualified Yi.Interact as I
import Yi.Monad
import Control.Monad.State
import Yi.Event
import Yi.Process ( SubprocessInfo, SubprocessId )
import qualified Yi.UI.Common as UI
import Data.Typeable
import Control.Applicative
data Action = forall a. Show a => YiA (YiM a)
| forall a. Show a => EditorA (EditorM a)
| forall a. Show a => BufferA (BufferM a)
-- | InsertA String
-- | TextA Direction Unit Operation
deriving Typeable
emptyAction :: Action
emptyAction = BufferA (return ())
instance I.PEq Action where
equiv _ _ = False
instance Show Action where
show (YiA _) = "@Y"
show (EditorA _) = "@E"
show (BufferA _) = "@B"
type Interact ev a = I.I ev Action a
type KeymapM a = Interact Event a
type Keymap = KeymapM ()
type KeymapEndo = Keymap -> Keymap
type KeymapProcess = I.P Event Action
data Yi = Yi {yiUi :: UI,
input :: Event -> IO (), -- ^ input stream
output :: [Action] -> IO (), -- ^ output stream
yiConfig :: Config,
yiVar :: MVar YiVar -- ^ The only mutable state in the program
}
deriving Typeable
data YiVar = YiVar {yiEditor :: !Editor,
threads :: ![ThreadId], -- ^ all our threads
yiSubprocessIdSupply :: !SubprocessId,
yiSubprocesses :: !(M.Map SubprocessId SubprocessInfo)
}
-- | The type of user-bindable functions
newtype YiM a = YiM {runYiM :: ReaderT Yi IO a}
deriving (Monad, MonadReader Yi, MonadIO, Typeable, Functor)
instance MonadState Editor YiM where
get = yiEditor <$> (readRef =<< yiVar <$> ask)
put v = flip modifyRef (\x -> x {yiEditor = v}) =<< yiVar <$> ask
instance MonadEditor YiM where
askCfg = yiConfig <$> ask
withEditor f = do
r <- asks yiVar
cfg <- asks yiConfig
liftIO $ unsafeWithEditor cfg r f
-----------------------
-- Keymap basics
-- | @write a@ returns a keymap that just outputs the action @a@.
write :: (I.MonadInteract m Action ev, YiAction a x, Show x) => a -> m ()
write x = I.write (makeAction x)
withBufferMode :: BufferRef -> (forall syntax. Mode syntax -> a) -> YiM a
withBufferMode b f = withGivenBuffer b $ withModeB f
--------------------------------
-- Uninteresting glue code
withUI :: (UI -> IO a) -> YiM a
withUI = with yiUi
unsafeWithEditor :: Config -> MVar YiVar -> EditorM a -> IO a
unsafeWithEditor cfg r f = modifyMVar r $ \var -> do
let e = yiEditor var
let (e',a) = runEditor cfg f e
-- Make sure that the result of runEditor is evaluated before
-- replacing the editor state. Otherwise, we might replace e
-- with an exception-producing thunk, which makes it impossible
-- to look at or update the editor state.
-- Maybe this could also be fixed by -fno-state-hack flag?
-- TODO: can we simplify this?
e' `seq` a `seq` return (var {yiEditor = e'}, a)
withGivenBuffer :: BufferRef -> BufferM a -> YiM a
withGivenBuffer b f = withEditor (Editor.withGivenBuffer0 b f)
withBuffer :: BufferM a -> YiM a
withBuffer f = withEditor (Editor.withBuffer0 f)
readEditor :: (Editor -> a) -> YiM a
readEditor f = withEditor (gets f)
catchDynE :: Typeable exception => YiM a -> (exception -> YiM a) -> YiM a
catchDynE (YiM inner) handler
= YiM $ ReaderT (\r -> catchDyn (runReaderT inner r) (\e -> runReaderT (runYiM $ handler e) r))
catchJustE :: (Exception -> Maybe b) -- ^ Predicate to select exceptions
-> YiM a -- ^ Computation to run
-> (b -> YiM a) -- ^ Handler
-> YiM a
catchJustE p (YiM c) h = YiM $ ReaderT (\r -> catchJust p (runReaderT c r) (\b -> runReaderT (runYiM $ h b) r))
handleJustE :: (Exception -> Maybe b) -> (b -> YiM a) -> YiM a -> YiM a
handleJustE p h c = catchJustE p c h
-- | Shut down all of our threads. Should free buffers etc.
shutdown :: YiM ()
shutdown = do ts <- threads <$> readsRef yiVar
liftIO $ mapM_ killThread ts
-- -------------------------------------------
class YiAction a x | a -> x where
makeAction :: Show x => a -> Action
instance YiAction (YiM x) x where
makeAction = YiA
instance YiAction (EditorM x) x where
makeAction = EditorA
instance YiAction (BufferM x) x where
makeAction = BufferA
instance YiAction Action () where
makeAction = id