packages feed

rasa 0.1.5 → 0.1.6

raw patch · 10 files changed

+243/−165 lines, 10 files

Files

README.md view
@@ -8,21 +8,22 @@  ![Rasa Editor](https://github.com/ChrisPenner/rasa/blob/master/docs/rasa.png "Rasa Editor") -A Rasa editing session with multiple cursors.+A Rasa editing session with multiple cursors & viewports.  Documentation ------------- You can find hackage documentation for rasa and some extensions here:  - [rasa](https://hackage.haskell.org/package/rasa)+- [rasa-ext-slate](https://hackage.haskell.org/package/rasa-ext-slate)+- [rasa-ext-views](https://hackage.haskell.org/package/rasa-ext-views)+- [rasa-ext-vim](https://hackage.haskell.org/package/rasa-ext-vim) - [rasa-ext-cmd](https://hackage.haskell.org/package/rasa-ext-cmd) - [rasa-ext-cursors](https://hackage.haskell.org/package/rasa-ext-cursors) - [rasa-ext-files](https://hackage.haskell.org/package/rasa-ext-files) - [rasa-ext-logger](https://hackage.haskell.org/package/rasa-ext-logger)-- [rasa-ext-slate](https://hackage.haskell.org/package/rasa-ext-slate) - [rasa-ext-status-bar](https://hackage.haskell.org/package/rasa-ext-status-bar) - [rasa-ext-style](https://hackage.haskell.org/package/rasa-ext-style)-- [rasa-ext-vim](https://hackage.haskell.org/package/rasa-ext-vim)  What people are saying ----------------------@@ -77,20 +78,15 @@ Getting started --------------- -Here's a great guide on building a copy-paste extension from scratch! I definitely-recommend checking it out!- ### Configuring Rasa -Rasa is designed to be easy to configure, both when adding extensions provided+Rasa is designed to be easy to configure and script, both when adding extensions provided by the community, and when writing your own user-scripts.  Rasa is written in Haskell, and the configuration is done in the Haskell language, don't let that scare you though, you can script Rasa and add extensions without knowing much haskell! -Check out the [Example Config](https://github.com/ChrisPenner/rasa/tree/master/rasa-example-config) to get an idea of where to go next!- ### [Building Your First Extension](https://github.com/ChrisPenner/rasa/blob/master/docs/Building-An-Extension.md)  \^ That guide will walk you through installation and getting running! Once@@ -118,6 +114,7 @@  - Loading and saving files  - Key bindings+- Listening for keyboard events - Multiple cursors - Rendering the editor to the terminal @@ -135,13 +132,11 @@ ### Cons  -   Module cross-dependencies makes the community infrastructure more fragile;-    We'll likely have to develop a solution to this as a community as time-    goes on.+    We'll likely develop a solution to this as a community as time goes on. -   Fragmentation; Not having a single implementation for a given feature means     extensions that depend on a feature have to pick a specific implementation     to augment. Over time data-structures and types will be standardized into     Rasa's core to help alleviate this.-  Core Features -------------
rasa.cabal view
@@ -1,5 +1,5 @@ name: rasa-version: 0.1.5+version: 0.1.6 cabal-version: >=1.10 build-type: Simple license: MIT
src/Rasa.hs view
@@ -25,13 +25,13 @@ -- >   vim -- >   slate -rasa :: Scheduler () -> IO ()-rasa scheduler =-  evalAction def hooks $ do+rasa :: Action () -> IO ()+rasa initilize =+  evalAction def $ do+    initilize     dispatchEvent Init     eventLoop     dispatchEvent Exit-    where hooks = getHooks scheduler  -- | This is the main event loop, it runs recursively forever until something -- sets 'Rasa.Editor.exiting'. It runs the pre-event hooks, then checks if any
src/Rasa/Ext.hs view
@@ -54,9 +54,11 @@   , prevBufRef   , getBufRefs   , getBuffers+  , getBuffer    -- * Working with Buffers   , BufAction+  , liftAction   , bufDo   , bufDo_   , buffersDo@@ -107,6 +109,7 @@     -- * Accessing/Editing Context   , Buffer+  , HasBuffer   , BufRef   , HasEditor   , text@@ -119,19 +122,20 @@   , Mod(..)    -- * Dealing with events-  , Scheduler   , Hooks   , Hook+  , HookId   , dispatchEvent   , eventListener+  , removeListener   , eventProvider    -- * Built-in Event Hooks-  , onInit   , beforeEvent   , beforeRender   , onRender   , afterRender+  , onInit   , onExit   , onBufAdded 
src/Rasa/Internal/Action.hs view
@@ -1,11 +1,11 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, ExistentialQuantification, TemplateHaskell, StandaloneDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, ExistentialQuantification, TemplateHaskell, StandaloneDeriving,+   MultiParamTypeClasses, FlexibleInstances #-}  module Rasa.Internal.Action where  import Control.Lens import Control.Concurrent.Async import Control.Monad.State-import Control.Monad.Reader import Data.Dynamic import Data.Map import Data.Default@@ -15,8 +15,13 @@   -- | A wrapper around event listeners so they can be stored in 'Hooks'.-data Hook = forall a. Hook a+data Hook = forall a. Hook HookId a+data HookId =+  HookId Int TypeRep +instance Eq HookId where+  HookId a _ == HookId b _ = a == b+ -- | A map of Event types to a list of listeners for that event type Hooks = Map TypeRep [Hook] @@ -28,29 +33,38 @@ --      * 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.Ext.Directive.bufDo' and 'Rasa.Internal.Ext.Directive.focusDo'+--      * Embed buffer actions using 'Rasa.Internal.Ext.Directive.bufDo' or 'Rasa.Internal.Ext.Directive.buffersDo' --      * Add\/Edit\/Focus buffers and a few other Editor-level things, see the 'Rasa.Internal.Ext.Directive' module.  newtype Action a = Action-  { runAct :: StateT ActionState (ReaderT Hooks IO) a-  } deriving (Functor, Applicative, Monad, MonadState ActionState, MonadReader Hooks, MonadIO)-+  { runAct :: StateT ActionState IO a+  } deriving (Functor, Applicative, Monad, MonadState ActionState, MonadIO) --- | Unwrap and execute an Action (returning the editor state)-execAction :: ActionState -> Hooks -> Action () -> IO ActionState-execAction actionState hooks action = flip runReaderT hooks . execStateT (runAct action) $ actionState+-- | Execute an Action (returning the editor state)+execAction :: ActionState ->  Action () -> IO ActionState+execAction actionState action = execStateT (runAct action) actionState --- | Unwrap and evaluate an Action (returning the value)-evalAction :: ActionState -> Hooks -> Action a -> IO a-evalAction actionState hooks action  = flip runReaderT hooks $ evalStateT (runAct action) actionState+-- | Evaluate an Action (returning the value)+evalAction :: ActionState -> Action a -> IO a+evalAction actionState (Action action)  = evalStateT action actionState  type AsyncAction = Async (Action ())+-- | This contains all data representing the editor's state. It acts as the state object for an 'Action data ActionState = ActionState   { _ed :: Editor   , _asyncs :: [AsyncAction]+  , _hooks :: Hooks+  , _nextHook :: Int   }-makeClassy ''ActionState+makeLenses ''ActionState +-- | Allows polymorphic lenses which need to access something in ActionState+class HasActionState a where+  actionState :: Lens' a ActionState++instance HasActionState ActionState where+  actionState = lens id (flip const)+ instance HasEditor ActionState where   editor = ed @@ -58,35 +72,64 @@   def = ActionState     { _ed=def     , _asyncs=def+    , _hooks=def+    , _nextHook=0     } +-- | Contains all data about the editor; as well as a buffer which is in 'focus'.+-- We keep the full ActionState here too so that 'Action's may be lifted inside a 'BufAction'+data BufActionState = BufActionState+  { _actState :: ActionState+  , _buf :: Buffer+  }+makeLenses ''BufActionState+ instance Show ActionState where   show as = show (_ed as)  -- | This is a monad-transformer stack for performing actions on a specific buffer.--- You register BufActions to be run by embedding them in a scheduled 'Action' via 'bufferDo' or 'focusDo'+-- You run 'BufAction's by embedding them in a 'Action' via 'bufferDo' or 'buffersDo' -- -- Within a BufAction you can: --+--      * Use 'liftAction' to run an 'Action'; It is your responsibility to ensure that any nested 'Action's don't edit+  --      the Buffer which the current 'BufAction' is editing; behaviour is undefined if this occurs. --      * Use liftIO for IO---      * Access/edit buffer extensions; see 'bufExt'---      * Embed and sequence any 'BufAction's from other extensions --      * Access/Edit the buffer's 'text'---+--      * Access/edit buffer extensions; see 'bufExt'+--      * Embed and sequence 'BufAction's from other extensions+ newtype BufAction a = BufAction-  { getBufAction::StateT Buffer (ReaderT Hooks IO) a-  } deriving (Functor, Applicative, Monad, MonadState Buffer, MonadReader Hooks, MonadIO)+  { runBufAct :: StateT BufActionState IO a+  } deriving (Functor, Applicative, Monad, MonadState BufActionState, MonadIO) +instance HasBuffer BufActionState where+  buffer = buf++instance HasActionState BufActionState where+  actionState = actState+ -- | This lifts up a bufAction into an Action which performs the 'BufAction' -- over the referenced buffer and returns the result (if the buffer existed) liftBuf :: BufAction a -> BufRef -> Action (Maybe a)-liftBuf bufAct (BufRef bufRef) = do-  mBuf <- use (buffers.at bufRef)+liftBuf (BufAction bufAct) (BufRef bufInd) = do+  actionState' <- get+  mBuf <- getBuffer   case mBuf of     Nothing -> return Nothing-    Just buf -> do-      hooks <- ask-      (val, newBuf) <- liftIO $ flip runReaderT hooks . flip runStateT buf . getBufAction $ bufAct-      buffers.at bufRef ?= newBuf-      return . Just $ val+    Just b -> do+      let bufActSt = BufActionState actionState' b+      (val, newBufActState) <- liftIO $ runStateT bufAct bufActSt+      put (newBufActState^.actionState)+      setBuffer (newBufActState^.buf)+      return (Just val)+  where+    getBuffer = use (buffers.at bufInd)+    setBuffer b = buffers.at bufInd ?= b +-- | This lifts up an 'Action' to be run inside a 'BufAction'+--+-- it is your responsibility to ensure that any nested 'Action's don't edit+-- the Buffer which the current 'BufAction' is editing; behaviour is undefined if this occurs.+liftAction :: Action a -> BufAction a+liftAction (Action action) = BufAction $ zoom actionState action
src/Rasa/Internal/Buffer.hs view
@@ -1,14 +1,20 @@-{-# LANGUAGE Rank2Types, TemplateHaskell, OverloadedStrings, ExistentialQuantification, ScopedTypeVariables,-   GeneralizedNewtypeDeriving, FlexibleInstances,-   StandaloneDeriving #-}+{-# language+     Rank2Types+  , TemplateHaskell+  , OverloadedStrings+  , ExistentialQuantification+  , ScopedTypeVariables+  , GeneralizedNewtypeDeriving+  , FlexibleInstances+  , StandaloneDeriving+  #-}  module Rasa.Internal.Buffer   ( Buffer-  , Ext(..)-  , bufExts-  -- | A lens over the extensions stored for a buffer+  , HasBuffer(..)   , text--- | A lens into the text of the given buffer. Use within a BufAction.+  , bufExt+  , Ext(..)   , mkBuffer   ) where @@ -17,25 +23,66 @@ import qualified Yi.Rope as Y import Control.Lens hiding (matching) import Data.Map+import Data.Typeable+import Data.Default+import Data.Maybe+import Unsafe.Coerce  -- | A buffer, holds the text in the buffer and any extension states that are set on the buffer.--- A buffer is the State of the 'Rasa.Internal.Action.BufAction' monad transformer stack,--- so the type may be useful in defining lenses over your extension states. data Buffer = Buffer-  { _text :: Y.YiString-  , _bufExts :: ExtMap+  { _text' :: Y.YiString+  , _bufExts' :: ExtMap   }- makeLenses ''Buffer  instance Show Buffer where   show b = "<Buffer {text:" ++ show (b^..text . to (Y.take 30)) ++ "...,\n"            ++ "exts: " ++ show (b^.bufExts) ++ "}>\n" --- | Creates a new buffer from the givven text.+-- | This allows creation of polymorphic lenses over any type which has access to a Buffer+class HasBuffer a where+  buffer :: Lens' a Buffer++instance HasBuffer Buffer where+  buffer = lens id (flip const)++-- | This lens focuses the text of the in-scope buffer.+text :: HasBuffer b => Lens' b Y.YiString+text = buffer.text'++-- | This lens focuses the Extensions States map of the in-scope buffer.+bufExts :: HasBuffer b => Lens' b ExtMap+bufExts = buffer.bufExts'++-- | 'bufExt' is a lens which will focus a given extension's state within a+-- buffer (within a 'Data.Action.BufAction'). The lens will automagically focus+-- the required extension by using type inference. It's a little bit of magic,+-- if you treat the focus as a member of your extension state it should just+-- work out.+--+-- This lens falls back on the extension's 'Data.Default.Default' instance (when getting) if+-- nothing has yet been stored.++bufExt+  :: forall a s.+    (Show a, Typeable a, Default a, HasBuffer s)+    => Lens' s a+bufExt = lens getter setter+  where+    getter buf =+      fromMaybe def $ buf ^. bufExts . at (typeRep (Proxy :: Proxy a)) .+      mapping coerce+    setter buf new =+      set+        (bufExts . at (typeRep (Proxy :: Proxy a)) . mapping coerce)+        (Just new)+        buf+    coerce = iso (\(Ext x) -> unsafeCoerce x) Ext++-- | Creates a new buffer from the given text. mkBuffer :: Y.YiString -> Buffer mkBuffer txt =   Buffer-    { _text = txt-    , _bufExts = empty+    { _text' = txt+    , _bufExts' = empty     }
src/Rasa/Internal/Directive.hs view
@@ -12,6 +12,7 @@   , newBuffer   , getBufRefs   , getBuffers+  , getBuffer   , nextBufRef   , prevBufRef @@ -73,8 +74,14 @@ getBufRefs :: Action [BufRef] getBufRefs = fmap BufRef <$> use (buffers.to keys) +-- | Returns the 'Buffer' for a BufRef if it still exists.+-- This is read-only; altering the buffer has no effect on the stored buffer.+-- This function is useful for renderers.+getBuffer :: BufRef -> Action (Maybe Buffer)+getBuffer (BufRef bufInt) = use (buffers.at bufInt)+ -- | Returns an up-to-date list of all 'Buffer's, returned values--- are read-only; altering them has no effect on the actual stored bufferrs.+-- are read-only; altering them has no effect on the actual stored buffers. -- This function is useful for renderers. getBuffers :: Action [(BufRef, Buffer)] getBuffers = fmap (first BufRef) <$> use (buffers.to assocs)
src/Rasa/Internal/Editor.hs view
@@ -1,34 +1,26 @@-{-# LANGUAGE TemplateHaskell, Rank2Types,-  ExistentialQuantification, ScopedTypeVariables,-  OverloadedStrings+{-# language+    TemplateHaskell+  , Rank2Types+  , ExistentialQuantification+  , ScopedTypeVariables+  , OverloadedStrings   #-}  module Rasa.Internal.Editor   (   -- * Accessing/Storing state   Editor-  , HasEditor-  , editor+  , HasEditor(..)   , buffers---- |'buffers' is a lens into all buffers.-   , exiting---- | 'exiting' Whether the editor is in the process of exiting. Can be set inside an 'Rasa.Internal.Action.Action':------ > exiting .= True-   , ext   , bufExt   , nextBufId-  , range   , BufRef(..)   ) where  import Rasa.Internal.Buffer import Rasa.Internal.Extensions-import Rasa.Internal.Range  import Unsafe.Coerce import Data.Dynamic@@ -36,7 +28,6 @@ import Data.Maybe import Data.IntMap import Control.Lens-import qualified Yi.Rope as Y  -- | An opaque reference to a buffer (The contained Int is not meant to be -- altered). It is possible for references to become stale if buffers are@@ -48,12 +39,12 @@  -- | This is the primary state of the editor. data Editor = Editor-  { _buffers :: IntMap Buffer-  , _exiting :: Bool-  , _extState :: ExtMap-  , _nextBufId :: Int+  { _buffers' :: IntMap Buffer+  , _exiting' :: Bool+  , _extState' :: ExtMap+  , _nextBufId' :: Int   }-makeClassy ''Editor+makeLenses ''Editor  instance Show Editor where   show ed =@@ -61,41 +52,38 @@     ++ "Editor Extensions==============\n" ++ show (ed^.extState) ++ "\n\n"     ++ "---\n\n" +-- | This allows polymorphic lenses over anything that has access to an Editor context+class HasEditor a where+  editor :: Lens' a Editor +-- | A lens over the map of available buffers+buffers :: HasEditor e => Lens' e (IntMap Buffer)+buffers = editor.buffers'++-- | A lens over the exiting status of the editor+exiting :: HasEditor e => Lens' e Bool+exiting = editor.exiting'++-- | A lens over the extension state of 'Editor' level extensions+extState :: HasEditor e => Lens' e ExtMap+extState = editor.extState'++-- | A lens over the next buffer id to be allocated+nextBufId :: HasEditor e => Lens' e Int+nextBufId = editor.nextBufId'++instance HasEditor Editor where+  editor = lens id (flip const)+ instance Default Editor where   def =     Editor-    { _extState = def-    , _buffers=empty-    , _exiting=False-    , _nextBufId=0+    { _extState'=def+    , _buffers'=empty+    , _exiting'=False+    , _nextBufId'=0     } --- | 'bufExt' is a lens which will focus a given extension's state within a--- buffer (within a 'Data.Action.BufAction'). The lens will automagically focus--- the required extension by using type inference. It's a little bit of magic,--- if you treat the focus as a member of your extension state it should just--- work out.------ This lens falls back on the extension's 'Data.Default.Default' instance (when getting) if--- nothing has yet been stored.--bufExt-  :: forall a.-     (Show a, Typeable a, Default a)-    => Lens' Buffer a-bufExt = lens getter setter-  where-    getter buf =-      fromMaybe def $ buf ^. bufExts . at (typeRep (Proxy :: Proxy a)) .-      mapping coerce-    setter buf new =-      set-        (bufExts . at (typeRep (Proxy :: Proxy a)) . mapping coerce)-        (Just new)-        buf-    coerce = iso (\(Ext x) -> unsafeCoerce x) Ext- -- | 'ext' is a lens which will focus the extension state that matches the type -- inferred as the focal point. It's a little bit of magic, if you treat the -- focus as a member of your extension state it should just work out.@@ -118,9 +106,3 @@         (Just new)         ed     coerce = iso (\(Ext x) -> unsafeCoerce x) Ext---- | A lens over text which is encompassed by a 'Range'-range :: Range -> Lens' Buffer Y.YiString-range (Range start end) = lens getter setter-  where getter = view (text . beforeC end . afterC start)-        setter old new = old & text . beforeC end . afterC start .~ new
src/Rasa/Internal/Range.hs view
@@ -6,6 +6,7 @@   , clampCoord   , clampRange   , Range(..)+  , range   , sizeOf   , sizeOfR   , moveRange@@ -19,6 +20,8 @@   , afterC   ) where +import Rasa.Internal.Buffer+ import Control.Lens import Data.Maybe import Data.Monoid@@ -192,3 +195,9 @@          setter old new = let prefix = old ^. beforeC c                           in prefix <> new++-- | A lens over text which is encompassed by a 'Range'+range :: HasBuffer s =>  Range -> Lens' s Y.YiString+range (Range start end) = lens getter setter+  where getter = view (text . beforeC end . afterC start)+        setter old new = old & text . beforeC end . afterC start .~ new
src/Rasa/Internal/Scheduler.hs view
@@ -1,19 +1,17 @@-{-# LANGUAGE DeriveFunctor, GeneralizedNewtypeDeriving,-   ExistentialQuantification, ScopedTypeVariables #-}+{-# LANGUAGE ExistentialQuantification, ScopedTypeVariables #-}  module Rasa.Internal.Scheduler-  ( Scheduler(..)-  , Hook+  ( Hook   , Hooks   , afterRender   , beforeEvent   , beforeRender   , dispatchEvent   , eventListener-  , getHooks+  , removeListener   , matchingHooks-  , onExit   , onInit+  , onExit   , onRender   , onBufAdded   ) where@@ -24,71 +22,64 @@ import Rasa.Internal.Editor  import Control.Lens-import Control.Monad.Reader-import Control.Monad.State import Data.Dynamic import Data.Foldable-import Data.Map+import Data.Map hiding (filter) import Unsafe.Coerce --- | The Scheduler is how you can register your extension's actions to run--- at different points in the editor's event cycle.------ The event cycle proceeds as follows:------ @---     Init  (Runs ONCE)------     -- The following loops until an exit is triggered:---     BeforeEvent -> (any event) -> BeforeRender -> OnRender -> AfterRender------     Exit (Runs ONCE)--- @------ Each extension which wishes to perform actions exports a @'Scheduler' ()@--- which the user inserts in their config file.-newtype Scheduler a = Scheduler-  { runSched :: State Hooks a-  } deriving (Functor, Applicative, Monad, MonadState Hooks)- -- | Use this to dispatch an event of any type, any hooks 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-  hooks <- ask-  traverse_ ($ evt) (matchingHooks hooks)+  hooks' <- use hooks+  traverse_ ($ evt) (matchingHooks hooks')  -- | This is a helper which extracts and coerces a hook from its wrapper back into the proper event handler type. getHook :: forall a. Hook -> (a -> Action ()) getHook = coerce   where     coerce :: Hook -> (a -> Action ())-    coerce (Hook x) = unsafeCoerce x+    coerce (Hook _ x) = unsafeCoerce x +makeHook :: forall a. Typeable a => (a -> Action ()) -> Action (HookId, Hook)+makeHook hookFunc = do+  n <- nextHook <<+= 1+  let hookId = HookId n (typeRep (Proxy :: Proxy a))+  return (hookId, Hook hookId hookFunc)+ -- | This extracts all event listener hooks from a map of hooks which match the type of the provided event. matchingHooks :: forall a. Typeable a => Hooks -> [a -> Action ()]-matchingHooks hooks = getHook <$> (hooks^.at (typeRep (Proxy :: Proxy a))._Just)+matchingHooks hooks' = getHook <$> (hooks'^.at (typeRep (Proxy :: Proxy a))._Just)  -- | This registers an event listener hook, as long as the listener is well-typed similar to this: -- -- @MyEventType -> Action ()@ then it will be registered to listen for dispatched events of that type. -- Use within the 'Rasa.Internal.Scheduler.Scheduler' and add have the user add it to their config.-eventListener :: forall a. Typeable a => (a -> Action ()) -> Scheduler ()-eventListener hook = modify $ insertWith mappend (typeRep (Proxy :: Proxy a)) [Hook hook]---- | Transform a 'Rasa.Internal.Scheduler.Scheduler' monad into a 'Hooks' map.-getHooks :: Scheduler () -> Hooks-getHooks = flip execState mempty . runSched+-- It returns an ID which may be used with 'removeListener'+eventListener :: forall a. Typeable a => (a -> Action ()) -> Action HookId+eventListener hookFunc = do+  (hookId, hook) <- makeHook hookFunc+  hooks %= insertWith mappend (typeRep (Proxy :: Proxy a)) [hook]+  return hookId +-- | This removes a listener and prevents it from responding to any more events.+removeListener :: HookId -> Action ()+removeListener hkIdA@(HookId _ typ) =+  hooks.at typ._Just %= filter hookMatches+    where+      hookMatches (Hook hkIdB _) = hkIdA /= hkIdB  -- | Registers an action to be performed during the Initialization phase. -- -- This phase occurs exactly ONCE when the editor starts up.-onInit :: Action () -> Scheduler ()+-- 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 :: Action () -> Action HookId onInit action = eventListener (const action :: Init -> Action ())  -- | Registers an action to be performed BEFORE each event phase.-beforeEvent :: Action () -> Scheduler ()+beforeEvent :: Action () -> Action HookId beforeEvent action = eventListener (const action :: BeforeEvent -> Action ())  -- | Registers an action to be performed BEFORE each render phase.@@ -96,20 +87,20 @@ -- 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.-beforeRender :: Action () -> Scheduler ()+beforeRender :: Action () -> Action HookId beforeRender action = eventListener (const action :: BeforeRender -> Action ())  -- | Registers an action to be performed during each render phase. -- -- This phase should only be used by extensions which actually render something.-onRender :: Action () -> Scheduler ()+onRender :: Action () -> Action HookId onRender action = eventListener (const action :: OnRender -> Action ())  -- | 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.-afterRender :: Action () -> Scheduler ()+afterRender :: Action () -> Action HookId afterRender action = eventListener (const action :: AfterRender -> Action ())  -- | Registers an action to be performed during the exit phase.@@ -118,13 +109,13 @@ -- allows an opportunity to do clean-up, kill any processes you've started, or -- save any data before the editor terminates. -onExit :: Action () -> Scheduler ()+onExit :: Action () -> Action HookId onExit action = eventListener (const action :: Exit -> Action ())  -- | 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 :: (BufRef -> Action ()) -> Scheduler ()+onBufAdded :: (BufRef -> Action ()) -> Action HookId onBufAdded f = eventListener listener   where     listener (BufAdded bRef) = f bRef