diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,30 @@
+# 1.0.0.0
+
+* Each plugin (type) now defines an environment which is similar to how
+  stateful plugins have been declared in previous versions. If you need
+  multiple different environments for different functions, you can make them
+  fields of a bigger environment or define multiple plugin values.
+
+  The type `Neovim r st a` has become `Neovim env a` where `env` is
+  technically equivalent to the previous `r`.
+  I was mainly motivated by this blog post:
+
+  https://www.fpcomplete.com/blog/2017/06/readert-design-pattern
+
+* Only works with ghc >= 8. I removed some backwards compatibility. If you
+  need older ghc versions, just use the previous version (0.2.5) as the
+  feature set hasn't really changed.
+
+* A different pretty printer library is now used and may surface at some
+  places.
+
+* Functions do now time out after some time, 10 seconds for those that block
+  neovim and 10 minutes for background functions.
+
+* A few types have been adjusted.
+
+* Some improvement in error reporting.
+
 # 0.2.5
 
 * Older versions of `nvim-hs` may not function if some versions of a
diff --git a/library/Neovim.hs b/library/Neovim.hs
--- a/library/Neovim.hs
+++ b/library/Neovim.hs
@@ -22,7 +22,6 @@
     -- ** tl;dr
     -- $tldrgettingstarted
     Neovim,
-    Neovim',
     neovim,
     NeovimConfig(..),
     defaultConfig,
@@ -35,7 +34,6 @@
     -- ** Creating a plugin
     -- $creatingplugins
     NeovimPlugin(..),
-    StatefulFunctionality(..),
     Plugin(..),
     NvimObject(..),
     (+:),
@@ -53,14 +51,9 @@
     CommandArguments(..),
     AutocmdOptions(..),
     addAutocmd,
-    addAutocmd',
 
     ask,
     asks,
-    put,
-    get,
-    gets,
-    modify,
 
     -- ** Creating a stateful plugin
     -- $statefulplugin
@@ -72,10 +65,7 @@
     waitErr,
     waitErr',
     err,
-    Doc,
-    Pretty(..),
     errOnInvalidResult,
-    text,
     NeovimException(..),
     -- ** Generated functions for neovim interaction
     module Neovim.API.String,
@@ -89,6 +79,11 @@
     unlessM,
     docToObject,
     docFromObject,
+    Doc,
+    AnsiStyle,
+    Pretty(..),
+    putDoc,
+    exceptionToDoc,
     Priority(..),
     module Control.Monad,
     module Control.Applicative,
@@ -110,15 +105,17 @@
 import           Neovim.API.TH                (autocmd, command, command',
                                                function, function')
 import           Neovim.Classes               (Dictionary, NvimObject (..),
+                                               Doc, AnsiStyle, Pretty(..),
                                                docFromObject, docToObject, (+:))
 import           Neovim.Config                (NeovimConfig (..))
-import           Neovim.Context               (Neovim, Neovim',
-                                               NeovimException (ErrorMessage),
+import           Neovim.Context               (Neovim,
+                                               NeovimException(..),
+                                               exceptionToDoc,
                                                ask, asks, err,
                                                errOnInvalidResult, get, gets,
                                                modify, put)
 import           Neovim.Main                  (neovim)
-import           Neovim.Plugin                (addAutocmd, addAutocmd')
+import           Neovim.Plugin                (addAutocmd)
 import           Neovim.Plugin.Classes        (AutocmdOptions (..),
                                                CommandArguments (..),
                                                CommandOption (CmdBang, CmdCount, CmdRange, CmdRegister, CmdSync),
@@ -126,14 +123,13 @@
                                                Synchronous (..))
 import qualified Neovim.Plugin.ConfigHelper   as ConfigHelper
 import           Neovim.Plugin.Internal       (NeovimPlugin (..), Plugin (..),
-                                               StatefulFunctionality(..), wrapPlugin)
+                                               wrapPlugin)
 import           Neovim.Plugin.Startup        (StartupConfig (..))
 import           Neovim.RPC.FunctionCall      (wait, wait', waitErr, waitErr')
 import           Neovim.Util                  (unlessM, whenM,
                                                withCustomEnvironment)
 import           System.Log.Logger            (Priority (..))
-import           Text.PrettyPrint.ANSI.Leijen (Doc, Pretty (..), text)
-
+import           Data.Text.Prettyprint.Doc.Render.Terminal (putDoc)
 -- Installation {{{1
 {- $installation
 
@@ -149,7 +145,7 @@
 If you are proficient with Haskell, it may be sufficient to point you at some of the
 important data structures and functions. So, I will do it here. If you need more
 assistance, please skip to the next section and follow the links for functions or data
-types you do no understand how to use. If you think that the documentation is lacking,
+types you do not understand how to use. If you think that the documentation is lacking,
 please create an issue on github (or even better, a pull request with a fix @;-)@).
 The code sections that describe new functionality are followed by the source code
 documentation of the used functions (and possibly a few more).
@@ -252,7 +248,7 @@
 import "Neovim"
 
 \-\- \| Neovim is not really good with big numbers, so we return a 'String' here.
-fibonacci :: 'Int' -> 'Neovim'' 'String'
+fibonacci :: 'Int' -> 'Neovim' env 'String'
 fibonacci n = 'return' . 'show' \$ fibs !! n
   where
     fibs :: [Integer]
@@ -270,8 +266,8 @@
 
 plugin :: 'Neovim' ('StartupConfig' 'NeovimConfig') () 'NeovimPlugin'
 plugin = 'wrapPlugin' Plugin
-    { 'exports'         = [ $('function'' 'fibonacci) 'Sync' ]
-    , 'statefulExports' = []
+    { 'environment' = ()
+    , 'exports'     = [ $('function'' 'fibonacci) 'Sync' ]
     }
 @
 
@@ -292,35 +288,27 @@
 that takes the @n@th element of the infinite list of Fibonacci numbers. Even though
 the definition is very concise and asthetically pleasing, the important part is the
 type signature for @fibonacci@. Similarly how @main :: IO ()@ works in normal Haskell
-programs, 'Neovim'' is the environment we need for plugins. Internally, it stores a
+programs, 'Neovim' is the environment we need for plugins. Internally, it stores a
 few things that are needed to communicate with neovim, but that shouldn't bother you
 too much. Simply remember that every plugin function must have a function signature
-whose last element is of type @'Neovim' r st something@. The result of @fibonacci@
+whose last element is of type @'Neovim' env something@. The result of @fibonacci@
 is 'String' because neovim cannot handle big numbers so well. :-)
 You can use any argument or result type as long as it is an instance of 'NvimObject'.
 
 The second part of of the puzzle, which is the definition of @plugin@
-in @~\/.config\/nvim\/lib\/Fibonacci.hs@, shows what a plugin is. It is essentially two
-lists of stateless and stateful functionality. A functionality can currently be one
-of three things: a function, a command and an autocmd in the context of vim
-terminology. In the end, all of those functionalities map to a function at the side
+in @~\/.config\/nvim\/lib\/Fibonacci.hs@, shows what a plugin is. It is essentially
+an environment that and a list of functions, commands or autocommands in the context of vim
+terminology. In the end, all of those things map to a function at the side
 of /nvim-hs/. If you really want to know what the distinction between those is, you
 have to consult the @:help@ pages of neovim (e.g. @:help :function@, @:help :command@
-and @:help :autocmd@). What's relevant from the side of /nvim-hs/ is the distinction
-between __stateful__ and __stateless__. A stateless function can be called at any
-time and it does not share any of its internals with other functions. A stateful
-function on the other hand can share a well-defined amount of state with other
-functions and in the next section I will show you a simple example for that.
-Anyhow, if you take a look at the type alias for 'Neovim', you notice the two
-type variables @r@ and @st@. These can be accessed with different semantics
-each. A value of type @r@ can only be read. It is more or less a static value
-you can query with 'ask' or 'asks' if you
-are inside a 'Neovim' environment. The value @st@ can be changed and those
-changes will be available to other functions which run in the same environment.
-You can get the current value with 'get', you can replace an existing value with
-'put' and you can also apply a function to the current state with 'modify'.
-Notice how 'Neovim'' is just a specialization of 'Neovim' with its @r@ and
-@st@ set to @()@.
+and @:help :autocmd@). What's relevant from the side of /nvim-hs/ is the environment.
+The environment is a data type that is avaiable to all exported functions of your
+plugin. This example does not make use of anything of that environment, so
+we used '()', also known as unit, as our environment. The definition of
+@fibonacci@ uses a type variable @env@ as it does not access the environment and
+can handly any environment. If you want to access the environment, you can call
+'ask' or 'asks' if you are inside a 'Neovim' environment. An example that shows
+you how to use it can be found in a later chapter.
 
 Now to the magical part: @\$('function'' 'fibonacci)@. This is a so called
 Template Haskell splice and this is why you need
@@ -367,15 +355,47 @@
 
 import "Neovim"
 
-\-\- | Neovim isn't so good with big numbers here either.
-nextRandom :: 'Neovim' r ['Int16'] 'Int16'
+import System.Random (newStdGen, randoms)
+import UnliftIO.STM  (TVar, atomically, readTVar, modifyTVar, newTVarIO)
+
+-- You may want to define a type alias for your plugin, so that if you change
+-- your environment, you don't have to change all type signatures.
+--
+-- If I were to write a real plugin, I would probably also create a data type
+-- instead of directly using a TVar here.
+--
+type MyNeovim a = Neovim ('TVar' ['Int16']) a
+
+-- This function will create an initial environment for our random number
+-- generator. Note that the return type is the type of our environment.
+randomNumbers :: Neovim startupEnv (TVar [Int16])
+randomNumbers = do
+    g <- liftIO newStdGen -- Create a new seed for a pseudo random number generator
+    newTVarIO (randoms g) -- Put an infinite list of random numbers into a TVar
+
+-- | Get the next random number and update the state of the list.
+nextRandom :: MyNeovim 'Int16'
 nextRandom = do
-    r <- 'gets' 'head' -- get the head of the infinite random number list
-    'modify' 'tail'    -- set the list to its tail
-    'return' r
+    tVarWithRandomNumbers <- 'ask'
+    atomically $ do
+        -- pick the head of our list of random numbers
+        r <- 'head' <$> 'readTVar' tVarWithRandomNumbers
 
-setNextRandom :: 'Int16' -> 'Neovim' r ['Int16'] ()
-setNextRandom n = 'modify' (n:) -- cons to the front of the infinite list
+        -- Since we do not want to return the same number all over the place
+        -- remove the head of our list of random numbers
+        modifyTVar tVarWithRandomNumbers 'tail'
+
+        'return' r
+
+
+-- | You probably don't want this in a random number generator, but this shows
+-- hoy you can edit the state of a stateful plugin.
+setNextRandom :: 'Int16' -> MyNeovim ()
+setNextRandom n = do
+    tVarWithRandomNumbers <- 'ask'
+
+    -- cons n to the front of the infinite list
+    atomically $ modifyTVar tVarWithRandomNumbers (n:)
 @
 
 File @~\/.config\/nvim\/lib\/Random.hs@:
@@ -390,18 +410,13 @@
 
 plugin :: 'Neovim' ('StartupConfig' 'NeovimConfig') () 'NeovimPlugin'
 plugin = do
-    g <- 'liftIO' 'newStdGen'         -- initialize with a random seed
-    let randomNumbers = 'randoms' g -- an infinite list of random numbers
+    env <- randomNumbers
     'wrapPlugin' 'Plugin'
-        { 'exports'         = []
-        , 'statefulExports' =  [ 'StatefulFunctionality'
-            { readOnly = ()
-            , writable = randomNumbers
-            , functionalities =
-                [ $('function'' 'nextRandom) 'Sync'
-                , $('function' \"SetNextRandom\" 'setNextRandom) 'Async'
-                ]
-            }]
+        { environment = env
+        , 'exports'         =
+          [ $('function'' 'nextRandom) 'Sync'
+          , $('function' \"SetNextRandom\" 'setNextRandom) 'Async'
+          ]
         }
 @
 
@@ -455,7 +470,7 @@
 to rename it.
 
 @
-inspectBuffer :: 'Neovim' r st ()
+inspectBuffer :: 'Neovim' env ()
 inspectBuffer = do
     cb <- 'vim_get_current_buffer'
     isValid <- 'buffer_is_valid' cb
diff --git a/library/Neovim/API/Parser.hs b/library/Neovim/API/Parser.hs
--- a/library/Neovim/API/Parser.hs
+++ b/library/Neovim/API/Parser.hs
@@ -19,20 +19,21 @@
 import           Neovim.Classes
 
 import           Control.Applicative
-import           Control.Exception.Lifted
 import           Control.Monad.Except
-import qualified Data.ByteString              as B
-import           Data.Map                     (Map)
-import qualified Data.Map                     as Map
+import qualified Data.ByteString                           as B
+import           Data.Map                                  (Map)
+import qualified Data.Map                                  as Map
 import           Data.MessagePack
-import           Data.Monoid
 import           Data.Serialize
-import           System.IO                    (hClose)
+import           Neovim.Compat.Megaparsec                  as P
+import           System.IO                                 (hClose)
 import           System.Process
-import           Neovim.Compat.Megaparsec     as P
-import           Text.PrettyPrint.ANSI.Leijen (Doc)
-import qualified Text.PrettyPrint.ANSI.Leijen as P
+import           UnliftIO.Exception                        (SomeException,
+                                                            bracket, catch)
 
+import           Data.Text.Prettyprint.Doc                 (Doc, Pretty(..), (<+>))
+import           Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle)
+
 import           Prelude
 
 data NeovimType = SimpleType String
@@ -79,10 +80,10 @@
     deriving (Show)
 
 -- | Run @nvim --api-info@ and parse its output.
-parseAPI :: IO (Either Doc NeovimAPI)
-parseAPI = either (Left . P.text) extractAPI <$> (decodeAPI `catch` readFromAPIFile)
+parseAPI :: IO (Either (Doc AnsiStyle) NeovimAPI)
+parseAPI = either (Left . pretty) extractAPI <$> (decodeAPI `catch` readFromAPIFile)
 
-extractAPI :: Object -> Either Doc NeovimAPI
+extractAPI :: Object -> Either (Doc AnsiStyle) NeovimAPI
 extractAPI apiObj = fromObject apiObj >>= \apiMap -> NeovimAPI
     <$> extractErrorTypes apiMap
     <*> extractCustomTypes apiMap
@@ -111,21 +112,21 @@
         terminateProcess ph
 
 
-oLookup :: (NvimObject o) => String -> Map String Object -> Either Doc o
+oLookup :: (NvimObject o) => String -> Map String Object -> Either (Doc AnsiStyle) o
 oLookup qry = maybe throwErrorMessage fromObject . Map.lookup qry
   where
-    throwErrorMessage = throwError . P.text $ "No entry for: " <> show qry
+      throwErrorMessage = throwError $ "No entry for:" <+> pretty qry
 
 
-oLookupDefault :: (NvimObject o) => o -> String -> Map String Object -> Either Doc o
+oLookupDefault :: (NvimObject o) => o -> String -> Map String Object -> Either (Doc AnsiStyle) o
 oLookupDefault d qry m = maybe (return d) fromObject $ Map.lookup qry m
 
 
-extractErrorTypes :: Map String Object -> Either Doc [(String, Int64)]
+extractErrorTypes :: Map String Object -> Either (Doc AnsiStyle) [(String, Int64)]
 extractErrorTypes objAPI = extractTypeNameAndID =<< oLookup "error_types" objAPI
 
 
-extractTypeNameAndID :: Object -> Either Doc [(String, Int64)]
+extractTypeNameAndID :: Object -> Either (Doc AnsiStyle) [(String, Int64)]
 extractTypeNameAndID m = do
     types <- Map.toList <$> fromObject m
     forM types $ \(errName, idMap) -> do
@@ -133,21 +134,21 @@
         return (errName,i)
 
 
-extractCustomTypes :: Map String Object -> Either Doc [(String, Int64)]
+extractCustomTypes :: Map String Object -> Either (Doc AnsiStyle) [(String, Int64)]
 extractCustomTypes objAPI = extractTypeNameAndID =<< oLookup "types" objAPI
 
 
-extractFunctions :: Map String Object -> Either Doc [NeovimFunction]
+extractFunctions :: Map String Object -> Either (Doc AnsiStyle) [NeovimFunction]
 extractFunctions objAPI = mapM extractFunction =<< oLookup "functions" objAPI
 
 
-toParameterlist :: [(String, String)] -> Either Doc [(NeovimType, String)]
+toParameterlist :: [(String, String)] -> Either (Doc AnsiStyle) [(NeovimType, String)]
 toParameterlist ps = forM ps $ \(t,n) -> do
     t' <- parseType t
     return (t', n)
 
 
-extractFunction :: Map String Object -> Either Doc NeovimFunction
+extractFunction :: Map String Object -> Either (Doc AnsiStyle) NeovimFunction
 extractFunction funDefMap = NeovimFunction
     <$> (oLookup "name" funDefMap)
     <*> (oLookup "parameters" funDefMap >>= toParameterlist)
@@ -156,8 +157,8 @@
     <*> (oLookup "return_type" funDefMap >>= parseType)
 
 
-parseType :: String -> Either Doc NeovimType
-parseType s = either (throwError . P.text . show) return $ parse (pType <* eof) s s
+parseType :: String -> Either (Doc AnsiStyle) NeovimType
+parseType s = either (throwError . pretty . show) return $ parse (pType <* eof) s s
 
 
 pType :: P.Parser NeovimType
diff --git a/library/Neovim/API/TH.hs b/library/Neovim/API/TH.hs
--- a/library/Neovim/API/TH.hs
+++ b/library/Neovim/API/TH.hs
@@ -21,7 +21,7 @@
     , autocmd
     , defaultAPITypeToHaskellTypeMap
 
-    , module Control.Exception.Lifted
+    , module UnliftIO.Exception
     , module Neovim.Classes
     , module Data.Data
     , module Data.MessagePack
@@ -44,7 +44,6 @@
 import           Control.Arrow (first)
 import           Control.Concurrent.STM   (STM)
 import           Control.Exception
-import           Control.Exception.Lifted
 import           Control.Monad
 import           Data.ByteString          (ByteString)
 import           Data.ByteString.UTF8     (fromString)
@@ -57,7 +56,8 @@
 import           Data.Monoid
 import qualified Data.Set                 as Set
 import           Data.Text                (Text)
-import           Text.PrettyPrint.ANSI.Leijen (text, (<+>), Doc)
+import           Data.Text.Prettyprint.Doc ((<+>), Doc, viaShow, Pretty(..))
+import           UnliftIO.Exception
 
 import           Prelude
 
@@ -148,8 +148,8 @@
     let withDeferred | async nf    = appT [t|STM|]
                      | otherwise   = id
 
-        withException | canFail nf = appT [t|Either NeovimException|]
-                      | otherwise  = id
+        withException' | canFail nf = appT [t|Either NeovimException|]
+                       | otherwise  = id
 
         callFns | async nf && canFail nf = [ [|acall|] ]
                 | async nf               = [ [|acall'|] ]
@@ -160,13 +160,13 @@
         toObjVar v = [|toObject $(varE v)|]
 
 
-    retTypes <- let (r,st) = (mkName "r", mkName "st")
+    retTypes <- let env = (mkName "env")
                     createSig retTypeFun =
-                        forallT [PlainTV r, PlainTV st] (return [])
-                        . appT ([t|Neovim $(varT r) $(varT st) |])
+                        forallT [PlainTV env] (return [])
+                        . appT ([t|Neovim $(varT env) |])
                         . withDeferred . retTypeFun
                         . apiTypeToHaskellType typeMap $ returnType nf
-                in mapM createSig [ withException, id ]
+                in mapM createSig [ withException', id ]
 
     vars <- mapM (\(t,n) -> (,) <$> apiTypeToHaskellType typeMap t
                                 <*> newName n)
@@ -252,9 +252,9 @@
             clause
                 [ varP o ]
                 (normalB [|throwError $
-                            text "Object is not convertible to:"
-                            <+> text n
-                            <+> text "Received:" <+> (text . show) $(varE o)|])
+                            pretty "Object is not convertible to:"
+                            <+> viaShow n
+                            <+> pretty "Received:" <+> viaShow $(varE o)|])
                 []
 
         toObjectClause :: Name -> Int64 -> Q Clause
@@ -309,7 +309,7 @@
 
 
 -- | Given a value of type 'Type', test whether it can be classified according
--- to the constructors of 'ArgType'.
+-- to the constructors of "ArgType".
 classifyArgType :: Type -> Q ArgType
 classifyArgType t = do
     set <- genStringTypesSet
@@ -481,7 +481,7 @@
     -- _ -> err "Wrong number of arguments"
     errorCase :: Q Match
     errorCase = match wildP
-        (normalB [|throw . ErrorMessage . text $ "Wrong number of arguments for function: "
+        (normalB [|throw . ErrorMessage . pretty $ "Wrong number of arguments for function: "
                         ++ $(litE (StringL (nameBase functionName))) |]) []
 
     -- [x,y] -> case pure add <*> fromObject x <*> fromObject y of ...
@@ -512,6 +512,6 @@
     failedEvaluation :: Q Match
     failedEvaluation = newName "e" >>= \e ->
         match (conP (mkName "Left") [varP e])
-              (normalB [|err ($(varE e) :: Doc)|])
+              (normalB [|err ($(varE e) :: Doc AnsiStyle)|])
               []
 
diff --git a/library/Neovim/Classes.hs b/library/Neovim/Classes.hs
--- a/library/Neovim/Classes.hs
+++ b/library/Neovim/Classes.hs
@@ -1,10 +1,10 @@
+{-# LANGUAGE CPP                   #-}
+{-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE FlexibleInstances     #-}
 {-# LANGUAGE LambdaCase            #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings     #-}
 {-# LANGUAGE RankNTypes            #-}
-{-# LANGUAGE FlexibleContexts      #-}
-{-# LANGUAGE CPP #-}
 #if __GLASGOW_HASKELL__ < 710
 {-# LANGUAGE OverlappingInstances  #-}
 #endif
@@ -25,36 +25,48 @@
     , Generic
     , docToObject
     , docFromObject
+    , docToText
 
+    , Doc
+    , AnsiStyle
+    , Pretty(..)
+    , (<+>)
     , module Data.Int
     , module Data.Word
     , module Control.DeepSeq
     ) where
 
-import           Neovim.Exceptions                 (NeovimException(..))
+import           Neovim.Exceptions                         (NeovimException (..))
 
 import           Control.Applicative
-import           Control.Arrow
+import           Control.Arrow                             ((***))
 import           Control.DeepSeq
-import           Control.Exception.Lifted     (throwIO)
 import           Control.Monad.Except
-import           Control.Monad.Base           (MonadBase(..))
-import           Data.ByteString              (ByteString)
-import           Data.Int                     (Int16, Int32, Int64, Int8)
-import qualified Data.Map.Strict              as SMap
+import           Data.ByteString                           (ByteString)
+import           Data.Int                                  (Int16, Int32, Int64,
+                                                            Int8)
+import qualified Data.Map.Strict                           as SMap
 import           Data.MessagePack
 import           Data.Monoid
-import           Data.Text                    as Text (Text)
-import           Data.Traversable             hiding (forM, mapM)
-import           Data.Word                    (Word, Word16, Word32, Word64,
-                                               Word8)
-import           GHC.Generics                 (Generic)
-import           Text.PrettyPrint.ANSI.Leijen (Doc, lparen,
-                                               rparen, text, displayS, renderCompact)
-import qualified Text.PrettyPrint.ANSI.Leijen as P
+import           Data.Text                                 as Text (Text)
+import           Data.Text.Prettyprint.Doc                 (Doc, Pretty (..),
+                                                            defaultLayoutOptions,
+                                                            layoutPretty, viaShow,
+                                                            (<+>))
+import qualified Data.Text.Prettyprint.Doc                 as P
+import           Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle,
+                                                            renderStrict)
+import           Data.Traversable                          hiding (forM, mapM)
+import           Data.Word                                 (Word, Word16,
+                                                            Word32, Word64,
+                                                            Word8)
+import           GHC.Generics                              (Generic)
 
-import qualified Data.ByteString.UTF8         as UTF8 (fromString, toString)
-import           Data.Text.Encoding           (decodeUtf8, encodeUtf8)
+import qualified Data.ByteString.UTF8                      as UTF8 (fromString,
+                                                                    toString)
+import           Data.Text.Encoding                        (decodeUtf8,
+                                                            encodeUtf8)
+import           UnliftIO.Exception                        (throwIO)
 
 import           Prelude
 
@@ -62,6 +74,9 @@
 infixr 5 +:
 
 -- | Convenient operator to create a list of 'Object' from normal values.
+-- @
+-- values +: of :+ different :+ types :+ can +: be +: combined +: this +: way +: []
+-- @
 (+:) :: (NvimObject o) => o -> [Object] -> [Object]
 o +: os = toObject o : os
 
@@ -69,13 +84,19 @@
 -- | Convert a 'Doc'-ument to a messagepack 'Object'. This is more a convenience
 -- method to transport error message from and to neovim. It generally does not
 -- hold that 'docToObject . docFromObject' = 'id'.
-docToObject :: Doc -> Object
-docToObject = toObject . flip displayS "" . renderCompact
+docToObject :: Doc AnsiStyle -> Object
+docToObject = ObjectString . encodeUtf8 . docToText
 
+
 -- | See 'docToObject'.
-docFromObject :: Object -> Either Doc Doc
-docFromObject o = P.pretty . UTF8.toString <$> fromObject o
+docFromObject :: Object -> Either (Doc AnsiStyle) (Doc AnsiStyle)
+docFromObject o = (P.viaShow :: Text -> Doc AnsiStyle) <$> fromObject o
 
+
+docToText :: Doc AnsiStyle -> Text
+docToText = renderStrict . layoutPretty defaultLayoutOptions
+
+
 -- | A generic vim dictionary is a simply a map from strings to objects.  This
 -- type alias is sometimes useful as a type annotation especially if the
 -- OverloadedStrings extension is enabled.
@@ -94,14 +115,14 @@
     fromObjectUnsafe :: Object -> o
     fromObjectUnsafe o = case fromObject o of
         Left e -> error . show $
-            text "Not the expected object:" P.<+> (text . show) o
-            P.<+> lparen P.<> e P.<> rparen
+            "Not the expected object:" <+> P.viaShow o
+            <+> P.lparen <> e <> P.rparen
         Right obj -> obj
 
-    fromObject :: Object -> Either Doc o
+    fromObject :: Object -> Either (Doc AnsiStyle) o
     fromObject = return . fromObjectUnsafe
 
-    fromObject' :: (MonadBase IO io) => Object -> io o
+    fromObject' :: (MonadIO io) => Object -> io o
     fromObject' = either (throwIO . ErrorMessage) return . fromObject
 
 
@@ -110,7 +131,7 @@
     toObject _           = ObjectNil
 
     fromObject ObjectNil = return ()
-    fromObject o         = throwError . text $ "Expected ObjectNil, but got " <> show o
+    fromObject o         = throwError $ "Expected ObjectNil, but got" <+> P.viaShow o
 
 
 -- We may receive truthy values from neovim, so we should be more forgiving
@@ -136,7 +157,8 @@
     fromObject (ObjectFloat o)  = return $ realToFrac o
     fromObject (ObjectInt o)    = return $ fromIntegral o
     fromObject (ObjectUInt o)   = return $ fromIntegral o
-    fromObject o                = throwError . text $ "Expected ObjectDouble, but got " <> show o
+    fromObject o                = throwError $ "Expected ObjectDouble, but got"
+                                                <+> viaShow o
 
 
 instance NvimObject Integer where
@@ -146,7 +168,7 @@
     fromObject (ObjectUInt o)   = return $ toInteger o
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected ObjectInt, but got " <> show o
+    fromObject o                = throwError $ "Expected ObjectInt, but got" <+> viaShow o
 
 
 instance NvimObject Int64 where
@@ -156,7 +178,7 @@
     fromObject (ObjectUInt o)   = return $ fromIntegral o
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Int32 where
@@ -166,7 +188,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Int16 where
@@ -176,7 +198,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Int8 where
@@ -186,7 +208,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Word where
@@ -196,7 +218,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Word64 where
@@ -206,7 +228,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Word32 where
@@ -216,7 +238,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Word16 where
@@ -226,7 +248,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Word8 where
@@ -236,7 +258,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance NvimObject Int where
@@ -246,7 +268,7 @@
     fromObject (ObjectUInt i)   = return $ fromIntegral i
     fromObject (ObjectDouble o) = return $ round o
     fromObject (ObjectFloat o)  = return $ round o
-    fromObject o                = throwError . text $ "Expected any Integer value, but got " <> show o
+    fromObject o                = throwError $ "Expected any Integer value, but got" <+> viaShow o
 
 
 instance {-# OVERLAPPING #-} NvimObject [Char] where
@@ -254,21 +276,21 @@
 
     fromObject (ObjectBinary o) = return $ UTF8.toString o
     fromObject (ObjectString o) = return $ UTF8.toString o
-    fromObject o                = throwError . text $ "Expected ObjectString, but got " <> show o
+    fromObject o                = throwError $ "Expected ObjectString, but got" <+> viaShow o
 
 
 instance {-# OVERLAPPABLE #-} NvimObject o => NvimObject [o] where
     toObject                    = ObjectArray . map toObject
 
     fromObject (ObjectArray os) = mapM fromObject os
-    fromObject o                = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o                = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 instance NvimObject o => NvimObject (Maybe o) where
     toObject = maybe ObjectNil toObject
 
     fromObject ObjectNil = return Nothing
-    fromObject o = either throwError (return . Just) $ fromObject o
+    fromObject o         = either throwError (return . Just) $ fromObject o
 
 
 
@@ -285,7 +307,7 @@
                                       return $ Left l
 
                                   Left e2 ->
-                                      throwError $ e1 P.<+> text "--" P.<+> e2
+                                      throwError $ e1 <+> "--" <+> e2
 
 
 instance (Ord key, NvimObject key, NvimObject val)
@@ -299,7 +321,7 @@
                     . (fromObject *** fromObject))
                 . SMap.toList) om
 
-    fromObject o = throwError . text $ "Expected ObjectMap, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectMap, but got" <+> viaShow o
 
 
 instance NvimObject Text where
@@ -307,7 +329,7 @@
 
     fromObject (ObjectBinary o) = return $ decodeUtf8 o
     fromObject (ObjectString o) = return $ decodeUtf8 o
-    fromObject o                = throwError . text $ "Expected ObjectBinary, but got " <> show o
+    fromObject o                = throwError $ "Expected ObjectBinary, but got" <+> viaShow o
 
 
 instance NvimObject ByteString where
@@ -315,7 +337,7 @@
 
     fromObject (ObjectBinary o) = return o
     fromObject (ObjectString o) = return o
-    fromObject o                = throwError . text $ "Expected ObjectBinary, but got " <> show o
+    fromObject o                = throwError $ "Expected ObjectBinary, but got" <+> viaShow o
 
 
 instance NvimObject Object where
@@ -332,7 +354,7 @@
     fromObject (ObjectArray [o1, o2]) = (,)
         <$> fromObject o1
         <*> fromObject o2
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 instance (NvimObject o1, NvimObject o2, NvimObject o3) => NvimObject (o1, o2, o3) where
     toObject (o1, o2, o3) = ObjectArray $ [toObject o1, toObject o2, toObject o3]
@@ -341,7 +363,7 @@
         <$> fromObject o1
         <*> fromObject o2
         <*> fromObject o3
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 instance (NvimObject o1, NvimObject o2, NvimObject o3, NvimObject o4) => NvimObject (o1, o2, o3, o4) where
@@ -352,7 +374,7 @@
         <*> fromObject o2
         <*> fromObject o3
         <*> fromObject o4
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 instance (NvimObject o1, NvimObject o2, NvimObject o3, NvimObject o4, NvimObject o5) => NvimObject (o1, o2, o3, o4, o5) where
@@ -364,7 +386,7 @@
         <*> fromObject o3
         <*> fromObject o4
         <*> fromObject o5
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 instance (NvimObject o1, NvimObject o2, NvimObject o3, NvimObject o4, NvimObject o5, NvimObject o6) => NvimObject (o1, o2, o3, o4, o5, o6) where
@@ -377,7 +399,7 @@
         <*> fromObject o4
         <*> fromObject o5
         <*> fromObject o6
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 instance (NvimObject o1, NvimObject o2, NvimObject o3, NvimObject o4, NvimObject o5, NvimObject o6, NvimObject o7) => NvimObject (o1, o2, o3, o4, o5, o6, o7) where
@@ -391,7 +413,7 @@
         <*> fromObject o5
         <*> fromObject o6
         <*> fromObject o7
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 instance (NvimObject o1, NvimObject o2, NvimObject o3, NvimObject o4, NvimObject o5, NvimObject o6, NvimObject o7, NvimObject o8) => NvimObject (o1, o2, o3, o4, o5, o6, o7, o8) where
@@ -406,7 +428,7 @@
         <*> fromObject o6
         <*> fromObject o7
         <*> fromObject o8
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 instance (NvimObject o1, NvimObject o2, NvimObject o3, NvimObject o4, NvimObject o5, NvimObject o6, NvimObject o7, NvimObject o8, NvimObject o9) => NvimObject (o1, o2, o3, o4, o5, o6, o7, o8, o9) where
@@ -422,7 +444,7 @@
         <*> fromObject o7
         <*> fromObject o8
         <*> fromObject o9
-    fromObject o = throwError . text $ "Expected ObjectArray, but got " <> show o
+    fromObject o = throwError $ "Expected ObjectArray, but got" <+> viaShow o
 
 
 -- 1}}}
diff --git a/library/Neovim/Config.hs b/library/Neovim/Config.hs
--- a/library/Neovim/Config.hs
+++ b/library/Neovim/Config.hs
@@ -23,7 +23,7 @@
 -- the fields' documentation for what you possibly want to change. Also, the
 -- tutorial in the "Neovim" module should get you started.
 data NeovimConfig = Config
-    { plugins      :: [Neovim (StartupConfig NeovimConfig) () NeovimPlugin]
+    { plugins      :: [Neovim (StartupConfig NeovimConfig) NeovimPlugin]
     -- ^ The list of plugins. The IO type inside the list allows the plugin
     -- author to run some arbitrary startup code before creating a value of
     -- type 'NeovimPlugin'.
diff --git a/library/Neovim/Context.hs b/library/Neovim/Context.hs
--- a/library/Neovim/Context.hs
+++ b/library/Neovim/Context.hs
@@ -14,13 +14,12 @@
     newUniqueFunctionName,
 
     Neovim,
-    Neovim',
     NeovimException(..),
+    exceptionToDoc,
     FunctionMap,
     FunctionMapEntry,
     mkFunctionMap,
     runNeovim,
-    forkNeovim,
     err,
     errOnInvalidResult,
     restart,
@@ -33,6 +32,10 @@
     put,
     modify,
 
+    Doc,
+    AnsiStyle,
+    docToText,
+
     throwError,
     module Control.Monad.IO.Class,
 #if __GLASGOW_HASKELL__ <= 710
@@ -43,10 +46,9 @@
 
 import           Neovim.Classes
 import           Neovim.Context.Internal      (FunctionMap, FunctionMapEntry,
-                                               Neovim, Neovim', forkNeovim,
-                                               mkFunctionMap,
+                                               Neovim, mkFunctionMap,
                                                newUniqueFunctionName, runNeovim)
-import           Neovim.Exceptions            (NeovimException (..))
+import           Neovim.Exceptions            (NeovimException (..), exceptionToDoc)
 
 import qualified Neovim.Context.Internal      as Internal
 
@@ -61,20 +63,19 @@
 import           Control.Monad.Reader
 import           Control.Monad.State
 import           Data.MessagePack             (Object)
-import           Text.PrettyPrint.ANSI.Leijen (Pretty (..))
 
 
 -- | @'throw'@ specialized to a 'Pretty' value.
-err :: Pretty err => err ->  Neovim r st a
-err = throw . ErrorMessage . pretty
+err :: Doc AnsiStyle -> Neovim env a
+err = throw . ErrorMessage
 
 
 errOnInvalidResult :: (NvimObject o)
-                   => Neovim r st (Either NeovimException Object)
-                   -> Neovim r st o
+                   => Neovim env (Either NeovimException Object)
+                   -> Neovim env o
 errOnInvalidResult a = a >>= \case
     Left o ->
-        (err . show) o
+        (err . exceptionToDoc) o
 
     Right o -> case fromObject o of
         Left e ->
@@ -84,12 +85,13 @@
             return x
 
 
+
 -- | Initiate a restart of the plugin provider.
-restart :: Neovim r st ()
+restart :: Neovim env ()
 restart = liftIO . flip putMVar Internal.Restart =<< Internal.asks' Internal.transitionTo
 
 
 -- | Initiate the termination of the plugin provider.
-quit :: Neovim r st ()
+quit :: Neovim env ()
 quit = liftIO . flip putMVar Internal.Quit =<< Internal.asks' Internal.transitionTo
 
diff --git a/library/Neovim/Context/Internal.hs b/library/Neovim/Context/Internal.hs
--- a/library/Neovim/Context/Internal.hs
+++ b/library/Neovim/Context/Internal.hs
@@ -4,6 +4,7 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE LambdaCase                 #-}
 {-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE OverloadedStrings          #-}
 {-# LANGUAGE ScopedTypeVariables        #-}
 {- |
 Module      :  Neovim.Context.Internal
@@ -21,75 +22,75 @@
     where
 
 import           Neovim.Classes
-import           Neovim.Exceptions            (NeovimException (..))
+import           Neovim.Exceptions                         (NeovimException (..),
+                                                            exceptionToDoc)
 import           Neovim.Plugin.Classes
-import           Neovim.Plugin.IPC            (SomeMessage)
+import           Neovim.Plugin.IPC                         (SomeMessage)
 
 import           Control.Applicative
-import           Control.Concurrent           (ThreadId, forkIO)
-import           Control.Concurrent           (MVar, newEmptyMVar)
-import           Control.Concurrent.STM
-import           Control.Exception            (ArithException, ArrayException,
-                                               ErrorCall, PatternMatchFail)
-import           Control.Monad.Base
-import           Control.Monad.Catch
+import           Control.Exception                         (ArithException,
+                                                            ArrayException,
+                                                            ErrorCall,
+                                                            PatternMatchFail)
 import           Control.Monad.Except
 import           Control.Monad.Reader
-import           Control.Monad.State
 import           Control.Monad.Trans.Resource
-import qualified Data.ByteString.UTF8         as U (fromString)
-import           Data.Map                     (Map)
-import qualified Data.Map                     as Map
-import           Data.MessagePack             (Object)
+import qualified Data.ByteString.UTF8                      as U (fromString)
+import           Data.Map                                  (Map)
+import qualified Data.Map                                  as Map
+import           Data.MessagePack                          (Object)
 import           System.Log.Logger
-import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
+import           UnliftIO
 
+import           Data.Text.Prettyprint.Doc                 (Doc, Pretty (..),
+                                                            viaShow)
+import           Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle)
+
 import           Prelude
 
 
 -- | This is the environment in which all plugins are initially started.
--- Stateless functions use '()' for the static configuration and the mutable
--- state and there is another type alias for that case: 'Neovim''.
 --
 -- Functions have to run in this transformer stack to communicate with neovim.
 -- If parts of your own functions dont need to communicate with neovim, it is
 -- good practice to factor them out. This allows you to write tests and spot
 -- errors easier. Essentially, you should treat this similar to 'IO' in general
 -- haskell programs.
-newtype Neovim r st a = Neovim
-    { unNeovim :: ResourceT (StateT st (ReaderT (Config r st) IO)) a }
-
-  deriving (Functor, Applicative, Monad, MonadIO, MonadState st
-           , MonadThrow, MonadCatch, MonadMask, MonadResource)
-
+newtype Neovim env a = Neovim
+    { unNeovim :: ResourceT (ReaderT (Config env) IO) a }
 
-instance MonadBase IO (Neovim r st) where
-    liftBase = liftIO
+  deriving (Functor, Applicative, Monad, MonadIO
+           , MonadThrow)
 
 
 -- | User facing instance declaration for the reader state.
-instance MonadReader r (Neovim r st) where
+instance MonadReader env (Neovim env) where
     ask = Neovim $ asks customConfig
     local f (Neovim a) = do
-        r <- Neovim $ ask
-        s <- get
-        fmap fst . liftIO $ runReaderT (runStateT (runResourceT a) s)
+        r <- Neovim ask
+        liftIO $ runReaderT (runResourceT a)
                     (r { customConfig = f (customConfig r)})
 
 
+instance MonadResource (Neovim env) where
+    liftResourceT m = Neovim $ liftResourceT m
+
+
+instance MonadUnliftIO (Neovim env) where
+    askUnliftIO = Neovim . withUnliftIO $ \x ->
+        return (UnliftIO (unliftIO x . unNeovim))
+
 -- | Same as 'ask' for the 'InternalConfig'.
-ask' :: Neovim r st (Config r st)
-ask' = Neovim $ ask
+ask' :: Neovim env (Config env)
+ask' = Neovim ask
 
 
 -- | Same as 'asks' for the 'InternalConfig'.
-asks' :: (Config r st -> a) -> Neovim r st a
+asks' :: (Config env -> a) -> Neovim env a
 asks' = Neovim . asks
 
--- | Convenience alias for @'Neovim' () ()@.
-type Neovim' = Neovim () ()
 
-exceptionHandlers :: [Handler IO (Either Doc a)]
+exceptionHandlers :: [Handler IO (Either (Doc ann) a)]
 exceptionHandlers =
     [ Handler $ \(_ :: ArithException)   -> ret "ArithException (e.g. division by 0)"
     , Handler $ \(_ :: ArrayException)   -> ret "ArrayException"
@@ -98,52 +99,35 @@
     , Handler $ \(_ :: SomeException)    -> ret "Unhandled exception"
     ]
   where
-    ret = return . Left . pretty
+    ret = return . Left
 
 -- | Initialize a 'Neovim' context by supplying an 'InternalEnvironment'.
 runNeovim :: NFData a
-          => Config r st
-          -> st
-          -> Neovim r st a
-          -> IO (Either Doc (a, st))
-runNeovim = runNeovimInternal (\(a,st) -> a `deepseq` return (a, st))
+          => Config env
+          -> Neovim env a
+          -> IO (Either (Doc AnsiStyle) a)
+runNeovim = runNeovimInternal (\a -> a `deepseq` return a)
 
-runNeovimInternal :: ((a, st) -> IO (a, st))
-                  -> Config r st
-                  -> st
-                  -> Neovim r st a
-                  -> IO (Either Doc (a, st))
-runNeovimInternal f r st (Neovim a) =
-    (try . runReaderT (runStateT (runResourceT a) st)) r >>= \case
+runNeovimInternal :: (a -> IO a)
+                  -> Config env
+                  -> Neovim env a
+                  -> IO (Either (Doc AnsiStyle) a)
+runNeovimInternal f r (Neovim a) =
+    (try . runReaderT (runResourceT a)) r >>= \case
         Left e -> case fromException e of
             Just e' ->
-                return . Left . pretty $ (e' :: NeovimException)
+                return . Left . exceptionToDoc $ (e' :: NeovimException)
 
             Nothing -> do
                 liftIO . errorM "Context" $ "Converting Exception to Error message: " ++ show e
-                (return . Left . text . show) e
+                (return . Left . viaShow) e
         Right res ->
             (Right <$> f res) `catches` exceptionHandlers
 
 
--- | Fork a neovim thread with the given custom config value and a custom
--- state. The result of the thread is discarded and only the 'ThreadId' is
--- returend immediately.
--- FIXME This function is pretty much unused and mayhave undesired effects,
---       namely that you cannot register autocmds in the forked thread.
-forkNeovim :: NFData a => ir -> ist -> Neovim ir ist a -> Neovim r st ThreadId
-forkNeovim r st a = do
-    cfg <- ask'
-    let threadConfig = cfg
-            { pluginSettings = Nothing -- <- slightly problematic
-            , customConfig = r
-            }
-    liftIO . forkIO . void $ runNeovim threadConfig st a
-
-
 -- | Create a new unique function name. To prevent possible name clashes, digits
 -- are stripped from the given suffix.
-newUniqueFunctionName :: Neovim r st FunctionName
+newUniqueFunctionName :: Neovim env FunctionName
 newUniqueFunctionName = do
     tu <- asks' uniqueCounter
     -- reverseing the integer string should distribute the first character more
@@ -156,19 +140,14 @@
 
 -- | This data type is used to dispatch a remote function call to the appopriate
 -- recipient.
-data FunctionType
-    = Stateless ([Object] -> Neovim' Object)
-    -- ^ 'Stateless' functions are simply executed with the sent arguments.
-
-    | Stateful (TQueue SomeMessage)
+newtype FunctionType = Stateful (TQueue SomeMessage)
     -- ^ 'Stateful' functions are handled within a special thread, the 'TQueue'
     -- is the communication endpoint for the arguments we have to pass.
 
 
 instance Pretty FunctionType where
     pretty = \case
-        Stateless _ -> blue $ text "\\os -> Neovim' o"
-        Stateful  _ -> green $ text "\\os -> Neovim r st o"
+        Stateful  _ -> "\\os -> Neovim env o"
 
 
 -- | Type of the values stored in the function map.
@@ -200,7 +179,7 @@
 --
 -- Note that you most probably do not want to change the fields prefixed with an
 -- underscore.
-data Config r st = Config
+data Config env = Config
     -- Global settings; initialized once
     { eventQueue        :: TQueue SomeMessage
     -- ^ A queue of messages that the event handler will propagate to
@@ -229,11 +208,11 @@
     -- it's appropriate targets.
 
     -- Local settings; intialized for each stateful component
-    , pluginSettings    :: Maybe (PluginSettings r st)
+    , pluginSettings    :: Maybe (PluginSettings env)
     -- ^ In a registered functionality this field contains a function (and
     -- possibly some context dependent values) to register new functionality.
 
-    , customConfig      :: r
+    , customConfig      :: env
     -- ^ Plugin author supplyable custom configuration. Queried on the
     -- user-facing side with 'ask' or 'asks'.
     }
@@ -243,30 +222,24 @@
 -- config.
 --
 -- Sets the 'pluginSettings' field to 'Nothing'.
-retypeConfig :: r -> st -> Config anotherR anotherSt -> Config r st
-retypeConfig r _ cfg = cfg { pluginSettings = Nothing, customConfig = r }
+retypeConfig :: env -> Config anotherEnv -> Config env
+retypeConfig r cfg = cfg { pluginSettings = Nothing, customConfig = r }
 
 
 -- | This GADT is used to share information between stateless and stateful
 -- plugin threads since they work fundamentally in the same way. They both
 -- contain a function to register some functionality in the plugin provider
 -- as well as some values which are specific to the one or the other context.
-data PluginSettings r st where
-    StatelessSettings
-        :: (FunctionalityDescription
-            -> ([Object] -> Neovim' Object)
-            -> Neovim' (Maybe FunctionMapEntry))
-        -> PluginSettings () ()
-
+data PluginSettings env where
     StatefulSettings
         :: (FunctionalityDescription
-            -> ([Object] -> Neovim r st Object)
+            -> ([Object] -> Neovim env Object)
             -> TQueue SomeMessage
-            -> TVar (Map FunctionName ([Object] -> Neovim r st Object))
-            -> Neovim r st (Maybe FunctionMapEntry))
+            -> TVar (Map FunctionName ([Object] -> Neovim env Object))
+            -> Neovim env (Maybe FunctionMapEntry))
         -> TQueue SomeMessage
-        -> TVar (Map FunctionName ([Object] -> Neovim r st Object))
-        -> PluginSettings r st
+        -> TVar (Map FunctionName ([Object] -> Neovim env Object))
+        -> PluginSettings env
 
 
 -- | Create a new 'InternalConfig' object by providing the minimal amount of
@@ -274,7 +247,7 @@
 --
 -- This function should only be called once per /nvim-hs/ session since the
 -- arguments are shared across processes.
-newConfig :: IO (Maybe String) -> IO r -> IO (Config r context)
+newConfig :: IO (Maybe String) -> IO env -> IO (Config env)
 newConfig ioProviderName r = Config
     <$> newTQueueIO
     <*> newEmptyMVar
@@ -293,7 +266,7 @@
     | Restart
     -- ^ Restart the plugin provider.
 
-    | Failure Doc
+    | Failure (Doc AnsiStyle)
     -- ^ The plugin provider failed to start or some other error occured.
 
     | InitSuccess
diff --git a/library/Neovim/Debug.hs b/library/Neovim/Debug.hs
--- a/library/Neovim/Debug.hs
+++ b/library/Neovim/Debug.hs
@@ -1,4 +1,5 @@
-{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE LambdaCase        #-}
+{-# LANGUAGE OverloadedStrings #-}
 {- |
 Module      :  Neovim.Debug
 Description :  Utilities to debug Neovim and nvim-hs functionality
@@ -26,22 +27,26 @@
 
 import           Neovim
 import           Neovim.Classes
-import           Neovim.Context               (runNeovim)
-import qualified Neovim.Context.Internal      as Internal
-import           Neovim.Log                   (disableLogger)
-import           Neovim.Main                  (CommandLineOptions (..),
-                                               runPluginProvider)
-import           Neovim.RPC.Common            (RPCConfig)
+import           Neovim.Context                            (runNeovim)
+import qualified Neovim.Context.Internal                   as Internal
+import           Neovim.Log                                (disableLogger)
+import           Neovim.Main                               (CommandLineOptions (..),
+                                                            runPluginProvider)
+import           Neovim.RPC.Common                         (RPCConfig)
 
-import           Control.Concurrent
-import           Control.Concurrent.STM
 import           Control.Monad
-import qualified Data.Map                     as Map
+import qualified Data.Map                                  as Map
 import           Foreign.Store
-import           System.IO                    (stdout)
-import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
-import qualified Text.PrettyPrint.ANSI.Leijen as Pretty
 
+import           UnliftIO.Async                            (Async, async,
+                                                            cancel)
+import           UnliftIO.Concurrent                       (putMVar, takeMVar)
+import           UnliftIO.STM
+
+import           Data.Text.Prettyprint.Doc                 (nest, softline,
+                                                            vcat, vsep)
+import           Data.Text.Prettyprint.Doc.Render.Terminal (putDoc)
+
 import           Prelude
 
 
@@ -54,9 +59,9 @@
 --
 -- Tip: If you run a terminal inside a neovim instance, then this variable is
 -- automatically set.
-debug :: r -> st -> Internal.Neovim r st a -> IO (Either Doc (a, st))
-debug r st a = disableLogger $ do
-    runPluginProvider def { env = True } Nothing transitionHandler Nothing
+debug :: env -> Internal.Neovim env a -> IO (Either (Doc AnsiStyle) a)
+debug env a = disableLogger $ do
+    runPluginProvider def { envVar = True } Nothing transitionHandler Nothing
   where
     transitionHandler tids cfg = takeMVar (Internal.transitionTo cfg) >>= \case
         Internal.Failure e ->
@@ -65,15 +70,14 @@
         Internal.InitSuccess -> do
             res <- Internal.runNeovimInternal
                 return
-                (cfg { Internal.customConfig = r, Internal.pluginSettings = Nothing })
-                st
+                (cfg { Internal.customConfig = env, Internal.pluginSettings = Nothing })
                 a
 
-            mapM_ killThread tids
+            mapM_ cancel tids
             return res
 
         _ ->
-            return . Left $ text "Unexpected transition state."
+            return . Left $ "Unexpected transition state."
 
 
 -- | Run a 'Neovim'' function.
@@ -83,8 +87,8 @@
 -- @
 --
 -- See documentation for 'debug'.
-debug' :: Internal.Neovim' a -> IO (Either Doc a)
-debug' a = fmap fst <$> debug () () a
+debug' :: Internal.Neovim () a -> IO (Either (Doc AnsiStyle) a)
+debug' a = debug () a
 
 
 -- | This function is intended to be run _once_ in a ghci session that to
@@ -111,11 +115,11 @@
 --
 develMain
     :: Maybe NeovimConfig
-    -> IO (Either Doc ([ThreadId], Internal.Config RPCConfig ()))
+    -> IO (Either (Doc AnsiStyle) [Async ()])
 develMain mcfg = lookupStore 0 >>= \case
     Nothing -> do
         x <- disableLogger $
-                runPluginProvider def { env = True } mcfg transitionHandler Nothing
+                runPluginProvider def { envVar = True } mcfg transitionHandler Nothing
         void $ newStore x
         return x
 
@@ -127,10 +131,9 @@
             return $ Left e
 
         Internal.InitSuccess -> do
-            transitionHandlerThread <- forkIO $ do
-                myTid <- myThreadId
-                void $ transitionHandler (myTid:tids) cfg
-            return $ Right (transitionHandlerThread:tids, cfg)
+            transitionHandlerThread <- async $ do
+                void $ transitionHandler (tids) cfg
+            return $ Right (transitionHandlerThread:tids)
 
         Internal.Quit -> do
             lookupStore 0 >>= \case
@@ -140,23 +143,23 @@
                 Just x ->
                     deleteStore x
 
-            mapM_ killThread tids
-            return . Left $ text "Quit develMain"
+            mapM_ cancel tids
+            return . Left $ "Quit develMain"
 
         _ ->
-            return . Left $ text "Unexpected transition state for develMain."
+            return . Left $ "Unexpected transition state for develMain."
 
 
 -- | Quit a previously started plugin provider.
-quitDevelMain :: Internal.Config r st -> IO ()
+quitDevelMain :: Internal.Config env -> IO ()
 quitDevelMain cfg = putMVar (Internal.transitionTo cfg) Internal.Quit
 
 
 -- | Restart the development plugin provider.
 restartDevelMain
-    :: Internal.Config RPCConfig ()
+    :: Internal.Config RPCConfig
     -> Maybe NeovimConfig
-    -> IO (Either Doc ([ThreadId], Internal.Config RPCConfig ()))
+    -> IO (Either (Doc AnsiStyle) [Async ()])
 restartDevelMain cfg mcfg = do
     quitDevelMain cfg
     develMain mcfg
@@ -164,23 +167,22 @@
 
 -- | Convenience function to run a stateless 'Neovim' function.
 runNeovim' :: NFData a
-           => Internal.Config r st -> Neovim' a -> IO (Either Doc a)
+           => Internal.Config env -> Neovim () a -> IO (Either (Doc AnsiStyle) a)
 runNeovim' cfg =
-    fmap (fmap fst) . runNeovim (Internal.retypeConfig () () cfg) ()
+    runNeovim (Internal.retypeConfig () cfg)
 
 
 -- | Print the global function map to the console.
-printGlobalFunctionMap :: Internal.Config r st -> IO ()
+printGlobalFunctionMap :: Internal.Config env -> IO ()
 printGlobalFunctionMap cfg = do
     es <- fmap Map.toList . atomically $ readTMVar (Internal.globalFunctionMap cfg)
-    let header = text "Printing global function map:"
+    let header = "Printing global function map:"
         funs   = map (\(fname, (d, f)) ->
                     nest 3 (pretty fname
-                    </> text "->"
-                    </> pretty d <+> text ":"
+                    <> softline <> "->"
+                    <> softline <> pretty d <+> ":"
                     <+> pretty f)) es
-    displayIO stdout . renderPretty 0.4 80 $
-        nest 2 (header <$$> vcat funs)
-            <$$> Pretty.empty
+    putDoc $
+        nest 2 $ vsep [header, vcat funs, mempty]
 
 
diff --git a/library/Neovim/Exceptions.hs b/library/Neovim/Exceptions.hs
--- a/library/Neovim/Exceptions.hs
+++ b/library/Neovim/Exceptions.hs
@@ -1,5 +1,6 @@
-{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE LambdaCase         #-}
+{-# LANGUAGE OverloadedStrings  #-}
 {- |
 Module      :  Neovim.Exceptions
 Description :  General Exceptions
@@ -13,17 +14,19 @@
 -}
 module Neovim.Exceptions
     ( NeovimException(..)
+    , exceptionToDoc
     ) where
 
-import           Control.Exception            (Exception)
-import           Data.MessagePack             (Object (..))
-import           Data.String                  (IsString (..))
-import           Data.Typeable                (Typeable)
-import           Text.PrettyPrint.ANSI.Leijen (Doc, Pretty (..))
+import           Control.Exception                         (Exception)
+import           Data.MessagePack                          (Object (..))
+import           Data.String                               (IsString (..))
+import           Data.Text.Prettyprint.Doc                 (Doc, (<+>), viaShow)
+import           Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle)
+import           Data.Typeable                             (Typeable)
 
 -- | Exceptions specific to /nvim-hs/.
 data NeovimException
-    = ErrorMessage Doc
+    = ErrorMessage (Doc AnsiStyle)
     -- ^ Simply error message that is passed to neovim. It should currently only
     -- contain one line of text.
     | ErrorResult Object
@@ -39,7 +42,11 @@
     fromString = ErrorMessage . fromString
 
 
-instance Pretty NeovimException where
-    pretty = \case
-        ErrorMessage s -> s
-        ErrorResult e -> pretty $ show e
+exceptionToDoc :: NeovimException -> Doc AnsiStyle
+exceptionToDoc = \case
+    ErrorMessage e ->
+        "Error message:" <+> e
+
+    ErrorResult o ->
+        "Result representing an error:" <+> viaShow o
+
diff --git a/library/Neovim/Main.hs b/library/Neovim/Main.hs
--- a/library/Neovim/Main.hs
+++ b/library/Neovim/Main.hs
@@ -33,6 +33,7 @@
 import           Options.Applicative
 import           System.IO               (stdin, stdout)
 import           System.SetEnv
+import           UnliftIO.Async          (Async, async, cancel)
 
 import           Prelude
 import           System.Environment
@@ -46,7 +47,7 @@
     Opt { providerName :: Maybe String
         , hostPort     :: Maybe (String, Int)
         , unix         :: Maybe FilePath
-        , env          :: Bool
+        , envVar       :: Bool
         , logOpts      :: Maybe (FilePath, Priority)
         }
 
@@ -56,7 +57,7 @@
             { providerName = Nothing
             , hostPort     = Nothing
             , unix         = Nothing
-            , env          = False
+            , envVar       = False
             , logOpts      = Nothing
             }
 
@@ -133,7 +134,7 @@
 -- 'Internal.Config' with the custom field set to 'RPCConfig'. These information
 -- can be used to properly clean up a session and then do something else.
 -- The transition handler is first called after the plugin provider has started.
-type TransitionHandler a = [ThreadId] -> Internal.Config RPCConfig () -> IO a
+type TransitionHandler a = [Async ()] -> Internal.Config RPCConfig -> IO a
 
 
 -- | This main functions can be used to create a custom executable without
@@ -164,7 +165,7 @@
     (_, Just fp) ->
         createHandle (UnixSocket fp) >>= \s -> run s s
 
-    _ | env os ->
+    _ | envVar os ->
         createHandle Environment >>= \s -> run s s
 
     _ ->
@@ -179,11 +180,11 @@
 
         conf <- Internal.newConfig (pure (providerName os)) newRPCConfig
 
-        ehTid <- forkIO $ runEventHandler
+        ehTid <- async $ runEventHandler
                             evHandlerHandle
                             conf { Internal.pluginSettings = Nothing }
 
-        srTid <- forkIO $ runSocketReader sockreaderHandle conf
+        srTid <- async $ runSocketReader sockreaderHandle conf
 
         ghcEnv <- forM ["GHC_PACKAGE_PATH","CABAL_SANDBOX_CONFIG"] $ \var -> do
             val <- lookupEnv var
@@ -191,11 +192,10 @@
             return (var, val)
         let startupConf = Internal.retypeConfig
                             (StartupConfig mDyreParams ghcEnv)
-                            ()
                             conf
         startPluginThreads startupConf allPlugins >>= \case
             Left e -> do
-                errorM logger $ "Error initializing plugins: " <> oneLineErrorMessage e
+                errorM logger $ "Error initializing plugins: " <> show (oneLineErrorMessage e)
                 putMVar (Internal.transitionTo conf) $ Internal.Failure e
                 transitionHandler [ehTid, srTid] conf
 
@@ -217,11 +217,11 @@
 
     Internal.Restart -> do
         debugM logger "Trying to restart nvim-hs"
-        mapM_ killThread threads
+        mapM_ cancel threads
         Dyre.relaunchMaster Nothing
 
     Internal.Failure e ->
-        errorM logger $ oneLineErrorMessage e
+        errorM logger . show $ oneLineErrorMessage e
 
     Internal.Quit ->
         return ()
diff --git a/library/Neovim/Plugin.hs b/library/Neovim/Plugin.hs
--- a/library/Neovim/Plugin.hs
+++ b/library/Neovim/Plugin.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE GADTs               #-}
 {-# LANGUAGE LambdaCase          #-}
+{-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE RecordWildCards     #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {- |
@@ -24,17 +25,16 @@
     CommandOption(..),
 
     addAutocmd,
-    addAutocmd',
 
-    registerInStatelessContext,
-    registerInStatefulContext,
+    registerPlugin,
     ) where
 
 import           Neovim.API.String
 import           Neovim.Classes
 import           Neovim.Config
 import           Neovim.Context
-import           Neovim.Context.Internal      (FunctionType (..), runNeovimInternal)
+import           Neovim.Context.Internal      (FunctionType (..),
+                                               runNeovimInternal)
 import qualified Neovim.Context.Internal      as Internal
 import           Neovim.Plugin.Classes        hiding (register)
 import           Neovim.Plugin.Internal
@@ -43,10 +43,7 @@
 import           Neovim.RPC.FunctionCall
 
 import           Control.Applicative
-import           Control.Concurrent           (ThreadId, forkIO)
-import           Control.Concurrent.STM
 import           Control.Monad                (foldM, void)
-import           Control.Monad.Catch          (SomeException, try)
 import           Control.Monad.Trans.Resource hiding (register)
 import           Data.ByteString              (ByteString)
 import           Data.ByteString.UTF8         (toString)
@@ -57,7 +54,10 @@
 import           Data.MessagePack
 import           Data.Traversable             (forM)
 import           System.Log.Logger
-import           Text.PrettyPrint.ANSI.Leijen (Doc)
+import           UnliftIO.Async               (Async, async, race)
+import           UnliftIO.Concurrent          (threadDelay)
+import           UnliftIO.Exception           (SomeException, try)
+import           UnliftIO.STM
 
 import           Prelude
 
@@ -69,36 +69,26 @@
 type StartupConfig = Plugin.StartupConfig NeovimConfig
 
 
-startPluginThreads :: Internal.Config StartupConfig ()
-                   -> [Neovim StartupConfig () NeovimPlugin]
-                   -> IO (Either Doc ([FunctionMapEntry],[ThreadId]))
-startPluginThreads cfg = fmap (fmap fst)
-    . runNeovimInternal return cfg ()
-    . foldM go ([], [])
+startPluginThreads :: Internal.Config StartupConfig
+                   -> [Neovim StartupConfig NeovimPlugin]
+                   -> IO (Either (Doc AnsiStyle) ([FunctionMapEntry],[Async ()]))
+startPluginThreads cfg = runNeovimInternal return cfg . foldM go ([], [])
   where
-    go :: ([FunctionMapEntry], [ThreadId])
-       -> Neovim StartupConfig () NeovimPlugin
-       -> Neovim StartupConfig () ([FunctionMapEntry], [ThreadId])
-    go acc iop = do
+    go :: ([FunctionMapEntry], [Async ()])
+       -> Neovim StartupConfig NeovimPlugin
+       -> Neovim StartupConfig ([FunctionMapEntry], [Async ()])
+    go (es, tids) iop = do
         NeovimPlugin p <- iop
-
-        (es, tids) <- foldl (\(es, tids) (es', tid) -> (es'++es, tid:tids)) acc
-            <$> mapM registerStatefulFunctionality (statefulExports p)
-
-        es' <- forM (exports p) $ \e -> do
-            registerInStatelessContext
-                (\_ -> return ())
-                (getDescription e)
-                (getFunction e)
+        (es', tid) <- registerStatefulFunctionality p
 
-        return $ (catMaybes es' ++ es, tids)
+        return $ (es ++ es', tid:tids)
 
 
 -- | Callthe vimL functions to define a function, command or autocmd on the
 -- neovim side. Returns 'True' if registration was successful.
 --
 -- Note that this does not have any effect on the side of /nvim-hs/.
-registerWithNeovim :: FunctionalityDescription -> Neovim anyConfig anyState Bool
+registerWithNeovim :: FunctionalityDescription -> Neovim anyEnv Bool
 registerWithNeovim = \case
     Function (F functionName) s -> do
         pName <- getProviderName
@@ -165,7 +155,7 @@
 
 -- | Return or retrive the provider name that the current instance is associated
 -- with on the neovim side.
-getProviderName :: Neovim r st (Either String Int)
+getProviderName :: Neovim env (Either String Int)
 getProviderName = do
     mp <- Internal.asks' Internal.providerName
     (liftIO . atomically . tryReadTMVar) mp >>= \case
@@ -176,9 +166,10 @@
             api <- nvim_get_api_info
             case api of
                 Right (i:_) -> do
-                    case fromObject i :: Either Doc Int of
+                    case fromObject i :: Either (Doc AnsiStyle) Int of
                       Left _ ->
-                          err "Expected an integral value as the first argument of nvim_get_api_info"
+                          err $ "Expected an integral value as the first"
+                               <+> "argument of nvim_get_api_info"
                       Right channelId -> do
                           liftIO . atomically . putTMVar mp . Right $ fromIntegral channelId
                           return . Right $ fromIntegral channelId
@@ -188,25 +179,18 @@
 
 
 registerFunctionality :: FunctionalityDescription
-                      -> ([Object] -> Neovim r st Object)
-                      -> Neovim r st (Maybe (FunctionMapEntry, Either (Neovim anyR anySt ()) ReleaseKey))
+                      -> ([Object] -> Neovim env Object)
+                      -> Neovim env (Maybe (FunctionMapEntry, Either (Neovim anyEnv ()) ReleaseKey))
 registerFunctionality d f = Internal.asks' Internal.pluginSettings >>= \case
     Nothing -> do
         liftIO $ errorM logger "Cannot register functionality in this context."
         return Nothing
 
-    Just (Internal.StatelessSettings reg) ->
-        reg d f >>= \case
-            Just e -> do
-                return $ Just (e, Left (freeFun (fst e)))
-            _ ->
-                return Nothing
-
     Just (Internal.StatefulSettings reg q m) ->
         reg d f q m >>= \case
             Just e -> do
                 -- Redefine fields so that it gains a new type
-                cfg <- Internal.retypeConfig () () <$> Internal.ask'
+                cfg <- Internal.retypeConfig () <$> Internal.ask'
                 rk <- fst <$> allocate (return ()) (free cfg (fst e))
                 return $ Just (e, Right rk)
 
@@ -228,26 +212,10 @@
             liftIO $ warningM logger "Free not implemented for functions."
 
 
-    free cfg = const . void . liftIO . runNeovimInternal return cfg () . freeFun
-
-
--- | Register a functoinality in a stateless context.
-registerInStatelessContext
-    :: (FunctionMapEntry -> Neovim r st ())
-    -> FunctionalityDescription
-    -> ([Object] -> Neovim' Object)
-    -> Neovim r st (Maybe FunctionMapEntry)
-registerInStatelessContext reg d f = registerWithNeovim d >>= \case
-    False ->
-        return Nothing
-
-    True -> do
-        let e = (d, Stateless f)
-        reg e
-        return $ Just e
+    free cfg = const . void . liftIO . runNeovimInternal return cfg . freeFun
 
 
-registerInGlobalFunctionMap :: FunctionMapEntry -> Neovim r st ()
+registerInGlobalFunctionMap :: FunctionMapEntry -> Neovim env ()
 registerInGlobalFunctionMap e = do
     liftIO . debugM logger $ "Adding function to global function map." ++ show (fst e)
     funMap <- Internal.asks' Internal.globalFunctionMap
@@ -256,14 +224,14 @@
         putTMVar funMap $ Map.insert ((name . fst) e) e m
     liftIO . debugM logger $ "Added function to global function map." ++ show (fst e)
 
-registerInStatefulContext
-    :: (FunctionMapEntry -> Neovim r st ())
+registerPlugin
+    :: (FunctionMapEntry -> Neovim env ())
     -> FunctionalityDescription
-    -> ([Object] -> Neovim r st Object)
+    -> ([Object] -> Neovim env Object)
     -> TQueue SomeMessage
-    -> TVar (Map FunctionName ([Object] -> Neovim r st Object))
-    -> Neovim r st (Maybe FunctionMapEntry)
-registerInStatefulContext reg d f q tm = registerWithNeovim d >>= \case
+    -> TVar (Map FunctionName ([Object] -> Neovim env Object))
+    -> Neovim env (Maybe FunctionMapEntry)
+registerPlugin reg d f q tm = registerWithNeovim d >>= \case
     True -> do
         let n = name d
             e = (d, Stateful q)
@@ -277,104 +245,99 @@
 
 -- | Register an autocmd in the current context. This means that, if you are
 -- currently in a stateful plugin, the function will be called in the current
--- thread and has access to the configuration and state of this thread. If you
--- need that information, but do not want to block the other functions in this
--- thread, you have to manually fork a thread and make the state you need
--- available there. If you don't care abou the state (or your function has been
--- appield to all the necessary state (e.g. a 'TVar' to share the rusult), then
--- you can also call 'addAutocmd'' which will register a stateless function that
--- only interacts with other threads by means of concurrency abstractions.
+-- thread and has access to the configuration and state of this thread. .
 --
 -- Note that the function you pass must be fully applied.
 --
--- Note beside: This function is equivalent to 'addAutocmd'' if called from a
--- stateless plugin thread.
 addAutocmd :: ByteString
            -- ^ The event to register to (e.g. BufWritePost)
            -> AutocmdOptions
-           -> (Neovim r st ())
+           -> (Neovim env ())
            -- ^ Fully applied function to register
-           -> Neovim r st (Maybe (Either (Neovim anyR anySt ()) ReleaseKey))
+           -> Neovim env (Maybe (Either (Neovim anyEnv ()) ReleaseKey))
            -- ^ A 'ReleaseKey' if the registration worked
 addAutocmd event (opts@AutocmdOptions{..}) f = do
     n <- newUniqueFunctionName
     fmap snd <$> registerFunctionality (Autocmd event n opts) (\_ -> toObject <$> f)
 
 
--- | Add a stateless autocmd.
---
--- See 'addAutocmd' for more details.
-addAutocmd' :: ByteString
-            -> AutocmdOptions
-            -> Neovim' ()
-            -> Neovim r st (Maybe ReleaseKey)
-addAutocmd' event opts f = do
-    n <- newUniqueFunctionName
-    void $ registerInStatelessContext
-                registerInGlobalFunctionMap
-                (Autocmd event n opts)
-                (\_ -> toObject <$> f)
-    return Nothing
-
-
 -- | Create a listening thread for events and add update the 'FunctionMap' with
 -- the corresponding 'TQueue's (i.e. communication channels).
 registerStatefulFunctionality
-    :: StatefulFunctionality r st
-    -> Neovim anyconfig anyState ([FunctionMapEntry], ThreadId)
-registerStatefulFunctionality (StatefulFunctionality r st fs) = do
-    q <- liftIO newTQueueIO
+    :: Plugin env
+    -> Neovim anyEnv ([FunctionMapEntry], Async ())
+registerStatefulFunctionality (Plugin { environment = env, exports = fs }) = do
+    messageQueue <- liftIO newTQueueIO
     route <- liftIO $ newTVarIO Map.empty
 
     cfg <- Internal.ask'
 
     let startupConfig = cfg
-            { Internal.customConfig = r
+            { Internal.customConfig = env
             , Internal.pluginSettings = Just $ Internal.StatefulSettings
-                (registerInStatefulContext (\_ -> return ())) q route
+                (registerPlugin (\_ -> return ())) messageQueue route
             }
-    res <- liftIO . runNeovimInternal return startupConfig st . forM fs $ \f ->
+    res <- liftIO . runNeovimInternal return startupConfig . forM fs $ \f ->
             registerFunctionality (getDescription f) (getFunction f)
     es <- case res of
-        Left e -> err e
-        Right (a,_) -> return $ catMaybes a
+        Left e  -> err e
+        Right a -> return $ catMaybes a
 
     let pluginThreadConfig = cfg
-            { Internal.customConfig = r
+            { Internal.customConfig = env
             , Internal.pluginSettings = Just $ Internal.StatefulSettings
-                (registerInStatefulContext registerInGlobalFunctionMap) q route
+                (registerPlugin registerInGlobalFunctionMap) messageQueue route
             }
 
-    tid <- liftIO . forkIO . void . runNeovimInternal return pluginThreadConfig st $ do
-                listeningThread q route
+    tid <- liftIO . async . void . runNeovim pluginThreadConfig $ do
+                listeningThread messageQueue route
 
     return (map fst es, tid) -- NB: dropping release functions/keys here
 
 
   where
     executeFunction
-        :: ([Object] -> Neovim r st Object)
+        :: ([Object] -> Neovim env Object)
         -> [Object]
-        -> Neovim r st (Either String Object)
+        -> Neovim env (Either String Object)
     executeFunction f args = try (f args) >>= \case
             Left e -> return . Left $ show (e :: SomeException)
             Right res -> return $ Right res
 
+    timeoutAndLog :: Word ->  FunctionName -> Neovim anyEnv String
+    timeoutAndLog seconds functionName = do
+        threadDelay (fromIntegral seconds * 1000 * 1000)
+        return . show $
+            pretty functionName <+> "has been aborted after"
+            <+> pretty seconds <+> "seconds"
+
+
     listeningThread :: TQueue SomeMessage
-                    -> TVar (Map FunctionName ([Object] -> Neovim r st Object))
-                    -> Neovim r st ()
+                    -> TVar (Map FunctionName ([Object] -> Neovim env Object))
+                    -> Neovim env ()
     listeningThread q route = do
         msg <- liftIO . atomically $ readTQueue q
 
         forM_ (fromMessage msg) $ \req@Request{..} -> do
             route' <- liftIO $ readTVarIO route
-            forM_ (Map.lookup reqMethod route') $ \f ->
-                respond req =<< executeFunction f reqArgs
+            forM_ (Map.lookup reqMethod route') $ \f -> do
+                respond req . either Left id =<< race
+                    (timeoutAndLog 10 reqMethod)
+                    (executeFunction f reqArgs)
 
         forM_ (fromMessage msg) $ \Notification{..} -> do
             route' <- liftIO $ readTVarIO route
             forM_ (Map.lookup notMethod route') $ \f ->
-                void $ executeFunction f notArgs
+                void . async $ do
+                    result <- either Left id <$> race
+                        (timeoutAndLog 600 notMethod)
+                        (executeFunction f notArgs)
+                    case result of
+                      Left message ->
+                          nvim_err_writeln' message
+                      Right _ ->
+                          return ()
+
 
         listeningThread q route
 
diff --git a/library/Neovim/Plugin/Classes.hs b/library/Neovim/Plugin/Classes.hs
--- a/library/Neovim/Plugin/Classes.hs
+++ b/library/Neovim/Plugin/Classes.hs
@@ -32,7 +32,6 @@
 import           Control.Applicative          hiding (empty)
 import           Control.Monad.Error.Class
 import           Data.ByteString              (ByteString)
-import           Data.ByteString.UTF8         (toString)
 import           Data.Char                    (isDigit)
 import           Data.Default
 import           Data.List                    (groupBy, sort)
@@ -41,8 +40,10 @@
 import           Data.MessagePack
 import           Data.String
 import           Data.Traversable             (sequence)
-import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
+import           Data.Text.Encoding           (decodeUtf8)
 
+import Data.Text.Prettyprint.Doc
+
 import           Prelude                      hiding (sequence)
 
 
@@ -55,7 +56,7 @@
 
 
 instance Pretty FunctionName where
-    pretty (F n) = blue . text $ toString n
+    pretty (F n) = pretty $ decodeUtf8 n
 
 
 -- | Functionality specific functional description entries.
@@ -97,13 +98,13 @@
 instance Pretty FunctionalityDescription where
     pretty = \case
         Function fname s ->
-            text "Function" <+> pretty s <+> pretty fname
+            "Function" <+> pretty s <+> pretty fname
 
         Command fname copts ->
-            text "Command" <+> pretty copts <+> pretty fname
+            "Command" <+> pretty copts <+> pretty fname
 
         Autocmd t fname aopts ->
-            text "Autocmd" <+> (text . toString) t
+            "Autocmd" <+> pretty (decodeUtf8 t)
                 <+> pretty aopts
                 <+> pretty fname
 
@@ -128,8 +129,8 @@
 
 instance Pretty Synchronous where
     pretty = \case
-        Async -> red  $ text "async"
-        Sync  -> blue $ text "sync"
+        Async -> "async"
+        Sync  -> "sync"
 
 
 instance IsString Synchronous where
@@ -200,19 +201,19 @@
             pretty s
 
         CmdRegister ->
-            text "\""
+            "\""
 
         CmdNargs n ->
-            text n
+            pretty n
 
         CmdRange rs ->
             pretty rs
 
         CmdCount c ->
-            text (show c)
+            pretty c
 
         CmdBang ->
-            text "!"
+            "!"
 
 
 instance IsString CommandOption where
@@ -273,8 +274,8 @@
             CmdNargs n    -> Just ("nargs"   , toObject n)
             _             -> Nothing
 
-    fromObject o = throwError . text $
-        "Did not expect to receive a CommandOptions object: " ++ show o
+    fromObject o = throwError $
+        "Did not expect to receive a CommandOptions object:" <+> viaShow o
 
 
 -- | Specification of a range that acommand can operate on.
@@ -295,13 +296,13 @@
 instance Pretty RangeSpecification where
     pretty = \case
         CurrentLine ->
-            empty
+            mempty
 
         WholeFile ->
-            text "%"
+            "%"
 
         RangeCount c ->
-            text $ show c
+            pretty c
 
 
 instance NvimObject RangeSpecification where
@@ -344,12 +345,12 @@
 instance Pretty CommandArguments where
     pretty CommandArguments{..} =
         cat $ catMaybes
-            [ (\b -> if b then (text "!") else empty) <$> bang
-            , (\(s,e) -> lparen <> (text . show) s <> comma
-                         <+> (text . show) e <> rparen)
+            [ (\b -> if b then "!" else mempty) <$> bang
+            , (\(s,e) -> lparen <> pretty s <> comma
+                         <+> pretty e <> rparen)
                 <$> range
-            , (text . show) <$> count
-            , (text . show) <$> register
+            , pretty <$> count
+            , pretty <$> register
             ]
 
 instance Default CommandArguments where
@@ -382,7 +383,8 @@
 
     fromObject ObjectNil = return def
     fromObject o =
-        throwError . text $ "Expected a map for CommandArguments object, but got: " ++ show o
+        throwError $ "Expected a map for CommandArguments object, but got: "
+                      <+> viaShow o
 
 
 -- | Options that can be used to register an autocmd. See @:h :autocmd@ or any
@@ -407,9 +409,9 @@
 
 instance Pretty AutocmdOptions where
     pretty AutocmdOptions{..} =
-        text acmdPattern
-            <+> text (if acmdNested then "nested" else "unnested")
-            <> maybe empty (\g -> empty <+> text g) acmdGroup
+        pretty acmdPattern
+            <+> if acmdNested then "nested" else "unnested"
+            <> maybe mempty (\g -> mempty <+> pretty g) acmdGroup
 
 
 instance Default AutocmdOptions where
@@ -428,8 +430,8 @@
             ] ++ catMaybes
             [ acmdGroup >>= \g -> return ("group", toObject g)
             ]
-    fromObject o = throwError . text $
-        "Did not expect to receive an AutocmdOptions object: " ++ show o
+    fromObject o = throwError  $
+        "Did not expect to receive an AutocmdOptions object: " <+> viaShow o
 
 -- | Conveniennce class to extract a name from some value.
 class HasFunctionName a where
diff --git a/library/Neovim/Plugin/ConfigHelper.hs b/library/Neovim/Plugin/ConfigHelper.hs
--- a/library/Neovim/Plugin/ConfigHelper.hs
+++ b/library/Neovim/Plugin/ConfigHelper.hs
@@ -25,33 +25,28 @@
 
 import           Config.Dyre.Paths                   (getPaths)
 import           Data.Default
+import           UnliftIO.STM
 
 
-plugin :: Neovim (StartupConfig NeovimConfig) () NeovimPlugin
+plugin :: Neovim (StartupConfig NeovimConfig) NeovimPlugin
 plugin = asks dyreParams >>= \case
     Nothing ->
-        wrapPlugin Plugin { exports = [], statefulExports = [] }
+        wrapPlugin Plugin { environment = (), exports = [] }
 
     Just params -> do
         ghcEnv <- asks ghcEnvironmentVariables
         (_, _, cfgFile, _, libsDir) <- liftIO $ getPaths params
+        emptyQuickfixList <- newTVarIO []
         wrapPlugin Plugin
-            { exports =
+            { environment = ConfigHelperEnv params ghcEnv emptyQuickfixList
+            , exports =
                 [ $(function' 'pingNvimhs) Sync
-                ]
-            , statefulExports =
-                [ StatefulFunctionality
-                    { readOnly = (params, ghcEnv)
-                    , writable = []
-                    , functionalities =
-                        [ $(autocmd 'recompileNvimhs) "BufWritePost" def
-                                { acmdPattern = cfgFile
-                                }
-                        , $(autocmd 'recompileNvimhs) "BufWritePost" def
-                                { acmdPattern = libsDir++"/*"
-                                }
-                        , $(command' 'restartNvimhs) [CmdSync Async, CmdBang, CmdRegister]
-                        ]
+                , $(autocmd 'recompileNvimhs) "BufWritePost" def
+                    { acmdPattern = cfgFile
                     }
+                , $(autocmd 'recompileNvimhs) "BufWritePost" def
+                    { acmdPattern = libsDir++"/*"
+                    }
+                , $(command' 'restartNvimhs) [CmdSync Async, CmdBang, CmdRegister]
                 ]
             }
diff --git a/library/Neovim/Plugin/ConfigHelper/Internal.hs b/library/Neovim/Plugin/ConfigHelper/Internal.hs
--- a/library/Neovim/Plugin/ConfigHelper/Internal.hs
+++ b/library/Neovim/Plugin/ConfigHelper/Internal.hs
@@ -26,29 +26,38 @@
 import           Config.Dyre             (Params)
 import           Config.Dyre.Compile
 import           Config.Dyre.Paths       (getPaths)
-import           Control.Applicative     hiding (many, (<|>))
+import           Control.Applicative     hiding ((<|>))
 import           Control.Monad           (void, forM_)
 import           Data.Char
 import           System.SetEnv
 import           System.Directory        (removeDirectoryRecursive)
+import           UnliftIO.STM
 
 import           Prelude
 
 
 -- | Simple function that will return @"Pong"@ if the plugin provider is
 -- running.
-pingNvimhs :: Neovim' String
+pingNvimhs :: Neovim env String
 pingNvimhs = return "Pong"
 
+data ConfigHelperEnv = ConfigHelperEnv
+    { dyreParameters       :: Params NeovimConfig
+    , environmentVariables :: [(String, Maybe String)]
+    , quickfixList         :: TVar [QuickfixListItem String]
+    }
 
 -- | Recompile the plugin provider and put comile errors in the quickfix list.
-recompileNvimhs :: Neovim (Params NeovimConfig, [(String, Maybe String)]) [QuickfixListItem String] ()
-recompileNvimhs = ask >>= \(cfg,env) -> withCustomEnvironment env $ do
-    mErrString <- liftIO (customCompile cfg >> getErrorString cfg)
-    let qs = maybe [] parseQuickfixItems mErrString
-    put qs
-    setqflist qs Replace
-    void $ vim_command "cwindow"
+recompileNvimhs :: Neovim ConfigHelperEnv ()
+recompileNvimhs = ask >>= \ConfigHelperEnv{..} ->
+    withCustomEnvironment environmentVariables $ do
+        mErrString <- liftIO $ do
+            customCompile dyreParameters
+            getErrorString dyreParameters
+        let qs = maybe [] parseQuickfixItems mErrString
+        atomically $ modifyTVar' quickfixList (const qs)
+        setqflist qs Replace
+        void $ vim_command "cwindow"
 
 
 -- | Note that restarting the plugin provider implies compilation because Dyre
@@ -59,11 +68,11 @@
 -- If you provide a bang to the command, the cache directory of /nvim-hs/ is
 -- forcibly removed.
 restartNvimhs :: CommandArguments
-              -> Neovim (Params NeovimConfig, [(String, Maybe String)]) [QuickfixListItem String] ()
+              -> Neovim ConfigHelperEnv ()
 restartNvimhs CommandArguments{..} = do
     case bang of
         Just True -> do
-            (_,_,_, cacheDir,_) <- liftIO . getPaths =<< asks fst
+            (_,_,_, cacheDir,_) <- liftIO . getPaths =<< asks dyreParameters
             liftIO $ removeDirectoryRecursive cacheDir
 
         _ ->
@@ -71,9 +80,8 @@
 
     recompileNvimhs
 
-    (_, env) <- ask
-    forM_ env $ \(var, val) -> liftIO $ do
-        maybe (unsetEnv var) (setEnv var) val
+    envVars <- asks environmentVariables
+    forM_ envVars $ \(var, val) -> liftIO $ maybe (unsetEnv var) (setEnv var) val
     restart
 
 -- Parsing {{{1
@@ -95,7 +103,7 @@
     e <- pSeverity
     desc <- try pShortDesrciption <|> pLongDescription
     return $ (quickfixListItem (Right f) (Left l))
-        { col = Just (c, True)
+        { col = VisualColumn c
         , text = desc
         , errorType = e
         }
diff --git a/library/Neovim/Plugin/IPC/Classes.hs b/library/Neovim/Plugin/IPC/Classes.hs
--- a/library/Neovim/Plugin/IPC/Classes.hs
+++ b/library/Neovim/Plugin/IPC/Classes.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE DeriveDataTypeable        #-}
 {-# LANGUAGE DeriveGeneric             #-}
 {-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE RecordWildCards           #-}
 {- |
 Module      :  Neovim.Plugin.IPC.Classes
@@ -27,17 +28,17 @@
     ) where
 
 import           Neovim.Classes
-import           Neovim.Plugin.Classes        (FunctionName)
+import           Neovim.Plugin.Classes     (FunctionName)
 
 import           Control.Concurrent.STM
-import           Data.Data                    (Typeable, cast)
-import           Data.Int                     (Int64)
+import           Data.Data                 (Typeable, cast)
+import           Data.Int                  (Int64)
 import           Data.MessagePack
-import           Data.Time                    (UTCTime, formatTime,
-                                               getCurrentTime)
-import           Data.Time.Locale.Compat      (defaultTimeLocale)
-import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
+import           Data.Time                 (UTCTime, formatTime, getCurrentTime)
+import           Data.Time.Locale.Compat   (defaultTimeLocale)
 
+import           Data.Text.Prettyprint.Doc (Pretty (..), nest, hardline, (<+>), (<>), viaShow)
+
 import           Prelude
 
 -- | Taken from xmonad and based on ideas in /An Extensible Dynamically-Typed
@@ -73,10 +74,10 @@
 
 instance Pretty FunctionCall where
     pretty (FunctionCall fname args _ t) =
-        nest 2 $ text "Function call for:" <+> pretty fname
-            <$$> text "Arguments:" <+> text (show args)
-            <$$> text "Timestamp:"
-                <+> (yellow . text . formatTime defaultTimeLocale "%H:%M:%S (%q)") t
+        nest 2 $ "Function call for:" <+> pretty fname
+            <> hardline <> "Arguments:" <+> viaShow args
+            <> hardline <> "Timestamp:"
+                <+> (viaShow . formatTime defaultTimeLocale "%H:%M:%S (%q)") t
 
 
 -- | A request is a data type containing the method to call, its arguments and
@@ -99,9 +100,9 @@
 
 instance Pretty Request where
     pretty Request{..} =
-        nest 2 $ text "Request" <+> (yellow . text . ('#':) . show) reqId
-            <$$> (text "Method:" <+> pretty reqMethod)
-            <$$> (text "Arguments:" <+> text (show reqArgs))
+        nest 2 $ "Request" <+> "#" <> pretty reqId
+            <> hardline <> "Method:" <+> pretty reqMethod
+            <> hardline <> "Arguments:" <+> viaShow reqArgs
 
 
 -- | A notification is similar to a 'Request'. It essentially does the same
@@ -124,7 +125,7 @@
 
 instance Pretty Notification where
     pretty Notification{..} =
-        nest 2 $ text "Notification"
-            <$$> (text "Method:" <+> pretty notMethod)
-            <$$> (text "Arguments:" <+> text (show notArgs))
+        nest 2 $ "Notification"
+            <> hardline <> "Method:" <+> pretty notMethod
+            <> hardline <> "Arguments:" <+> viaShow notArgs
 
diff --git a/library/Neovim/Plugin/Internal.hs b/library/Neovim/Plugin/Internal.hs
--- a/library/Neovim/Plugin/Internal.hs
+++ b/library/Neovim/Plugin/Internal.hs
@@ -12,7 +12,6 @@
 -}
 module Neovim.Plugin.Internal (
     ExportedFunctionality(..),
-    StatefulFunctionality(..),
     getFunction,
     getDescription,
     NeovimPlugin(..),
@@ -28,47 +27,38 @@
 
 -- | This data type is used in the plugin registration to properly register the
 -- functions.
-newtype ExportedFunctionality r st
-    = EF (FunctionalityDescription, [Object] -> Neovim r st Object)
+newtype ExportedFunctionality env
+    = EF (FunctionalityDescription, [Object] -> Neovim env Object)
 
 
 -- | Extract the description of an 'ExportedFunctionality'.
-getDescription :: ExportedFunctionality r st -> FunctionalityDescription
+getDescription :: ExportedFunctionality env -> FunctionalityDescription
 getDescription (EF (d,_)) = d
 
 
 -- | Extract the function of an 'ExportedFunctionality'.
-getFunction :: ExportedFunctionality r st -> [Object] -> Neovim r st Object
+getFunction :: ExportedFunctionality env -> [Object] -> Neovim env Object
 getFunction (EF (_, f)) = f
 
 
-instance HasFunctionName (ExportedFunctionality r st) where
+instance HasFunctionName (ExportedFunctionality env) where
     name = name . getDescription
 
 
--- | This datatype contains the initial state (mutable and immutable) for the
--- functionalities defined here.
-data StatefulFunctionality r st = StatefulFunctionality
-    { readOnly        :: r
-    , writable        :: st
-    , functionalities :: [ExportedFunctionality r st]
-    }
-
-
 -- | This data type contains meta information for the plugin manager.
 --
-data Plugin r st = Plugin
-    { exports         :: [ExportedFunctionality () ()]
-    , statefulExports :: [StatefulFunctionality r st]
+data Plugin env = Plugin
+    { environment :: env
+    , exports     :: [ExportedFunctionality env]
     }
 
 
 -- | 'Plugin' values are wraped inside this data type via 'wrapPlugin' so that
 -- we can put plugins in an ordinary list.
-data NeovimPlugin = forall r st. NeovimPlugin (Plugin r st)
+data NeovimPlugin = forall env. NeovimPlugin (Plugin env)
 
 
 -- | Wrap a 'Plugin' in some nice blankets, so that we can put them in a simple
 -- list.
-wrapPlugin :: Applicative m => Plugin r st -> m NeovimPlugin
+wrapPlugin :: Applicative m => Plugin env -> m NeovimPlugin
 wrapPlugin = pure . NeovimPlugin
diff --git a/library/Neovim/Quickfix.hs b/library/Neovim/Quickfix.hs
--- a/library/Neovim/Quickfix.hs
+++ b/library/Neovim/Quickfix.hs
@@ -26,8 +26,8 @@
 import           Neovim.API.String
 import           Neovim.Classes
 import           Neovim.Context
-import           Text.PrettyPrint.ANSI.Leijen (Doc)
-import qualified Text.PrettyPrint.ANSI.Leijen as P
+import           Data.Text.Prettyprint.Doc (Doc, viaShow, (<+>))
+import           Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle)
 
 import           Prelude
 
@@ -37,10 +37,28 @@
 setqflist :: (Monoid strType, NvimObject strType)
           => [QuickfixListItem strType]
           -> QuickfixAction
-          -> Neovim r st ()
+          -> Neovim env ()
 setqflist qs a =
     void $ vim_call_function "setqflist" $ qs +: a +: []
 
+data ColumnNumber
+    = VisualColumn Int
+    | ByteIndexColumn Int
+    | NoColumn
+  deriving (Eq, Ord, Show, Generic)
+
+
+instance NFData ColumnNumber
+
+data SignLocation strType
+    = LineNumber Int
+    | SearchPattern strType
+  deriving (Eq, Ord, Show, Generic)
+
+
+instance (NFData strType) => NFData (SignLocation strType)
+
+
 -- | Quickfix list item. The parameter names should mostly conform to those in
 -- @:h setqflist()@. Some fields are merged to explicitly state mutually
 -- exclusive elements or some other behavior of the fields.
@@ -55,7 +73,7 @@
     , lnumOrPattern :: Either Int strType
     -- ^ Line number or search pattern to locate the error.
 
-    , col           :: Maybe (Int, Bool)
+    , col           :: ColumnNumber
     -- ^ A tuple of a column number and a boolean indicating which kind of
     -- indexing should be used. 'True' means that the visual column should be
     -- used. 'False' means to use the byte index.
@@ -87,7 +105,7 @@
         Warning -> ObjectBinary "W"
         Error   -> ObjectBinary "E"
 
-    fromObject o = case fromObject o :: Either Doc String of
+    fromObject o = case fromObject o :: Either (Doc AnsiStyle) String of
         Right "W" -> return Warning
         Right "E" -> return Error
         _         -> return Error
@@ -102,7 +120,7 @@
 quickfixListItem bufferOrFile lineOrPattern = QFItem
     { bufOrFile = bufferOrFile
     , lnumOrPattern = lineOrPattern
-    , col = Nothing
+    , col = NoColumn
     , nr = Nothing
     , text = mempty
     , errorType = Error
@@ -121,27 +139,28 @@
                      lnumOrPattern
             , ("type", toObject errorType)
             , ("text", toObject text)
-            ] ++ catMaybes
-            [ (\n -> ("nr", toObject n)) <$> nr
-            , (\(c,_) -> ("col", toObject c)) <$> col
-            , (\(_,t) -> ("vcol", toObject t)) <$> col
+            ] ++ concat
+            [ case col of
+                NoColumn -> []
+                ByteIndexColumn i -> [ ("col", toObject i), ("vcol", toObject False) ]
+                VisualColumn i -> [ ("col", toObject i), ("vcol", toObject True) ]
             ]
 
     fromObject objectMap@(ObjectMap _) = do
         m <- fromObject objectMap
-        let l :: NvimObject o => ByteString -> Either Doc o
+        let l :: NvimObject o => ByteString -> Either (Doc AnsiStyle) o
             l key = case Map.lookup key m of
                 Just o -> fromObject o
-                Nothing -> throwError . P.text $ "Key not found."
+                Nothing -> throwError $ "Key not found."
         bufOrFile <- case (l "bufnr", l "filename") of
             (Right b, _) -> return $ Left b
             (_, Right f) -> return $ Right f
-            _           -> throwError . P.text $ "No buffer number or file name inside quickfix list item."
+            _           -> throwError $ "No buffer number or file name inside quickfix list item."
         lnumOrPattern <- case (l "lnum", l "pattern") of
             (Right lnum, _) -> return $ Left lnum
             (_, Right pat)  -> return $ Right pat
-            _              -> throwError . P.text $ "No line number or search pattern inside quickfix list item."
-        let l' :: NvimObject o => ByteString -> Either Doc (Maybe o)
+            _              -> throwError $ "No line number or search pattern inside quickfix list item."
+        let l' :: NvimObject o => ByteString -> Either (Doc AnsiStyle) (Maybe o)
             l' key = case Map.lookup key m of
                 Just o -> Just <$> fromObject o
                 Nothing -> return Nothing
@@ -150,17 +169,20 @@
                 nr' -> return nr'
         c <- l' "col"
         v <- l' "vcol"
-        let col = do
+        let col = maybe NoColumn id $ do
                 c' <- c
                 v' <- v
-                case c' of
-                    0 -> Nothing
-                    _ -> Just (c',v')
+                case (c',v') of
+                    (0, _) -> return $ NoColumn
+                    (_, True) -> return $ VisualColumn c'
+                    (_, False) -> return $ ByteIndexColumn c'
         text <- fromMaybe mempty <$> l' "text"
         errorType <- fromMaybe Error <$> l' "type"
         return QFItem{..}
 
-    fromObject o = throwError . P.text $ "Could not deserialize QuickfixListItem, expected a map but received: " ++ show o
+    fromObject o = throwError $ "Could not deserialize QuickfixListItem,"
+                                 <+> "expected a map but received:"
+                                 <+> viaShow o
 
 
 data QuickfixAction
diff --git a/library/Neovim/RPC/Classes.hs b/library/Neovim/RPC/Classes.hs
--- a/library/Neovim/RPC/Classes.hs
+++ b/library/Neovim/RPC/Classes.hs
@@ -1,7 +1,8 @@
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
 {-# LANGUAGE LambdaCase         #-}
+{-# LANGUAGE OverloadedStrings  #-}
 {-# LANGUAGE RecordWildCards    #-}
-{-# LANGUAGE DeriveGeneric      #-}
 {- |
 Module      :  Neovim.RPC.Classes
 Description :  Data types and classes for the RPC components
@@ -19,16 +20,18 @@
     ) where
 
 import           Neovim.Classes
-import           Neovim.Plugin.Classes        (FunctionName (..))
-import qualified Neovim.Plugin.IPC.Classes    as IPC
+import           Neovim.Plugin.Classes     (FunctionName (..))
+import qualified Neovim.Plugin.IPC.Classes as IPC
 
 import           Control.Applicative
 import           Control.Monad.Error.Class
-import           Data.Data                    (Typeable)
-import           Data.Int                     (Int64)
-import           Data.MessagePack             (Object (..))
-import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
+import           Data.Data                 (Typeable)
+import           Data.Int                  (Int64)
+import           Data.MessagePack          (Object (..))
 
+import           Data.Text.Prettyprint.Doc (Pretty (..), hardline, nest,
+                                            viaShow, (<+>), (<>))
+
 import           Prelude
 
 -- | See https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md for
@@ -97,7 +100,7 @@
             return $ Notification n
 
         o ->
-            throwError . text $ "Not a known/valid msgpack-rpc message" ++ show o
+            throwError $ "Not a known/valid msgpack-rpc message:" <+> viaShow o
 
 
 instance Pretty Message where
@@ -106,8 +109,8 @@
             pretty request
 
         Response i ret ->
-            nest 2 $ text "Response" <+> (yellow . text . ('#':) . show) i
-                <$$> either (red . text . show) (green . text. show) ret
+            nest 2 $ "Response" <+> "#" <> pretty i
+                <> hardline <> either viaShow viaShow ret
 
         Notification notification ->
             pretty notification
diff --git a/library/Neovim/RPC/Common.hs b/library/Neovim/RPC/Common.hs
--- a/library/Neovim/RPC/Common.hs
+++ b/library/Neovim/RPC/Common.hs
@@ -41,6 +41,9 @@
     -- ^ A map from message identifiers (as per RPC spec) to a tuple with a
     -- timestamp and a 'TMVar' that is used to communicate the result back to
     -- the calling thread.
+
+    , nextMessageId :: TVar Int64
+    -- ^ Message identifier for the next message as per RPC spec.
     }
 
 -- | Create a new basic configuration containing a communication channel for
@@ -49,6 +52,7 @@
 newRPCConfig :: (Applicative io, MonadIO io) => io RPCConfig
 newRPCConfig = RPCConfig
     <$> liftIO (newTVarIO mempty)
+    <*> liftIO (newTVarIO 1)
 
 -- | Simple data type defining the kind of socket the socket reader should use.
 data SocketType = Stdout Handle
diff --git a/library/Neovim/RPC/EventHandler.hs b/library/Neovim/RPC/EventHandler.hs
--- a/library/Neovim/RPC/EventHandler.hs
+++ b/library/Neovim/RPC/EventHandler.hs
@@ -26,15 +26,9 @@
 import           Control.Applicative
 import           Control.Concurrent.STM       hiding (writeTQueue)
 import           Control.Monad.Reader
-import           Control.Monad.State.Strict
 import           Control.Monad.Trans.Resource
 import           Data.ByteString              (ByteString)
-import           Data.Conduit                 as C
-#if MIN_VERSION_conduit_extra(1,2,2)
-import           Data.Conduit.Binary          (sinkHandleFlush)
-#else
-import           Data.Conduit.Binary          (sinkHandle)
-#endif
+import           Conduit                      as C
 import qualified Data.Map                     as Map
 import           Data.Serialize               (encode)
 import           System.IO                    (Handle)
@@ -46,35 +40,29 @@
 -- | This function will establish a connection to the given socket and write
 -- msgpack-rpc requests to it.
 runEventHandler :: Handle
-                -> Internal.Config RPCConfig Int64
+                -> Internal.Config RPCConfig
                 -> IO ()
 runEventHandler writeableHandle env =
-    runEventHandlerContext env $ do
+    runEventHandlerContext env . runConduit $ do
         eventHandlerSource
-            $= eventHandler
-            $$ addCleanup
-                (cleanUpHandle writeableHandle)
-#if MIN_VERSION_conduit_extra(1,2,2)
-                (sinkHandleFlush writeableHandle)
-#else
-                (sinkHandle writeableHandle)
-#endif
+            .| eventHandler
+            .| (sinkHandleFlush writeableHandle)
 
 
 -- | Convenient monad transformer stack for the event handler
 newtype EventHandler a =
-    EventHandler (ResourceT (ReaderT (Internal.Config RPCConfig Int64) (StateT Int64 IO)) a)
-    deriving ( Functor, Applicative, Monad, MonadState Int64, MonadIO
-             , MonadReader (Internal.Config RPCConfig Int64))
+    EventHandler (ResourceT (ReaderT (Internal.Config RPCConfig) IO) a)
+    deriving ( Functor, Applicative, Monad, MonadIO
+             , MonadReader (Internal.Config RPCConfig))
 
 
 runEventHandlerContext
-    :: Internal.Config RPCConfig Int64 -> EventHandler a -> IO a
+    :: Internal.Config RPCConfig -> EventHandler a -> IO a
 runEventHandlerContext env (EventHandler a) =
-    evalStateT (runReaderT (runResourceT a) env) 1
+    runReaderT (runResourceT a) env
 
 
-eventHandlerSource :: Source EventHandler SomeMessage
+eventHandlerSource :: ConduitT () SomeMessage EventHandler ()
 eventHandlerSource = asks Internal.eventQueue >>= \q ->
     forever $ yield =<< atomically' (readTQueue q)
 
@@ -89,37 +77,31 @@
         eventHandler
 
 
-#if MIN_VERSION_conduit_extra(1,2,2)
-type EncodedResponse = Flush ByteString
-#else
-type EncodedResponse = ByteString
-#endif
+type EncodedResponse = C.Flush ByteString
 
 yield' :: (MonadIO io) => MsgpackRPC.Message -> ConduitM i EncodedResponse io ()
 yield' o = do
     liftIO . debugM "EventHandler" $ "Sending: " ++ show o
-#if MIN_VERSION_conduit_extra(1,2,2)
     yield . Chunk . encode $ toObject o
     yield Flush
-#else
-    yield . encode $ toObject o
-#endif
 
 handleMessage :: (Maybe FunctionCall, Maybe MsgpackRPC.Message)
               -> ConduitM i EncodedResponse EventHandler ()
 handleMessage = \case
     (Just (FunctionCall fn params reply time), _) -> do
-        i <- get
-        modify succ
-        rs <- asks (recipients . Internal.customConfig)
-        atomically' . modifyTVar rs $ Map.insert i (time, reply)
-        yield' $ MsgpackRPC.Request (Request fn i params)
+        cfg <- asks (Internal.customConfig)
+        messageId <- atomically' $ do
+            i <- readTVar (nextMessageId cfg)
+            modifyTVar' (nextMessageId cfg) succ
+            modifyTVar' (recipients cfg) $ Map.insert i (time, reply)
+            return i
+        yield' $ MsgpackRPC.Request (Request fn messageId params)
 
     (_, Just r@MsgpackRPC.Response{}) ->
-        yield' $ r
+        yield' r
 
     (_, Just n@MsgpackRPC.Notification{}) ->
-        yield' $ n
+        yield' n
 
     _ ->
         return () -- i.e. skip to next message
diff --git a/library/Neovim/RPC/FunctionCall.hs b/library/Neovim/RPC/FunctionCall.hs
--- a/library/Neovim/RPC/FunctionCall.hs
+++ b/library/Neovim/RPC/FunctionCall.hs
@@ -30,15 +30,13 @@
 import           Neovim.Plugin.Classes     (FunctionName)
 import           Neovim.Plugin.IPC.Classes
 import qualified Neovim.RPC.Classes        as MsgpackRPC
-import           Neovim.Exceptions         (NeovimException(..))
+import           Neovim.Exceptions         (NeovimException(..), exceptionToDoc)
 
 import           Control.Applicative
 import           Control.Concurrent.STM
-import           Control.Exception.Lifted  (throwIO)
 import           Control.Monad.Reader
 import           Data.MessagePack
-import           Data.Monoid
-import qualified Text.PrettyPrint.ANSI.Leijen as P
+import           UnliftIO.Exception        (throwIO)
 
 import           Prelude
 
@@ -49,7 +47,7 @@
 unexpectedException :: String -> err -> a
 unexpectedException fn _ = error $
     "Function threw an exception even though it was declared not to throw one: "
-    <> fn
+    ++ fn
 
 
 -- | Strip the error result from the function call. This should only be used by
@@ -66,7 +64,7 @@
 acall :: (NvimObject result)
      => FunctionName
      -> [Object]
-     -> Neovim r st (STM (Either NeovimException result))
+     -> Neovim env (STM (Either NeovimException result))
 acall fn parameters = do
     q <- Internal.asks' Internal.eventQueue
     mv <- liftIO newEmptyTMVarIO
@@ -87,7 +85,7 @@
 acall' :: (NvimObject result)
        => FunctionName
        -> [Object]
-       -> Neovim r st (STM result)
+       -> Neovim env (STM result)
 acall' fn parameters = withIgnoredException fn <$> acall fn parameters
 
 
@@ -96,7 +94,7 @@
 scall :: (NvimObject result)
       => FunctionName
       -> [Object]      -- ^ Parameters in an 'Object' array
-      -> Neovim r st (Either NeovimException result)
+      -> Neovim env (Either NeovimException result)
       -- ^ result value of the call or the thrown exception
 scall fn parameters = acall fn parameters >>= atomically'
 
@@ -104,13 +102,13 @@
 scallThrow :: (NvimObject result)
            => FunctionName
            -> [Object]
-           -> Neovim r st result
+           -> Neovim env result
 scallThrow fn parameters = scall fn parameters >>= either throwIO return
 
 
 -- | Helper function similar to 'scall' that throws a runtime exception if the
 -- result is an error object.
-scall' :: NvimObject result => FunctionName -> [Object] -> Neovim r st result
+scall' :: NvimObject result => FunctionName -> [Object] -> Neovim env result
 scall' fn = withIgnoredException fn . scall fn
 
 
@@ -123,34 +121,36 @@
 --
 -- This action possibly blocks as it is an alias for
 -- @ \ioSTM -> ioSTM >>= liftIO . atomically@.
-wait :: Neovim r st (STM result) -> Neovim r st result
+wait :: Neovim env (STM result) -> Neovim env result
 wait = (=<<) atomically'
 
 
 -- | Variant of 'wait' that discards the result.
-wait' :: Neovim r st (STM result) -> Neovim r st ()
+wait' :: Neovim env (STM result) -> Neovim env ()
 wait' = void . wait
 
 
 -- | Wait for the result of the 'STM' action and call @'err' . (loc++) . show@
 -- if the action returned an error.
-waitErr :: (P.Pretty e)
-        => String                              -- ^ Prefix error message with this.
-        -> Neovim r st (STM (Either e result)) -- ^ Function call to neovim
-        -> Neovim r st result
-waitErr loc act = wait act >>= either (err . (P.<>) (P.text loc) . P.pretty) return
+waitErr :: String                             -- ^ Prefix error message with this.
+        -> Neovim env (STM (Either NeovimException result)) -- ^ Function call to neovim
+        -> Neovim env result
+waitErr loc act = wait act >>= \case
+    Left e ->
+        err $ pretty loc <+> exceptionToDoc e
+    Right result ->
+        return result
 
 
 -- | 'waitErr' that discards the result.
-waitErr' :: (P.Pretty e)
-         => String
-         -> Neovim r st (STM (Either e result))
-         -> Neovim r st ()
+waitErr' :: String
+         -> Neovim env (STM (Either NeovimException result))
+         -> Neovim env ()
 waitErr' loc = void . waitErr loc
 
 
 -- | Send the result back to the neovim instance.
-respond :: (NvimObject result) => Request -> Either String result -> Neovim r st ()
+respond :: (NvimObject result) => Request -> Either String result -> Neovim env ()
 respond Request{..} result = do
     q <- Internal.asks' Internal.eventQueue
     atomically' . writeTQueue q . SomeMessage . MsgpackRPC.Response reqId $
diff --git a/library/Neovim/RPC/SocketReader.hs b/library/Neovim/RPC/SocketReader.hs
--- a/library/Neovim/RPC/SocketReader.hs
+++ b/library/Neovim/RPC/SocketReader.hs
@@ -26,19 +26,16 @@
                                              FunctionalityDescription (..),
                                              getCommandOptions)
 import           Neovim.Plugin.IPC.Classes
-import           Neovim.Plugin              (registerInStatelessContext)
 import qualified Neovim.RPC.Classes         as MsgpackRPC
 import           Neovim.RPC.Common
 import           Neovim.RPC.FunctionCall
 
 import           Control.Applicative
-import           Control.Concurrent         (forkIO)
 import           Control.Concurrent.STM
 import           Control.Monad              (void)
 import           Control.Monad.Trans.Class  (lift)
-import           Data.Conduit               as C
-import           Data.Conduit.Binary
-import           Data.Conduit.Cereal
+import           Conduit               as C
+import           Data.Conduit.Cereal        (conduitGet2)
 import           Data.Default               (def)
 import           Data.Foldable              (foldl', forM_)
 import qualified Data.Map                   as Map
@@ -47,7 +44,8 @@
 import qualified Data.Serialize             (get)
 import           System.IO                  (Handle)
 import           System.Log.Logger
-import           Text.PrettyPrint.ANSI.Leijen (renderCompact, displayS)
+import           UnliftIO.Async             (async, race)
+import           UnliftIO.Concurrent        (threadDelay)
 
 import           Prelude
 
@@ -55,30 +53,24 @@
 logger = "Socket Reader"
 
 
-type SocketHandler = Neovim RPCConfig ()
+type SocketHandler = Neovim RPCConfig
 
 
 -- | This function will establish a connection to the given socket and read
 -- msgpack-rpc events from it.
 runSocketReader :: Handle
-                -> Internal.Config RPCConfig st
+                -> Internal.Config RPCConfig
                 -> IO ()
 runSocketReader readableHandle cfg =
-    void . runNeovim (Internal.retypeConfig (Internal.customConfig cfg) () cfg) () $ do
-        -- addCleanup (cleanUpHandle h) (sourceHandle h)
-        -- TODO test whether/how this should be handled
-        -- this has been commented out because I think that restarting the
-        -- plugin provider should not cause the stdin and stdout handles to be
-        -- closed since that would cause neovim to stop the plugin provider (I
-        -- think).
+    void . runNeovim (Internal.retypeConfig (Internal.customConfig cfg) cfg) . runConduit $ do
         sourceHandle readableHandle
-            $= conduitGet2 Data.Serialize.get
-            $$ messageHandlerSink
+            .| conduitGet2 Data.Serialize.get
+            .| messageHandlerSink
 
 
 -- | Sink that delegates the messages depending on their type.
 -- <https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md>
-messageHandlerSink :: Sink Object SocketHandler ()
+messageHandlerSink :: ConduitT Object Void SocketHandler ()
 messageHandlerSink = awaitForever $ \rpc -> do
     liftIO . debugM logger $ "Received: " <> show rpc
     case fromObject rpc of
@@ -95,7 +87,8 @@
             "Unhandled rpc message: " <> show e
 
 
-handleResponse :: Int64 -> Either Object Object -> Sink a SocketHandler ()
+handleResponse :: Int64 -> Either Object Object
+               -> ConduitT a Void SocketHandler ()
 handleResponse i result = do
     answerMap <- asks recipients
     mReply <- Map.lookup i <$> liftIO (readTVarIO answerMap)
@@ -106,57 +99,53 @@
             atomically' . modifyTVar' answerMap $ Map.delete i
             atomically' $ putTMVar reply result
 
+
 -- | Act upon the received request or notification. The main difference between
 -- the two is that a notification does not generate a reply. The distinction
 -- between those two cases is done via the first paramater which is 'Maybe' the
 -- function call identifier.
-handleRequestOrNotification :: Maybe Int64 -> FunctionName -> [Object] -> Sink a SocketHandler ()
-handleRequestOrNotification mi m params = do
+handleRequestOrNotification :: Maybe Int64 -> FunctionName -> [Object]
+                            -> ConduitT a Void SocketHandler ()
+handleRequestOrNotification requestId functionToCall params = do
     cfg <- lift Internal.ask'
-    void . liftIO . forkIO $ handle cfg
+    void . liftIO . async $ race logTimeout (handle cfg)
+    return ()
 
   where
     lookupFunction
         :: TMVar Internal.FunctionMap
         -> STM (Maybe (FunctionalityDescription, Internal.FunctionType))
-    lookupFunction funMap = Map.lookup m <$> readTMVar funMap
+    lookupFunction funMap = Map.lookup functionToCall <$> readTMVar funMap
 
-    handle :: Internal.Config RPCConfig () -> IO ()
+    logTimeout :: IO ()
+    logTimeout = do
+        let seconds = 1000 * 1000
+        threadDelay (10 * seconds)
+        debugM logger $ "Cancelled another action before it was finished"
+
+    handle :: Internal.Config RPCConfig -> IO ()
     handle rpc = atomically (lookupFunction (Internal.globalFunctionMap rpc)) >>= \case
 
         Nothing -> do
-            let errM = "No provider for: " <> show m
+            let errM = "No provider for: " <> show functionToCall
             debugM logger errM
-            forM_ mi $ \i -> atomically' . writeTQueue (Internal.eventQueue rpc)
+            forM_ requestId $ \i -> atomically' . writeTQueue (Internal.eventQueue rpc)
                 . SomeMessage $ MsgpackRPC.Response i (Left (toObject errM))
-        Just (copts, Internal.Stateless f) -> do
-            liftIO . debugM logger $ "Executing stateless function with ID: " <> show mi
-            -- Stateless function: Create a boring state object for the
-            -- Neovim context.
-            -- drop the state of the result with (fmap fst <$>)
-            let rpc' = rpc
-                    { Internal.customConfig = ()
-                    , Internal.pluginSettings = Just . Internal.StatelessSettings $
-                        registerInStatelessContext (\_ -> return ())
-                    }
-            res <- fmap fst <$> runNeovim rpc' () (f $ parseParams copts params)
-            -- Send the result to the event handler
-            forM_ mi $ \i -> atomically' . writeTQueue (Internal.eventQueue rpc)
-                . SomeMessage . MsgpackRPC.Response i $ either (Left . toObject . flip displayS "" . renderCompact) Right res
+
         Just (copts, Internal.Stateful c) -> do
             now <- liftIO getCurrentTime
             reply <- liftIO newEmptyTMVarIO
             let q = (recipients . Internal.customConfig) rpc
-            liftIO . debugM logger $ "Executing stateful function with ID: " <> show mi
-            case mi of
+            liftIO . debugM logger $ "Executing stateful function with ID: " <> show requestId
+            case requestId of
                 Just i -> do
                     atomically' . modifyTVar q $ Map.insert i (now, reply)
                     atomically' . writeTQueue c . SomeMessage $
-                        Request m i (parseParams copts params)
+                        Request functionToCall i (parseParams copts params)
 
                 Nothing ->
                     atomically' . writeTQueue c . SomeMessage $
-                        Notification m (parseParams copts params)
+                        Notification functionToCall (parseParams copts params)
 
 
 parseParams :: FunctionalityDescription -> [Object] -> [Object]
diff --git a/library/Neovim/Test.hs b/library/Neovim/Test.hs
--- a/library/Neovim/Test.hs
+++ b/library/Neovim/Test.hs
@@ -1,4 +1,5 @@
-{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE LambdaCase        #-}
+{-# LANGUAGE OverloadedStrings #-}
 {- |
 Module      :  Neovim.Test
 Description :  Testing functions
@@ -16,23 +17,27 @@
     ) where
 
 import           Neovim
-import qualified Neovim.Context.Internal      as Internal
-import           Neovim.RPC.Common            (newRPCConfig, RPCConfig)
-import           Neovim.RPC.EventHandler      (runEventHandler)
-import           Neovim.RPC.SocketReader      (runSocketReader)
+import qualified Neovim.Context.Internal                   as Internal
+import           Neovim.RPC.Common                         (RPCConfig,
+                                                            newRPCConfig)
+import           Neovim.RPC.EventHandler                   (runEventHandler)
+import           Neovim.RPC.SocketReader                   (runSocketReader)
 
-import           Control.Concurrent
-import           Control.Concurrent.STM       (atomically, putTMVar)
-import           Control.Exception.Lifted
-import           Control.Monad.Reader         (runReaderT)
-import           Control.Monad.State          (runStateT)
-import           Control.Monad.Trans.Resource (runResourceT)
-import           GHC.IO.Exception             (ioe_filename)
+import           Control.Monad.Reader                      (runReaderT)
+import           Control.Monad.Trans.Resource              (runResourceT)
+import           Data.Text.Prettyprint.Doc                 (annotate, vsep)
+import           Data.Text.Prettyprint.Doc.Render.Terminal (Color (..), color,
+                                                            putDoc)
+import           GHC.IO.Exception                          (ioe_filename)
 import           System.Directory
-import           System.Exit                  (ExitCode (..))
-import           System.IO                    (Handle)
+import           System.Exit                               (ExitCode (..))
+import           System.IO                                 (Handle)
 import           System.Process
-import           Text.PrettyPrint.ANSI.Leijen (red, text, putDoc, (<$$>))
+import           UnliftIO.Async                            (async, cancel)
+import           UnliftIO.Concurrent                       (threadDelay)
+import           UnliftIO.Exception
+import           UnliftIO.STM                              (atomically,
+                                                            putTMVar)
 
 
 -- | Type synonym for 'Word'.
@@ -49,25 +54,24 @@
 testWithEmbeddedNeovim
     :: Maybe FilePath -- ^ Optional path to a file that should be opened
     -> Seconds        -- ^ Maximum time (in seconds) that a test is allowed to run
-    -> r              -- ^ Read-only configuration
-    -> st             -- ^ State
-    -> Neovim r st a  -- ^ Test case
+    -> env            -- ^ Read-only configuration
+    -> Neovim env a   -- ^ Test case
     -> IO ()
-testWithEmbeddedNeovim file timeout r st (Internal.Neovim a) =
+testWithEmbeddedNeovim file timeout r (Internal.Neovim a) =
     runTest `catch` catchIfNvimIsNotOnPath
   where
     runTest = do
-        (_, _, ph, cfg) <- startEmbeddedNvim file timeout
+        (_, _, ph, cfg, cleanUp) <- startEmbeddedNvim file timeout
 
-        let testCfg = Internal.retypeConfig r st cfg
+        let testCfg = Internal.retypeConfig r cfg
 
-        void $ runReaderT (runStateT (runResourceT a) st) testCfg
+        void $ runReaderT (runResourceT a) testCfg
 
         -- vim_command isn't asynchronous, so we need to avoid waiting for the
         -- result of the operation since neovim cannot send a result if it
         -- has quit.
         let Internal.Neovim q = vim_command "qa!"
-        void . forkIO . void $ runReaderT (runStateT (runResourceT q) st ) testCfg
+        testRunner <- async . void $ runReaderT (runResourceT q) testCfg
 
         waitForProcess ph >>= \case
             ExitFailure i ->
@@ -75,21 +79,25 @@
 
             ExitSuccess ->
                 return ()
+        cancel testRunner
+        cleanUp
 
 
 catchIfNvimIsNotOnPath :: IOException -> IO ()
 catchIfNvimIsNotOnPath e = case ioe_filename e of
     Just "nvim" ->
-        putDoc . red $ text "The neovim executable 'nvim' is not on the PATH."
-                    <$$> text "You may not be testing fully!"
+        putDoc . annotate (color Red) $ vsep
+            [ "The neovim executable 'nvim' is not on the PATH."
+            , "You may not be testing fully!"
+            ]
 
     _           ->
-        throw e
+        throwIO e
 
 startEmbeddedNvim
     :: Maybe FilePath
     -> Seconds
-    -> IO (Handle, Handle, ProcessHandle, Internal.Config RPCConfig st)
+    -> IO (Handle, Handle, ProcessHandle, Internal.Config RPCConfig, IO ())
 startEmbeddedNvim file (Seconds timeout) = do
     args <- case file of
                 Nothing ->
@@ -109,11 +117,11 @@
 
     cfg <- Internal.newConfig (pure Nothing) newRPCConfig
 
-    void . forkIO $ runSocketReader
+    socketReader <- async . void $ runSocketReader
                     hout
                     (cfg { Internal.pluginSettings = Nothing })
 
-    void . forkIO $ runEventHandler
+    eventHandler <- async . void $ runEventHandler
                     hin
                     (cfg { Internal.pluginSettings = Nothing })
 
@@ -121,9 +129,11 @@
                     (Internal.globalFunctionMap cfg)
                     (Internal.mkFunctionMap [])
 
-    void . forkIO $ do
+    timeoutAsync <- async . void $ do
         threadDelay $ (fromIntegral timeout) * 1000 * 1000
         getProcessExitCode ph >>= maybe (terminateProcess ph) (\_ -> return ())
 
-    return (hin, hout, ph, cfg)
+    let cleanUp = mapM_ cancel [socketReader, eventHandler, timeoutAsync]
+
+    return (hin, hout, ph, cfg, cleanUp)
 
diff --git a/library/Neovim/Util.hs b/library/Neovim/Util.hs
--- a/library/Neovim/Util.hs
+++ b/library/Neovim/Util.hs
@@ -18,17 +18,21 @@
     ) where
 
 import           Control.Monad       (forM, forM_, when, unless)
-import           Control.Monad.Catch (MonadMask, bracket)
 import           Neovim.Context
 import           System.SetEnv
 import           System.Environment
-import qualified Text.PrettyPrint.ANSI.Leijen as P
+import qualified Data.Text as T
+import           UnliftIO (MonadUnliftIO)
+import           UnliftIO.Exception  (bracket)
 
 
 -- | Execute the given action with a changed set of environment variables and
 -- restore the original state of the environment afterwards.
-withCustomEnvironment :: (MonadMask io, MonadIO io)
-                      => [(String, Maybe String)] -> io a -> io a
+withCustomEnvironment
+    :: (Traversable t, MonadUnliftIO m)
+    => t (String, Maybe String)
+    -> m c
+    -> m c
 withCustomEnvironment modifiedEnvironment action =
     bracket saveAndSet unset (const action)
 
@@ -54,7 +58,7 @@
 unlessM mp a = mp >>= \p -> unless p a
 
 
-oneLineErrorMessage :: P.Doc -> String
-oneLineErrorMessage d = case lines $ P.displayS (P.renderCompact d) "" of
+oneLineErrorMessage :: Doc AnsiStyle -> T.Text
+oneLineErrorMessage d = case T.lines $ docToText d of
     (x:_) -> x
-    []    -> ""
+    []    -> mempty
diff --git a/nvim-hs.cabal b/nvim-hs.cabal
--- a/nvim-hs.cabal
+++ b/nvim-hs.cabal
@@ -1,5 +1,5 @@
 name:                nvim-hs
-version:             0.2.5
+version:             1.0.0.0
 synopsis:            Haskell plugin backend for neovim
 description:
   This package provides a plugin provider for neovim. It allows you to write
@@ -30,7 +30,6 @@
 category:            Editor
 build-type:          Simple
 cabal-version:       >=1.18
-tested-with:         GHC == 7.10.3, GHC == 8.0.2
 extra-source-files:    nvim-hs-devel.sh
                      , test-files/compile-error-for-quickfix-test1
                      , test-files/compile-error-for-quickfix-test2
@@ -95,40 +94,28 @@
                       , Neovim.API.TH
   other-extensions:     DeriveGeneric
   build-depends:        base >=4.6 && < 5
-                      , ansi-wl-pprint
                       , bytestring
                       , cereal
-                      , cereal-conduit >= 0.7.3
-                      , conduit
-
-                      -- sinkHandle flushed after every yield in the releases
-                      -- [1.1.2, 1.1.17) and in release 1.2.2 an API was added
-                      -- to flush manually, so any other version probably does
-                      -- not work unless you set NoBuffering on the handle (see #61)
-                      -- I think having NoBuffering on TCP-Handles is kind of
-                      -- ridicilous, so the version range [1.1.17, 1.2.2) is
-                      -- simply excluded
-                      , conduit-extra >= 1.1.2 && < 1.1.17 || >= 1.2.2
-
+                      , cereal-conduit >= 0.8.0
+                      , conduit >= 1.3.0
                       , containers
                       , data-default
                       , deepseq >= 1.1 && < 1.5
                       , directory
                       , dyre
-                      , exceptions
                       , filepath
                       , foreign-store
                       , hslogger
                       , messagepack >= 0.5.4
-                      , monad-control
                       , network
-                      , lifted-base
                       , mtl >= 2.2.1 && < 2.3
                       , optparse-applicative
                       , time-locale-compat
                       , megaparsec
+                      , prettyprinter >= 1.2 && < 2
+                      , prettyprinter-ansi-terminal
                       , process
-                      , resourcet
+                      , resourcet >= 1.1.11
                       , setenv >= 0.1.1.3
                       , stm
                       , streaming-commons
@@ -137,6 +124,8 @@
                       , time
                       , transformers
                       , transformers-base
+                      , unliftio >= 0.2
+                      , unliftio-core
                       , utf8-string
                       , void
   hs-source-dirs:       library
@@ -156,30 +145,28 @@
                       , hspec-discover
                       , QuickCheck >=2.6
 
-                      , ansi-wl-pprint
                       , bytestring
                       , cereal
                       , cereal-conduit
                       , conduit
-                      , conduit-extra
                       , containers
                       , data-default
                       , directory
                       , dyre
-                      , exceptions
                       , filepath
                       , foreign-store
                       , hslogger
-                      , lifted-base
                       , mtl
                       , messagepack
                       , time-locale-compat
                       , network
                       , optparse-applicative
                       , megaparsec
+                      , prettyprinter
+                      , prettyprinter-ansi-terminal
                       , process
                       , resourcet
-                      , setenv >= 0.1.1.3
+                      , setenv
                       , stm
                       , streaming-commons
                       , text
@@ -187,6 +174,8 @@
                       , time
                       , transformers
                       , transformers-base
+                      , unliftio
+                      , unliftio-core
                       , utf8-string
                       , HUnit
 
diff --git a/test-suite/Neovim/API/THSpec.hs b/test-suite/Neovim/API/THSpec.hs
--- a/test-suite/Neovim/API/THSpec.hs
+++ b/test-suite/Neovim/API/THSpec.hs
@@ -20,14 +20,14 @@
 
 import           Control.Applicative
 
-call :: ([Object] -> Neovim () () Object) -> [Object]
+call :: ([Object] -> Neovim () Object) -> [Object]
      -> IO Object
 call f args = do
     cfg <- Internal.newConfig (pure Nothing) (pure ())
-    res <- fmap fst <$> runNeovim cfg () (f args)
+    res <- runNeovim cfg (f args)
     case res of
         Right x -> return x
-        Left e -> (throw . ErrorMessage) e
+        Left e -> (throwIO . ErrorMessage) e
 
 
 isNeovimException :: NeovimException -> Bool
diff --git a/test-suite/Neovim/API/THSpecFunctions.hs b/test-suite/Neovim/API/THSpecFunctions.hs
--- a/test-suite/Neovim/API/THSpecFunctions.hs
+++ b/test-suite/Neovim/API/THSpecFunctions.hs
@@ -4,19 +4,19 @@
 import qualified Data.Map as Map
 import           Neovim
 
-testFunction0 :: Neovim' Int
+testFunction0 :: Neovim env Int
 testFunction0 = return 42
 
-testFunction2 :: CommandArguments -> String -> [String] -> Neovim' Double
+testFunction2 :: CommandArguments -> String -> [String] -> Neovim env Double
 testFunction2 _ _ _ = return 2
 
-testFunctionMap :: Map.Map String Int -> String -> Neovim' (Maybe Int)
+testFunctionMap :: Map.Map String Int -> String -> Neovim env (Maybe Int)
 testFunctionMap m k = return $ Map.lookup k m
 
-testSucc :: Int -> Neovim r st Int
+testSucc :: Int -> Neovim env Int
 testSucc = return . succ
 
-testCommandOptArgument :: CommandArguments -> Maybe String -> Neovim' String
+testCommandOptArgument :: CommandArguments -> Maybe String -> Neovim env String
 testCommandOptArgument _ ms = case ms of
     Just x  -> return x
     Nothing -> return "default"
diff --git a/test-suite/Neovim/EmbeddedRPCSpec.hs b/test-suite/Neovim/EmbeddedRPCSpec.hs
--- a/test-suite/Neovim/EmbeddedRPCSpec.hs
+++ b/test-suite/Neovim/EmbeddedRPCSpec.hs
@@ -18,7 +18,6 @@
 import           Control.Concurrent.STM
 import           Control.Monad.Reader         (runReaderT)
 import           Control.Monad.State          (runStateT)
-import           Control.Monad.Trans.Resource (runResourceT)
 import qualified Data.Map                     as Map
 import           System.Directory
 import           System.Exit                  (ExitCode (..))
@@ -29,7 +28,7 @@
 spec :: Spec
 spec = parallel $ do
   let helloFile = "test-files/hello"
-      withNeovimEmbedded f a = testWithEmbeddedNeovim f (Seconds 3) () () a
+      withNeovimEmbedded f a = testWithEmbeddedNeovim f (Seconds 3) () a
   describe "Read hello test file" .
     it "should match 'Hello, World!'" . withNeovimEmbedded (Just helloFile) $ do
         bs <- vim_get_buffers
