diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -80,6 +80,17 @@
 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
+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
diff --git a/rasa.cabal b/rasa.cabal
--- a/rasa.cabal
+++ b/rasa.cabal
@@ -1,84 +1,85 @@
-name:                rasa
-version:             0.1.3
-synopsis:            A modular text editor
-homepage:            https://github.com/ChrisPenner/rasa#readme
-license:             MIT
-license-file:        LICENSE
-author:              Chris Penner
-maintainer:          christopher.penner@gmail.com
-copyright:           2016 Chris Penner
-category:            Text Editor, Executable
-build-type:          Simple
-extra-source-files:  README.md
-cabal-version:       >=1.10
-description:         A modular text editor
-  This is only a snippet, see the project's <https://github.com/ChrisPenner/rasa README>.
-  .
-  Rasa is a text editor project with a few interesting goals. For better or worse it attempts
-  to be as modular as possible. This means that most functionality which would typically be considered to be 'core' in other editors
-  is implemented as extensions in Rasa.
-  This approach comes with its own share of pros and cons, for instance:
-  .
-  /Pros/
-  .
-  * Implementing most core functionality as extensions ensures a powerful and elegant extension interface.
-  .
-  * Flexibility; don't like the default cursor implementation? Write your own!
-  .
-  * Adaptability; the core of Rasa is miniscule, you can mix and match extensions to build any editor you want.
-  .
-  /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.
-  .
-  * 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.
-  .
-  While highly experimental, I've found the current API to be quite expressive and adaptable; 
-  for instance I was able to implement the notion of multiple cursors using the extension API in less than a day. I hope to keep the learning curve low as development continues.
-  .
-  /Getting Started/
-  .
-  First clone the <https://github.com/ChrisPenner/rasa Github repo>
-  and try running the example-config included there. 
-  Once you get it running (see the <https://github.com/ChrisPenner/rasa/blob/master/README.md README>) then you
-  can customize your keymap to add a few mappings you like. 
-  Then I'd check out the 
-  <https://github.com/ChrisPenner/rasa/blob/master/docs/Building-An-Extension.md Building your own extensions guide>.
-  It goes in depth into everything you'd want to know!
-  .
-  If you have any issues (and I'm sure there'll be a few; it's a new project!) please report them <https://github.com/ChrisPenner/rasa/issues here> and we'll talk about it!
-
-library
-  hs-source-dirs:      src
-  exposed-modules:
-                       Rasa
-
-                       Rasa.Ext
-
-                       Rasa.Internal.Action
-                       Rasa.Internal.Buffer
-                       Rasa.Internal.Directive
-                       Rasa.Internal.Editor
-                       Rasa.Internal.Events
-                       Rasa.Internal.Extensions
-                       Rasa.Internal.Range
-                       Rasa.Internal.Scheduler
-                       Rasa.Internal.Text
+name: rasa
+version: 0.1.4
+cabal-version: >=1.10
+build-type: Simple
+license: MIT
+license-file: LICENSE
+copyright: 2016 Chris Penner
+maintainer: christopher.penner@gmail.com
+homepage: https://github.com/ChrisPenner/rasa#readme
+synopsis: A modular text editor
+description:
+    A modular text editor
+    This is only a snippet, see the project's <https://github.com/ChrisPenner/rasa README>.
+    .
+    Rasa is a text editor project with a few interesting goals. For better or worse it attempts
+    to be as modular as possible. This means that most functionality which would typically be considered to be 'core' in other editors
+    is implemented as extensions in Rasa.
+    This approach comes with its own share of pros and cons, for instance:
+    .
+    /Pros/
+    .
+    * Implementing most core functionality as extensions ensures a powerful and elegant extension interface.
+    .
+    * Flexibility; don't like the default cursor implementation? Write your own!
+    .
+    * Adaptability; the core of Rasa is miniscule, you can mix and match extensions to build any editor you want.
+    .
+    /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.
+    .
+    * 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.
+    .
+    While highly experimental, I've found the current API to be quite expressive and adaptable;
+    for instance I was able to implement the notion of multiple cursors using the extension API in less than a day. I hope to keep the learning curve low as development continues.
+    .
+    /Getting Started/
+    .
+    First clone the <https://github.com/ChrisPenner/rasa Github repo>
+    and try running the example-config included there.
+    Once you get it running (see the <https://github.com/ChrisPenner/rasa/blob/master/README.md README>) then you
+    can customize your keymap to add a few mappings you like.
+    Then I'd check out the
+    <https://github.com/ChrisPenner/rasa/blob/master/docs/Building-An-Extension.md Building your own extensions guide>.
+    It goes in depth into everything you'd want to know!
+    .
+    If you have any issues (and I'm sure there'll be a few; it's a new project!) please report them <https://github.com/ChrisPenner/rasa/issues here> and we'll talk about it!
+category: Text Editor, Executable
+author: Chris Penner
+extra-source-files:
+    README.md
 
-  ghc-options:         -Wall
-  build-depends:       base >= 4.7 && < 5
-                     , async
-                     , containers
-                     , data-default
-                     , lens
-                     , mtl
-                     , text
-                     , text-lens
-                     , transformers
-                     , yi-rope
+source-repository head
+    type: git
+    location: https://github.com/ChrisPenner/rasa
 
-  default-language:    Haskell2010
+library
+    exposed-modules:
+        Rasa
+        Rasa.Ext
+        Rasa.Internal.Action
+        Rasa.Internal.Async
+        Rasa.Internal.Buffer
+        Rasa.Internal.Directive
+        Rasa.Internal.Editor
+        Rasa.Internal.Events
+        Rasa.Internal.Extensions
+        Rasa.Internal.Range
+        Rasa.Internal.Scheduler
+        Rasa.Internal.Text
+    build-depends:
+        base >=4.8 && <5,
+        async >=2.1.1 && <2.2,
+        containers >=0.5.7.1 && <0.6,
+        data-default >=0.7.1.1 && <0.8,
+        lens ==4.14.*,
+        mtl >=2.2.1 && <2.3,
+        text >=1.2.2.1 && <1.3,
+        text-lens >=0.1.0.0 && <0.2,
+        transformers >=0.5.2.0 && <0.6,
+        yi-rope >=0.7.0.2 && <0.8
+    default-language: Haskell2010
+    hs-source-dirs: src
+    ghc-options: -Wall
 
-source-repository head
-  type:     git
-  location: https://github.com/ChrisPenner/rasa
diff --git a/src/Rasa.hs b/src/Rasa.hs
--- a/src/Rasa.hs
+++ b/src/Rasa.hs
@@ -6,46 +6,45 @@
 import Rasa.Internal.Events
 import Rasa.Internal.Scheduler
 
-import Control.Lens
 import Control.Concurrent.Async
+import Control.Lens
 import Control.Monad
-import Control.Monad.State
-import Control.Monad.Reader
+import Control.Monad.IO.Class
 import Data.Default (def)
-import Data.Foldable
+import Data.List
 
 -- | The main function to run rasa.
--- 
+--
 -- @rasa eventProviders extensions@
--- 
--- This should be imported by a user-config and called with extension event providers and extension event hooks as
--- arguments. e.g.:
--- 
--- > rasa [slateEvent] $ do
+--
+-- This should be imported by a user-config with and called with a 'Scheduler'
+-- containing any extensions which have event listeners.
+--
+-- > rasa $ do
 -- >   cursor
 -- >   vim
-rasa :: [Action [Keypress]] -> Scheduler () -> IO ()
-rasa eventProviders scheduler =
+-- >   slate
+
+rasa :: Scheduler () -> IO ()
+rasa scheduler =
   evalAction def hooks $ do
     dispatchEvent Init
-    eventLoop eventProviders
+    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 listens for an
--- event from the event providers, then runs the post event hooks and repeats.
-eventLoop :: [Action [Keypress]] -> Action ()
-eventLoop eventProviders = do
+-- sets 'Rasa.Editor.exiting'. It runs the pre-event hooks, then checks if any
+-- async events have finished, then runs the post event hooks and repeats.
+eventLoop :: Action ()
+eventLoop = do
   dispatchEvent BeforeRender
   dispatchEvent OnRender
   dispatchEvent AfterRender
   dispatchEvent BeforeEvent
-  currentEditor <- get
-  hooks <- ask
-  -- This is a little weird, but I think it needs to be this way to execute providers in parallel
-  asyncEventProviders <- liftIO $ traverse (async.evalAction currentEditor hooks) eventProviders
-  (_, nextEvents) <- liftIO $ waitAny asyncEventProviders
-  traverse_ dispatchEvent nextEvents
+  asyncActions <- use asyncs
+  (done, action) <- liftIO $ waitAny asyncActions
+  asyncs %= delete done
+  action
   isExiting <- use exiting
-  unless isExiting $ eventLoop eventProviders
+  unless isExiting eventLoop
diff --git a/src/Rasa/Ext.hs b/src/Rasa/Ext.hs
--- a/src/Rasa/Ext.hs
+++ b/src/Rasa/Ext.hs
@@ -45,23 +45,29 @@
   (
   -- * Editor Actions
     Action
+  , doAsync
   , exit
-  , addBuffer
-  , addBufferThen
-  , nextBuf
-  , prevBuf
 
-  -- * Buffer Actions
+  -- * Managing Buffers
+  , newBuffer
+  , nextBufRef
+  , prevBufRef
+
+  -- * Working with Buffers
   , BufAction
   , bufDo
-  , focusDo
+  , bufDo_
+  , buffersDo
+  , buffersDo_
+
+  -- * Working with Text
   , overRange
   , replaceRange
   , deleteRange
   , insertAt
   , sizeOf
 
-  -- * Persisting Extension State
+  -- * Working with Extensions
   -- | Extension states for ALL the extensions installed are stored in the same
   -- map keyed by their 'Data.Typeable.TypeRep' so if more than one extension
   -- uses the same type then they'll conflict. This is easily solved by simply
@@ -99,20 +105,13 @@
 
    -- * Accessing/Editing Context
   , Buffer
+  , BufRef
+  , HasEditor
   , text
-  -- | A lens over the buffer's 'Data.Text.Text'. Use within a 'BufAction' as
+  -- | A lens over the buffer's Text as a 'Yi.Rope.YiString'. Use within a 'BufAction':
   --
   -- > txt <- use text
 
-  , exiting
-  -- | A lens over the current 'exit' status of the editor, allows an extension to
-  -- signal the editor to shutdown. If this is set the current events will finish processing, then the
-  --
-  -- 'Rasa.Ext.Events.Exit' event will be dispatched, then the editor will exit.
-  -- Use within an 'Action'
-  --
-  -- > exiting .= True
-
   -- * Events
   , Keypress(..)
   , Mod(..)
@@ -123,6 +122,7 @@
   , Hook
   , dispatchEvent
   , eventListener
+  , eventProvider
 
   -- * Built-in Event Hooks
   , onInit
@@ -131,6 +131,7 @@
   , onRender
   , afterRender
   , onExit
+  , onBufAdded
 
    -- * Ranges
   , Range(..)
@@ -153,11 +154,11 @@
   , asText
   , asString
   , asLines
-  , rope
   , clamp
   ) where
 
 import Rasa.Internal.Action
+import Rasa.Internal.Async
 import Rasa.Internal.Directive
 import Rasa.Internal.Editor
 import Rasa.Internal.Events
diff --git a/src/Rasa/Internal/Action.hs b/src/Rasa/Internal/Action.hs
--- a/src/Rasa/Internal/Action.hs
+++ b/src/Rasa/Internal/Action.hs
@@ -1,15 +1,25 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving, ExistentialQuantification #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving, ExistentialQuantification, TemplateHaskell, StandaloneDeriving #-}
 
 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
 
 import Rasa.Internal.Buffer
 import Rasa.Internal.Editor
 
+
+-- | A wrapper around event listeners so they can be stored in 'Hooks'.
+data Hook = forall a. Hook a
+
+-- | A map of Event types to a list of listeners for that event
+type Hooks = Map TypeRep [Hook]
+
 -- | This is a monad-transformer stack for performing actions against the editor.
 -- You register Actions to be run in response to events using 'Rasa.Internal.Scheduler.eventListener'
 --
@@ -22,17 +32,37 @@
 --      * Add\/Edit\/Focus buffers and a few other Editor-level things, see the 'Rasa.Internal.Ext.Directive' module.
 
 newtype Action a = Action
-  { runAct :: StateT Editor (ReaderT Hooks IO) a
-  } deriving (Functor, Applicative, Monad, MonadState Editor, MonadReader Hooks, MonadIO)
+  { runAct :: StateT ActionState (ReaderT Hooks IO) a
+  } deriving (Functor, Applicative, Monad, MonadState ActionState, MonadReader Hooks, MonadIO)
 
+
 -- | Unwrap and execute an Action (returning the editor state)
-execAction :: Editor -> Hooks -> Action () -> IO Editor
-execAction editor hooks action  = flip runReaderT hooks $ execStateT (runAct action) editor
+execAction :: ActionState -> Hooks -> Action () -> IO ActionState
+execAction actionState hooks action = flip runReaderT hooks . execStateT (runAct action) $ actionState
 
 -- | Unwrap and evaluate an Action (returning the value)
-evalAction :: Editor -> Hooks -> Action a ->IO a
-evalAction editor hooks action  = flip runReaderT hooks $ evalStateT (runAct action) editor
+evalAction :: ActionState -> Hooks -> Action a -> IO a
+evalAction actionState hooks action  = flip runReaderT hooks $ evalStateT (runAct action) actionState
 
+type AsyncAction = Async (Action ())
+data ActionState = ActionState
+  { _ed :: Editor
+  , _asyncs :: [AsyncAction]
+  }
+makeClassy ''ActionState
+
+instance HasEditor ActionState where
+  editor = ed
+
+instance Default ActionState where
+  def = ActionState
+    { _ed=def
+    , _asyncs=def
+    }
+
+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'
 --
@@ -47,9 +77,16 @@
   { getBufAction::StateT Buffer (ReaderT Hooks IO) a
   } deriving (Functor, Applicative, Monad, MonadState Buffer, MonadReader Hooks, MonadIO)
 
-
--- | A wrapper around event listeners so they can be stored in 'Hooks'.
-data Hook = forall a. Hook a
+-- | 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)
+  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
 
--- | A map of Event types to a list of listeners for that event
-type Hooks = Map TypeRep [Hook]
diff --git a/src/Rasa/Internal/Async.hs b/src/Rasa/Internal/Async.hs
new file mode 100644
--- /dev/null
+++ b/src/Rasa/Internal/Async.hs
@@ -0,0 +1,59 @@
+module Rasa.Internal.Async 
+  ( doAsync
+  , eventProvider
+) where
+
+import Rasa.Internal.Action
+import Rasa.Internal.Scheduler
+
+import Data.Typeable
+import Control.Concurrent.Async
+import Control.Lens
+import Control.Monad.IO.Class
+
+-- | This function takes an IO which results in some Event, it runs the IO
+-- asynchronously and dispatches the event, then repeats the process all over
+-- again. Use this inside the onInit scheduler to register an event listener
+-- for some event (e.g. keypresses or network activity)
+
+eventProvider :: Typeable a => IO a -> Action ()
+eventProvider getEventIO = doAsync (dispatchAndRerun <$> getEventIO)
+    where dispatchAndRerun evt = do
+            dispatchEvent evt
+            eventProvider getEventIO
+
+-- | doAsync allows you to perform a task asynchronously and then apply the
+-- result. In @doAsync asyncAction@, @asyncAction@ is an IO which resolves to
+-- an Action, note that the context in which the second action is executed is
+-- NOT the same context in which doAsync is called; it is likely that text and
+-- other state have changed while the IO executed, so it's a good idea to check
+-- (inside the applying Action) that things are in a good state before making
+-- changes. Here's an example:
+--
+-- > asyncCapitalize :: Action ()
+-- > asyncCapitalize = do
+-- >   txt <- focusDo $ use text
+-- >   -- We give doAsync an IO which resolves in an action
+-- >   doAsync $ ioPart txt
+-- >
+-- > ioPart :: Text -> IO (Action ())
+-- > ioPart txt = do
+-- >   result <- longAsyncronousCapitalizationProgram txt
+-- >   -- Note that this returns an Action, but it's still wrapped in IO
+-- >   return $ maybeApplyResult txt result
+-- >
+-- > maybeApplyResult :: Text -> Text -> Action ()
+-- > maybeApplyResult oldTxt capitalized = do
+-- >   -- We get the current buffer's text, which may have changed since we started
+-- >   newTxt <- focusDo (use text)
+-- >   if newTxt == oldTxt
+-- >     -- If the text is the same as it was, we can apply the transformation
+-- >     then focusDo (text .= capitalized)
+-- >     -- Otherwise we can choose to re-queue the whole action and try again
+-- >     -- Or we could just give up.
+-- >     else asyncCapitalize
+
+doAsync ::  IO (Action ()) -> Action ()
+doAsync asyncIO = do
+  newAsync <- liftIO $ async asyncIO
+  asyncs <>= [newAsync]
diff --git a/src/Rasa/Internal/Buffer.hs b/src/Rasa/Internal/Buffer.hs
--- a/src/Rasa/Internal/Buffer.hs
+++ b/src/Rasa/Internal/Buffer.hs
@@ -8,15 +8,12 @@
   , bufExts
   -- | A lens over the extensions stored for a buffer
   , text
-  , rope
-  -- | A lens over the text stored in a buffer (as a 'Y.YiString')
-  , newBuffer
+-- | A lens into the text of the given buffer. Use within a BufAction.
+  , mkBuffer
   ) where
 
-import Rasa.Internal.Text
 import Rasa.Internal.Extensions
 
-import qualified Data.Text as T
 import qualified Yi.Rope as Y
 import Control.Lens hiding (matching)
 import Data.Map
@@ -25,24 +22,20 @@
 -- 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
-  { _rope :: Y.YiString
+  { _text :: Y.YiString
   , _bufExts :: ExtMap
   }
 
 makeLenses ''Buffer
 
--- | A lens into the text of the given buffer. Use within a BufAction.
-text :: Lens' Buffer T.Text
-text = rope.asText
-
 instance Show Buffer where
-  show b = "<Buffer {text:" ++ show (b^..text . to (T.take 30)) ++ "...,\n"
+  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.
-newBuffer :: T.Text -> Buffer
-newBuffer txt =
+mkBuffer :: Y.YiString -> Buffer
+mkBuffer txt =
   Buffer
-    { _rope = Y.fromText txt
+    { _text = txt
     , _bufExts = empty
     }
diff --git a/src/Rasa/Internal/Directive.hs b/src/Rasa/Internal/Directive.hs
--- a/src/Rasa/Internal/Directive.hs
+++ b/src/Rasa/Internal/Directive.hs
@@ -3,14 +3,16 @@
   (
   -- * Performing Actions on Buffers
     bufDo
-  , focusDo
+  , bufDo_
+  , buffersDo
+  , buffersDo_
 
   -- * Editor Actions
   , exit
-  , addBuffer
-  , addBufferThen
-  , nextBuf
-  , prevBuf
+  , newBuffer
+  , getBufRefs
+  , nextBufRef
+  , prevBufRef
 
   -- * Buffer Actions
   , overRange
@@ -24,36 +26,76 @@
 import Rasa.Internal.Editor
 import Rasa.Internal.Action
 import Rasa.Internal.Range
+import Rasa.Internal.Scheduler
+import Rasa.Internal.Events
 import Rasa.Internal.Buffer as B
 
 import Control.Lens
-import qualified Data.Text as T
+import Control.Monad
+import Data.Maybe
+import Data.IntMap as M
+import qualified Yi.Rope as Y
 
+
 -- | This lifts a 'Rasa.Action.BufAction' to an 'Rasa.Action.Action' which
--- performs the 'Rasa.Action.BufAction' on the focused buffer.
+-- performs the 'Rasa.Action.BufAction' on every buffer and collects the return
+-- values as a list.
 
-focusDo :: BufAction a -> Action a
-focusDo = Action . zoom focusedBuf . getBufAction
+buffersDo :: BufAction a -> Action [a]
+buffersDo bufAct = do
+  bufRefs <- getBufRefs
+  catMaybes . foldMap (:[]) <$> mapM (liftBuf bufAct) bufRefs
 
+buffersDo_ :: BufAction a -> Action ()
+buffersDo_ = void . buffersDo
+
 -- | This lifts a 'Rasa.Action.BufAction' to an 'Rasa.Action.Action' which
--- performs the 'Rasa.Action.BufAction' on every buffer and collects the return
--- values via 'mappend'
+-- performs the 'Rasa.Action.BufAction' on the buffer referred to by the 'BufRef'
+-- If the buffer referred to no longer exists this returns @Action Nothing@.
+bufDo :: BufRef -> BufAction a -> Action (Maybe a)
+bufDo bufRef bufAct = liftBuf bufAct bufRef
 
-bufDo :: Monoid a => BufAction a -> Action a
-bufDo = Action . zoom (buffers . traverse) . getBufAction
+bufDo_ :: BufRef -> BufAction a -> Action ()
+bufDo_ bufRef bufAct = void $ bufDo bufRef bufAct
 
 -- | This adds a new buffer with the given text.
+newBuffer :: Y.YiString -> Action BufRef
+newBuffer txt = do
+  n <- nextBufId <<+= 1
+  buffers %= insert n (mkBuffer txt)
+  let bufRef = BufRef n
+  dispatchEvent (BufAdded bufRef)
+  return bufRef
 
-addBuffer :: T.Text -> Action ()
-addBuffer txt = buffers <>= [newBuffer txt]
+-- | Returns an up-to-date list of all 'BufRef's
+getBufRefs :: Action [BufRef]
+getBufRefs = fmap BufRef <$> use (buffers.to keys)
 
--- | This adds a new buffer with the given text then performs the given
--- 'Rasa.Action.BufAction' agains that buffer.
-addBufferThen :: T.Text -> BufAction a -> Action a
-addBufferThen txt act = do
-  buffers <>= [newBuffer txt]
-  fmap (!! 0) . Action . zoom (buffers._last) . fmap (:[]) . getBufAction $ act
+-- | Gets 'BufRef' that comes after the one provided
+nextBufRef :: BufRef -> Action BufRef
+nextBufRef br@(BufRef bufInt) = do
+  bufMap <- use buffers
+  if M.null bufMap
+     then return br
+     else do
+       let mGreaterInd = lookupGT bufInt bufMap
+       case mGreaterInd of
+         Just (greaterInd, _) -> return $ BufRef greaterInd
+         Nothing -> return . BufRef . fst . findMin $ bufMap
 
+-- | Gets 'BufRef' that comes before the one provided
+prevBufRef :: BufRef -> Action BufRef
+prevBufRef br@(BufRef bufInt) = do
+  bufMap <- use buffers
+  if M.null bufMap
+     then return br
+     else do
+       let mLesserInd = lookupGT bufInt bufMap
+       case mLesserInd of
+         Just (lesserInd, _) -> return $ BufRef lesserInd
+         Nothing -> return . BufRef . fst . findMax $ bufMap
+
+
 -- | This signals to the editor that you'd like to shutdown. The current events
 -- will finish processing, then the 'Rasa.Ext.Scheduler.onExit' hook will run,
 -- then the editor will exit.
@@ -61,31 +103,19 @@
 exit :: Action ()
 exit = exiting .= True
 
--- | Switches focus to the next buffer
-nextBuf :: Action ()
-nextBuf = do
-  numBuffers <- use (buffers.to length)
-  focused %= (`mod` numBuffers) . (+1)
-
--- | Switches focus to the previous buffer
-prevBuf :: Action ()
-prevBuf = do
-  numBuffers <- use (buffers.to length)
-  focused %= (`mod` numBuffers) . subtract 1
-
 -- | Deletes the text in the given range from the buffer.
 deleteRange :: Range -> BufAction ()
 deleteRange r = range r.asText .= ""
 
 -- | Replaces the text in the given range from the buffer.
-replaceRange :: Range -> T.Text -> BufAction ()
-replaceRange r txt = range r.asText .= txt
+replaceRange :: Range -> Y.YiString -> BufAction ()
+replaceRange r txt = range r .= txt
 
 -- | Inserts text into the buffer at the given Coord.
-insertAt :: Coord -> T.Text -> BufAction ()
-insertAt c txt = range (Range c c).asText .= txt
+insertAt :: Coord -> Y.YiString -> BufAction ()
+insertAt c txt = range (Range c c) .= txt
 
 -- | Runs the given function over the text in the range, replacing it with the results.
-overRange :: Range -> (T.Text -> T.Text) -> BufAction ()
-overRange r f = range r.asText %= f
+overRange :: Range -> (Y.YiString -> Y.YiString) -> BufAction ()
+overRange r f = range r %= f
 
diff --git a/src/Rasa/Internal/Editor.hs b/src/Rasa/Internal/Editor.hs
--- a/src/Rasa/Internal/Editor.hs
+++ b/src/Rasa/Internal/Editor.hs
@@ -7,10 +7,8 @@
   (
   -- * Accessing/Storing state
   Editor
-
-  , focused
--- | 'focused' is the index of the currently selected buffer (this will likely change)
-
+  , HasEditor
+  , editor
   , buffers
 
 -- |'buffers' is a lens into all buffers.
@@ -22,10 +20,10 @@
 -- > exiting .= True
 
   , ext
-  , allBufExt
   , bufExt
-  , focusedBuf
+  , nextBufId
   , range
+  , BufRef(..)
   ) where
 
 import Rasa.Internal.Buffer
@@ -36,22 +34,31 @@
 import Data.Dynamic
 import Data.Default
 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
+-- deleted. Operations over invalid BufRef's are simply ignored and return
+-- 'Nothing' if a value was expected.
+newtype BufRef =
+  BufRef Int
+  deriving (Show, Eq)
+
 -- | This is the primary state of the editor.
 data Editor = Editor
-  { _buffers :: [Buffer]
-  , _focused :: Int
+  { _buffers :: IntMap Buffer
   , _exiting :: Bool
   , _extState :: ExtMap
+  , _nextBufId :: Int
   }
-makeLenses ''Editor
+makeClassy ''Editor
 
 instance Show Editor where
-  show editor =
-    "Buffers==============\n" ++ show (editor^.buffers) ++ "\n\n"
-    ++ "Editor Extensions==============\n" ++ show (editor^.extState) ++ "\n\n"
+  show ed =
+    "Buffers==============\n" ++ show (ed^.buffers) ++ "\n\n"
+    ++ "Editor Extensions==============\n" ++ show (ed^.extState) ++ "\n\n"
     ++ "---\n\n"
 
 
@@ -59,23 +66,11 @@
   def =
     Editor
     { _extState = def
-    , _buffers=fmap newBuffer [ "Buffer 0\nHey! How's it going over there?\nI'm having just a splended time!\nAnother line for you sir?"
-                            , "Buffer 1\nHey! How's it going over there?\nI'm having just a splended time!\nAnother line for you sir?" ]
-    , _focused=0
+    , _buffers=empty
     , _exiting=False
+    , _nextBufId=0
     }
 
--- | A lens over the extensions of all buffers.
--- This is useful for setting defaults or altering extension state across all buffers.
-allBufExt
-  :: forall a.
-     (Show a, Typeable a)
-  => Traversal' Editor (Maybe a)
-allBufExt =
-  buffers . traverse . bufExts . at (typeRep (Proxy :: Proxy a)) . mapping coerce
-  where
-    coerce = iso (\(Ext x) -> unsafeCoerce x) Ext
-
 -- | '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,
@@ -109,35 +104,23 @@
 -- nothing has yet been stored.
 
 ext
-  :: forall a.
-     (Show a, Typeable a, Default a)
-  => Lens' Editor a
+  :: forall a e.
+    (Show a, Typeable a, Default a, HasEditor e)
+  => Lens' e a
 ext = lens getter setter
   where
-    getter editor =
-      fromMaybe def $ editor ^. extState . at (typeRep (Proxy :: Proxy a)) .
+    getter ed =
+      fromMaybe def $ ed ^. extState . at (typeRep (Proxy :: Proxy a)) .
       mapping coerce
-    setter editor new =
+    setter ed new =
       set
         (extState . at (typeRep (Proxy :: Proxy a)) . mapping coerce)
         (Just new)
-        editor
+        ed
     coerce = iso (\(Ext x) -> unsafeCoerce x) Ext
 
--- | 'focusedBuf' is a lens which focuses the currently selected buffer.
-focusedBuf :: Lens' Editor Buffer
-focusedBuf = lens getter setter
-  where
-    getter editor =
-      let foc = editor ^. focused
-      in editor ^?! buffers . ix foc
-    setter editor new =
-      let foc = editor ^. focused
-      in editor & buffers . ix foc .~ new
-
 -- | 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 (rope . beforeC end . afterC start)
-        setter old new = old & rope . beforeC end . afterC start .~ new
-
+  where getter = view (text . beforeC end . afterC start)
+        setter old new = old & text . beforeC end . afterC start .~ new
diff --git a/src/Rasa/Internal/Events.hs b/src/Rasa/Internal/Events.hs
--- a/src/Rasa/Internal/Events.hs
+++ b/src/Rasa/Internal/Events.hs
@@ -1,7 +1,18 @@
 {-# language ExistentialQuantification #-}
-module Rasa.Internal.Events where
+module Rasa.Internal.Events 
+  ( Init(..)
+  , BeforeEvent(..)
+  , BeforeRender(..)
+  , OnRender(..)
+  , AfterRender(..)
+  , Exit(..)
+  , BufAdded(..)
+  , Keypress(..)
+  , Mod(..)
+  ) where
 
 import Data.Dynamic
+import Rasa.Internal.Editor
 
 -- | The Event type represents a common denominator for all actions that could
 -- occur Event transmitters express events that have occured as a member of this
@@ -29,6 +40,9 @@
 -- any clean-up (saving files, etc.)
 data Exit = Exit deriving (Show, Eq, Typeable)
 
+-- | This event is dispatched after adding a new buffer. The contained BufRef refers to the new buffer.
+data BufAdded = BufAdded BufRef deriving (Show, Eq, Typeable)
+
 -- | This event is dispatched in response to keyboard key presses. It contains both
 -- the char that was pressed and any modifiers ('Mod') that where held when the key was pressed.
 data Keypress
@@ -37,6 +51,7 @@
   | Esc
   | BS
   | Enter
+  | Unknown
   deriving (Show, Eq, Typeable)
 
 -- | This represents each modifier key that could be pressed along with a key.
diff --git a/src/Rasa/Internal/Scheduler.hs b/src/Rasa/Internal/Scheduler.hs
--- a/src/Rasa/Internal/Scheduler.hs
+++ b/src/Rasa/Internal/Scheduler.hs
@@ -15,11 +15,13 @@
   , onExit
   , onInit
   , onRender
+  , onBufAdded
   ) where
 
 
 import Rasa.Internal.Action
 import Rasa.Internal.Events
+import Rasa.Internal.Editor
 
 import Control.Lens
 import Control.Monad.Reader
@@ -119,3 +121,10 @@
 onExit :: Action () -> Scheduler ()
 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 f = eventListener listener
+  where
+    listener (BufAdded bRef) = f bRef
