diff --git a/src/Yi/Buffer/Adjusted.hs b/src/Yi/Buffer/Adjusted.hs
deleted file mode 100644
--- a/src/Yi/Buffer/Adjusted.hs
+++ /dev/null
@@ -1,83 +0,0 @@
-{-# OPTIONS_HADDOCK show-extensions #-}
-
--- |
--- Module      :  Yi.Buffer.Adjusted
--- License     :  GPL-2
--- Maintainer  :  yi-devel@googlegroups.com
--- Stability   :  experimental
--- Portability :  portable
---
--- This module re-exports Yi.Buffer overriding insert* and delete* functions
--- with their more indent-aware variants. It is intended to be imported
--- instead of Yi.Buffer or qualified to avoid name clashes.
-
-module Yi.Buffer.Adjusted
-    ( bdeleteB
-    , insertB
-    , insertN
-    , insertNAt
-    , deleteB
-    , deleteN
-    , deleteRegionB
-    , deleteRegionWithStyleB
-    , module Yi.Buffer
-    ) where
-
-import           Control.Monad       (forM_, when)
-import           Yi.Buffer           hiding (bdeleteB, insertB, insertN, insertNAt
-                                            , deleteB, deleteN, deleteNAt
-                                            , deleteRegionB, deleteRegionWithStyleB)
-import qualified Yi.Buffer           as B (deleteN, insertB, insertNAt)
-import           Yi.Misc             (adjBlock)
-import qualified Yi.Rope             as R (YiString, countNewLines, length, take)
-import           Yi.Utils            (SemiNum ((~-)))
-
-insertNAt :: R.YiString -> Point -> BufferM ()
-insertNAt rope point | R.countNewLines rope > 0 = B.insertNAt rope point
-insertNAt rope point = B.insertNAt rope point >> adjBlock (R.length rope)
-
--- | Insert the list at current point, extending size of buffer
-insertN :: R.YiString -> BufferM ()
-insertN rope = insertNAt rope =<< pointB
-
--- | Insert the char at current point, extending size of buffer
-insertB :: Char -> BufferM ()
-insertB c = B.insertB c >> adjBlock 1
-
--- | @deleteNAt n p@ deletes @n@ characters forwards from position @p@
-deleteNAt :: Direction -> Int -> Point -> BufferM ()
-deleteNAt dir n pos = do
-  els <- R.take n <$> streamB Forward pos
-  applyUpdate (Delete pos dir els)
-  when (R.countNewLines els == 0) $
-      adjBlock (-(R.length els))
-
-deleteN :: Int -> BufferM ()
-deleteN n = pointB >>= deleteNAt Forward n
-
-deleteB :: TextUnit -> Direction -> BufferM ()
-deleteB unit dir = deleteRegionB =<< regionOfPartNonEmptyB unit dir
-
-bdeleteB :: BufferM ()
-bdeleteB = deleteB Character Backward
-
-deleteRegionB :: Region -> BufferM ()
-deleteRegionB r =
-    deleteNAt (regionDirection r)
-              (fromIntegral (regionEnd r ~- regionStart r))
-              (regionStart r)
-
-deleteRegionWithStyleB :: Region -> RegionStyle -> BufferM Point
-deleteRegionWithStyleB reg Block = savingPointB $ do
-    (start, lengths) <- shapeOfBlockRegionB reg
-    moveTo start
-    forM_ (zip [1..] lengths) $ \(i, l) -> do
-        B.deleteN l
-        moveTo start
-        lineMoveRel i
-    return start
-
-deleteRegionWithStyleB reg style = savingPointB $ do
-    effectiveRegion <- convertRegionToStyleB reg style
-    deleteRegionB effectiveRegion
-    return $! regionStart effectiveRegion
diff --git a/src/Yi/Buffer/HighLevel.hs b/src/Yi/Buffer/HighLevel.hs
--- a/src/Yi/Buffer/HighLevel.hs
+++ b/src/Yi/Buffer/HighLevel.hs
@@ -127,6 +127,7 @@
 import           Control.Monad.State      (gets)
 import           Data.Char                (isDigit, isHexDigit, isOctDigit, isSpace, isUpper, toLower, toUpper)
 import           Data.List                (intersperse, sort)
+import           Data.List.NonEmpty       (NonEmpty(..))
 import           Data.Maybe               (catMaybes, fromMaybe, listToMaybe)
 import           Data.Monoid              ((<>))
 import qualified Data.Set                 as Set
@@ -586,10 +587,13 @@
 -- | Move cursor to the bottom of the screen
 scrollCursorToBottomB :: BufferM ()
 scrollCursorToBottomB = do
-    MarkSet _ i _ <- markLines
-    r <- winRegionB
-    t <- lineOf (regionEnd r - 1)
-    scrollB $ i - t
+    -- NOTE: This is only an approximation.
+    --       The correct scroll amount depends on how many lines just above
+    --       the current viewport are going to be wrapped. We don't have this
+    --       information here as wrapping is done in the frontend.
+    MarkSet f i _ <- markLines
+    h <- askWindow actualLines
+    scrollB $ i - f - h + 1
 
 -- | Scroll by n lines.
 scrollB :: Int -> BufferM ()
@@ -604,15 +608,17 @@
 
 -- Scroll line above window to the bottom.
 scrollToLineAboveWindowB :: BufferM ()
-scrollToLineAboveWindowB = do downFromTosB 0
-                              replicateM_ 1 lineUp
-                              scrollCursorToBottomB
+scrollToLineAboveWindowB = do
+    downFromTosB 0
+    replicateM_ 1 lineUp
+    scrollCursorToBottomB
 
 -- Scroll line below window to the top.
 scrollToLineBelowWindowB :: BufferM ()
-scrollToLineBelowWindowB = do upFromBosB 0
-                              replicateM_ 1 lineDown
-                              scrollCursorToTopB
+scrollToLineBelowWindowB = do
+    upFromBosB 0
+    replicateM_ 1 lineDown
+    scrollCursorToTopB
 
 -- | Move the point to inside the viewable region
 snapInsB :: BufferM ()
@@ -628,7 +634,7 @@
 indexOfSolAbove :: Int -> BufferM Point
 indexOfSolAbove n = pointAt $ gotoLnFrom (negate n)
 
-data  RelPosition = Above | Below | Within
+data RelPosition = Above | Below | Within
   deriving (Show)
 
 -- | return relative position of the point @p@
@@ -641,7 +647,7 @@
 pointScreenRelPosition _ _ _ = Within -- just to disable the non-exhaustive pattern match warning
 
 -- | Move the visible region to include the point
-snapScreenB :: Maybe ScrollStyle ->BufferM Bool
+snapScreenB :: Maybe ScrollStyle -> BufferM Bool
 snapScreenB style = do
     w <- askWindow wkey
     movePoint <- Set.member w <$> use pointFollowsWindowA
@@ -957,30 +963,33 @@
 splitBlockRegionToContiguousSubRegionsB :: Region -> BufferM [Region]
 splitBlockRegionToContiguousSubRegionsB reg = savingPointB $ do
     (start, lengths) <- shapeOfBlockRegionB reg
-    moveTo start
-    forM lengths $ \l -> do
+    forM (zip [0..] lengths) $ \(i, l) -> do
+        moveTo start
+        void $ lineMoveRel i
         p0 <- pointB
         moveXorEol l
         p1 <- pointB
         let subRegion = mkRegion p0 p1
-        moveTo p0
-        void $ lineMoveRel 1
         return subRegion
 
-deleteRegionWithStyleB :: Region -> RegionStyle -> BufferM Point
+-- Return list containing a single point for all non-block styles.
+-- For Block return all the points along the left edge of the region
+deleteRegionWithStyleB :: Region -> RegionStyle -> BufferM (NonEmpty Point)
 deleteRegionWithStyleB reg Block = savingPointB $ do
     (start, lengths) <- shapeOfBlockRegionB reg
     moveTo start
-    forM_ (zip [1..] lengths) $ \(i, l) -> do
+    points <- forM (zip [1..] lengths) $ \(i, l) -> do
         deleteN l
+        p <- pointB
         moveTo start
         lineMoveRel i
-    return start
+        return (if l == 0 then Nothing else Just p)
+    return $ start :| drop 1 (catMaybes points)
 
 deleteRegionWithStyleB reg style = savingPointB $ do
     effectiveRegion <- convertRegionToStyleB reg style
     deleteRegionB effectiveRegion
-    return $! regionStart effectiveRegion
+    return $! pure (regionStart effectiveRegion)
 
 readRegionRopeWithStyleB :: Region -> RegionStyle -> BufferM YiString
 readRegionRopeWithStyleB reg Block = savingPointB $ do
diff --git a/src/Yi/Buffer/Misc.hs b/src/Yi/Buffer/Misc.hs
--- a/src/Yi/Buffer/Misc.hs
+++ b/src/Yi/Buffer/Misc.hs
@@ -136,7 +136,6 @@
   , modePrettifyA
   , modeKeymapA
   , modeIndentA
-  , modeAdjustBlockA
   , modeFollowA
   , modeIndentSettingsA
   , modeToggleCommentSelectionA
@@ -483,8 +482,9 @@
 
 -- | Mark the current point in the undo list as a saved state.
 markSavedB :: UTCTime -> BufferM ()
-markSavedB t = do undosA %= setSavedFilePointU
-                  lastSyncTimeA .= t
+markSavedB t = do
+    undosA %= setSavedFilePointU
+    lastSyncTimeA .= t
 
 bkey :: FBuffer -> BufferRef
 bkey = view bkey__A
@@ -495,9 +495,7 @@
 startUpdateTransactionB :: BufferM ()
 startUpdateTransactionB = do
   transactionPresent <- use updateTransactionInFlightA
-  if transactionPresent
-  then error "Already started update transaction"
-  else do
+  when (not transactionPresent) $ do
     undosA %= addChangeU InteractivePoint
     updateTransactionInFlightA .= True
 
@@ -573,7 +571,6 @@
    modePrettify = const $ return (),
    modeKeymap = id,
    modeIndent = \_ _ -> return (),
-   modeAdjustBlock = \_ _ -> return (),
    modeFollow = const emptyAction,
    modeIndentSettings = IndentSettings
    { expandTabs = True
diff --git a/src/Yi/Buffer/Undo.hs b/src/Yi/Buffer/Undo.hs
--- a/src/Yi/Buffer/Undo.hs
+++ b/src/Yi/Buffer/Undo.hs
@@ -48,6 +48,7 @@
 module Yi.Buffer.Undo (
     emptyU
   , addChangeU
+  , deleteInteractivePointsU
   , setSavedFilePointU
   , isAtSavedFilePointU
   , undoU
@@ -87,6 +88,12 @@
 addChangeU :: Change -> URList -> URList
 addChangeU InteractivePoint (URList us rs) = URList (addIP us) rs
 addChangeU u (URList us _) = URList (u S.<| us) S.empty
+
+deleteInteractivePointsU :: URList -> URList
+deleteInteractivePointsU (URList us rs) = URList (go us) rs
+    where
+    go (S.viewl -> InteractivePoint S.:< x) = go x
+    go x = x
 
 -- | Add a saved file point so that we can tell that the buffer has not
 -- been modified since the previous saved file point.
diff --git a/src/Yi/Core.hs b/src/Yi/Core.hs
--- a/src/Yi/Core.hs
+++ b/src/Yi/Core.hs
@@ -20,21 +20,22 @@
   (
   -- * Construction and destruction
     startEditor
-  , quitEditor          -- :: YiM ()
+  , quitEditor             -- :: YiM ()
+  , quitEditorWithExitCode -- :: ExitCode -> YiM ()
 
   -- * User interaction
-  , refreshEditor       -- :: YiM ()
-  , suspendEditor       -- :: YiM ()
+  , refreshEditor          -- :: YiM ()
+  , suspendEditor          -- :: YiM ()
   , userForceRefresh
 
   -- * Global editor actions
-  , errorEditor         -- :: String -> YiM ()
-  , closeWindow         -- :: YiM ()
+  , errorEditor            -- :: String -> YiM ()
+  , closeWindow            -- :: YiM ()
   , closeWindowEmacs
 
   -- * Interacting with external commands
-  , runProcessWithInput          -- :: String -> String -> YiM String
-  , startSubprocess                 -- :: FilePath -> [String] -> YiM ()
+  , runProcessWithInput    -- :: String -> String -> YiM String
+  , startSubprocess        -- :: FilePath -> [String] -> YiM ()
   , sendToProcess
 
   -- * Misc
@@ -71,7 +72,7 @@
 import           Data.Traversable               (forM)
 import           GHC.Conc                       (labelThread)
 import           System.Directory               (doesFileExist)
-import           System.Exit                    (ExitCode)
+import           System.Exit                    (ExitCode (ExitSuccess))
 import           System.IO                      (Handle, hPutStr, hWaitForInput)
 import           System.PosixCompat.Files       (getFileStatus, modificationTime)
 import           System.Process                 (ProcessHandle,
@@ -223,10 +224,14 @@
 
 -- | Quit.
 quitEditor :: YiM ()
-quitEditor = do
+quitEditor = quitEditorWithExitCode ExitSuccess
+
+-- | Quit with an exit code. (This is used to implement vim's :cq command)
+quitEditorWithExitCode :: ExitCode -> YiM ()
+quitEditorWithExitCode exitCode = do
     savePersistentState
     onYiVar $ terminateSubprocesses (const True)
-    withUI (`UI.end` True)
+    withUI (`UI.end` (Just exitCode))
 
 -- | Update (visible) buffers if they have changed on disk.
 -- FIXME: since we do IO here we must catch exceptions!
diff --git a/src/Yi/Misc.hs b/src/Yi/Misc.hs
--- a/src/Yi/Misc.hs
+++ b/src/Yi/Misc.hs
@@ -14,7 +14,7 @@
 -- Various high-level functions to further classify.
 
 module Yi.Misc ( getAppropriateFiles, getFolder, cd, pwd, matchingFileNames
-               , rot13Char, placeMark, selectAll, adjBlock, adjIndent
+               , rot13Char, placeMark, selectAll, adjIndent
                , promptFile , promptFileChangingHints, matchFile, completeFile
                , printFileInfoE, debugBufferContent
                ) where
@@ -122,9 +122,6 @@
 -- | Select the contents of the whole buffer
 selectAll :: BufferM ()
 selectAll = botB >> placeMark >> topB >> setVisibleSelection True
-
-adjBlock :: Int -> BufferM ()
-adjBlock x = withSyntaxB' (\m s -> modeAdjustBlock m s x)
 
 -- | A simple wrapper to adjust the current indentation using
 -- the mode specific indentation function but according to the
diff --git a/src/Yi/Syntax/Tree.hs b/src/Yi/Syntax/Tree.hs
--- a/src/Yi/Syntax/Tree.hs
+++ b/src/Yi/Syntax/Tree.hs
@@ -46,11 +46,6 @@
 import           Yi.Region           (Region (regionEnd, regionStart), mkRegion)
 import           Yi.String           (showT)
 
-#ifdef TESTING
-import           Test.QuickCheck
-import           Test.QuickCheck.Property (unProperty)
-#endif
-
 -- Fundamental types
 type Path = [Int]
 type Node t = (Path, t)
@@ -301,123 +296,3 @@
 
 sepBy1 :: (Alternative f) => f a -> f v -> f [a]
 sepBy1 p s  = (:) <$> p <*> many (s *> p)
-
-
-----------------------------------------------------
--- Testing code.
-
-#ifdef TESTING
-
-nodeRegion :: IsTree tree => Node (tree (Tok a)) -> Region
-nodeRegion n = subtreeRegion t
-    where Just t = walkDown n
-
-data Test a = Empty | Leaf a | Bin (Test a) (Test a) deriving (Show, Eq, Foldable)
-
-instance IsTree Test where
-    uniplate (Bin l r) = ([l,r],\[l',r'] -> Bin l' r')
-    uniplate t = ([],\[] -> t)
-    emptyNode = Empty
-
-type TT = Tok ()
-
-instance Arbitrary (Test TT) where
-    arbitrary = sized $ \size -> do
-      arbitraryFromList [1..size+1]
-    shrink (Leaf _) = []
-    shrink (Bin l r) = [l,r] <>  (Bin <$> shrink l <*> pure r) <>  (Bin <$> pure l <*> shrink r)
-
-tAt :: Point -> TT
-tAt idx =  Tok () 1 (Posn (idx * 2) 0 0)
-
-arbitraryFromList :: [Int] -> Gen (Test TT)
-arbitraryFromList [] = error "arbitraryFromList expects non empty lists"
-arbitraryFromList [x] = pure (Leaf (tAt (fromIntegral x)))
-arbitraryFromList xs = do
-  m <- choose (1,length xs - 1)
-  let (l,r) = splitAt m xs
-  Bin <$> arbitraryFromList l <*> arbitraryFromList r
-
-newtype NTTT = N (Node (Test TT)) deriving Show
-
-instance Arbitrary NTTT where
-    arbitrary = do
-      t <- arbitrary
-      p <- arbitraryPath t
-      return $ N (p,t)
-
-arbitraryPath :: Test t -> Gen Path
-arbitraryPath (Leaf _) = return []
-arbitraryPath (Bin l r) = do
-  c <- choose (0,1)
-  let Just n' = index c [l,r]
-  (c :) <$> arbitraryPath n'
-
-regionInside :: Region -> Gen Region
-regionInside r = do
-  b :: Int <- choose (fromIntegral $ regionStart r, fromIntegral $ regionEnd r)
-  e :: Int <- choose (b, fromIntegral $ regionEnd r)
-  return $ mkRegion (fromIntegral b) (fromIntegral e)
-
-pointInside :: Region -> Gen Point
-pointInside r = do
-  p :: Int <- choose (fromIntegral $ regionStart r, fromIntegral $ regionEnd r)
-  return (fromIntegral p)
-
-prop_fromLeafAfterToFinal :: NTTT -> Property
-prop_fromLeafAfterToFinal (N n) = let
-    fullRegion = subtreeRegion $ snd n
- in forAll (pointInside fullRegion) $ \p -> do
-   let final@(_, (_, finalSubtree)) = fromLeafAfterToFinal p n
-       finalRegion = subtreeRegion finalSubtree
-       initialRegion = nodeRegion n
-
-   whenFail (do putStrLn $ "final = " <> show final
-                putStrLn $ "final reg = " <> show finalRegion
-                putStrLn $ "initialReg = " <> show initialRegion
-                putStrLn $ "p = " <> show p
-            )
-     ((regionStart finalRegion <= p) && (initialRegion `includedRegion` finalRegion))
-
-prop_allLeavesAfter :: NTTT -> Property
-prop_allLeavesAfter (N n@(xs,t)) = property $ do
-  let after = allLeavesRelative afterChild n
-  (xs',t') <- elements after
-  let t'' = walkDown (xs',t)
-  unProperty $ whenFail (do
-      putStrLn $ "t' = " <> show t'
-      putStrLn $ "t'' = " <> show t''
-      putStrLn $ "xs' = " <> show xs'
-    ) (Just t' == t'' && xs <= xs')
-
-prop_allLeavesBefore :: NTTT -> Property
-prop_allLeavesBefore (N n@(xs,t)) = property $ do
-  let after = allLeavesRelative beforeChild n
-  (xs',t') <- elements after
-  let t'' = walkDown (xs',t)
-  unProperty $ whenFail (do
-      putStrLn $ "t' = " <> show t'
-      putStrLn $ "t'' = " <> show t''
-      putStrLn $ "xs' = " <> show xs'
-    ) (Just t' == t'' && xs' <= xs)
-
-prop_fromNodeToLeafAfter :: NTTT -> Property
-prop_fromNodeToLeafAfter (N n) = forAll (pointInside (subtreeRegion $ snd n)) $ \p -> do
-   let after = fromLeafToLeafAfter p n
-       afterRegion = nodeRegion after
-   whenFail (do putStrLn $ "after = " <> show after
-                putStrLn $ "after reg = " <> show afterRegion
-            )
-     (regionStart afterRegion >= p)
-
-prop_fromNodeToFinal :: NTTT -> Property
-prop_fromNodeToFinal  (N t) = forAll (regionInside (subtreeRegion $ snd t)) $ \r -> do
-   let final@(_, finalSubtree) = fromNodeToFinal r t
-       finalRegion = subtreeRegion finalSubtree
-   whenFail (do putStrLn $ "final = " <> show final
-                putStrLn $ "final reg = " <> show finalRegion
-                putStrLn $ "leaf after = " <> show (fromLeafToLeafAfter (regionEnd r) t)
-            ) $ do
-     r `includedRegion` finalRegion
-
-#endif
diff --git a/src/Yi/Types.hs b/src/Yi/Types.hs
--- a/src/Yi/Types.hs
+++ b/src/Yi/Types.hs
@@ -297,8 +297,6 @@
     -- ^ Buffer-local keymap modification
   , modeIndent :: syntax -> IndentBehaviour -> BufferM ()
     -- ^ emacs-style auto-indent line
-  , modeAdjustBlock :: syntax -> Int -> BufferM ()
-    -- ^ adjust the indentation after modification
   , modeFollow :: syntax -> Action
     -- ^ Follow a \"link\" in the file. (eg. go to location of error message)
   , modeIndentSettings :: IndentSettings
diff --git a/src/Yi/UI/Common.hs b/src/Yi/UI/Common.hs
--- a/src/Yi/UI/Common.hs
+++ b/src/Yi/UI/Common.hs
@@ -2,6 +2,8 @@
 
 module Yi.UI.Common where
 
+import           System.Exit (ExitCode)
+
 {- | Record presenting a frontend's interface.
 
 The functions 'layout' and 'refresh' are both run by the editor's main loop,
@@ -44,13 +46,13 @@
 should wrap GUI updates in @postGUIAsync@.
 -}
 data UI e = UI
-    { main                  :: IO ()               -- ^ Main loop
-    , end                   :: Bool -> IO ()       -- ^ Clean up, and also terminate if given 'true'
-    , suspend               :: IO ()               -- ^ Suspend (or minimize) the program
-    , refresh               :: e -> IO ()     -- ^ Refresh the UI with the given state
-    , userForceRefresh      :: IO ()               -- ^ User force-refresh (in case the screen has been messed up from outside)
-    , layout                :: e -> IO e -- ^ Set window width and height
-    , reloadProject         :: FilePath -> IO ()   -- ^ Reload cabal project views
+    { main                  :: IO ()                   -- ^ Main loop
+    , end                   :: Maybe ExitCode -> IO () -- ^ Clean up, and also terminate if given an exit code.
+    , suspend               :: IO ()                   -- ^ Suspend (or minimize) the program
+    , refresh               :: e -> IO ()              -- ^ Refresh the UI with the given state
+    , userForceRefresh      :: IO ()                   -- ^ User force-refresh (in case the screen has been messed up from outside)
+    , layout                :: e -> IO e               -- ^ Set window width and height
+    , reloadProject         :: FilePath -> IO ()       -- ^ Reload cabal project views
     }
 
 dummyUI :: UI e
diff --git a/yi-core.cabal b/yi-core.cabal
--- a/yi-core.cabal
+++ b/yi-core.cabal
@@ -1,9 +1,9 @@
--- This file has been generated from package.yaml by hpack version 0.17.0.
+-- This file has been generated from package.yaml by hpack version 0.17.1.
 --
 -- see: https://github.com/sol/hpack
 
 name:           yi-core
-version:        0.14.1
+version:        0.15.0
 synopsis:       Yi editor core library
 category:       Yi
 homepage:       https://github.com/yi-editor/yi#readme
@@ -72,8 +72,7 @@
     , unordered-containers >= 0.1.3
     , xdg-basedir >= 0.2.1
     , yi-language >= 0.1.1.0
-    , yi-rope >= 0.7.0.0
-    , semigroups
+    , yi-rope >= 0.7.0.0 && < 0.9
   if os(win32)
     build-depends:
         Win32
@@ -89,15 +88,9 @@
     cpp-options: -DHINT
     build-depends:
         hint > 0.3.1
-  if flag(testing)
-    cpp-options: -DTESTING
-    build-depends:
-        QuickCheck >= 2.7
-      , random
   exposed-modules:
       Yi
       Yi.Buffer
-      Yi.Buffer.Adjusted
       Yi.Buffer.HighLevel
       Yi.Buffer.Indent
       Yi.Buffer.Normal
@@ -207,7 +200,7 @@
     , unordered-containers >= 0.1.3
     , xdg-basedir >= 0.2.1
     , yi-language >= 0.1.1.0
-    , yi-rope >= 0.7.0.0
+    , yi-rope >= 0.7.0.0 && < 0.9
     , tasty
     , tasty-hunit
     , tasty-quickcheck
@@ -265,7 +258,7 @@
     , unordered-containers >= 0.1.3
     , xdg-basedir >= 0.2.1
     , yi-language >= 0.1.1.0
-    , yi-rope >= 0.7.0.0
+    , yi-rope >= 0.7.0.0 && < 0.9
     , yi-core
     , criterion
     , deepseq
