diff --git a/src/library/Yi/Buffer/HighLevel.hs b/src/library/Yi/Buffer/HighLevel.hs
--- a/src/library/Yi/Buffer/HighLevel.hs
+++ b/src/library/Yi/Buffer/HighLevel.hs
@@ -56,6 +56,7 @@
     , lastNonSpaceB
     , leftEdgesOfRegionB
     , leftOnEol
+    , lineMoveVisRel
     , linePrefixSelectionB
     , lineStreamB
     , lowercaseWordB
@@ -1152,3 +1153,39 @@
     if leftChar == 'x' && leftToLeftChar == '0'
     then return True
     else return False
+
+-- | Move point down by @n@ lines
+-- If line extends past width of window, count moving
+-- a single line as moving width points to the right.
+lineMoveVisRel :: Int -> BufferM ()
+lineMoveVisRel = movingToPrefVisCol . lineMoveVisRelUp
+
+lineMoveVisRelUp :: Int -> BufferM ()
+lineMoveVisRelUp 0 = return ()
+lineMoveVisRelUp n | n < 0 = lineMoveVisRelDown $ negate n
+                   | otherwise = do
+    wid <- width <$> use lastActiveWindowA
+    col <- curCol
+    len <- pointB >>= eolPointB >>= colOf
+    let jumps = (len `div` wid) - (col `div` wid)
+        next = n - jumps
+    if next <= 0
+        then moveXorEol (n * wid)
+        else do moveXorEol (jumps * wid)
+                void $ gotoLnFrom 1
+                lineMoveVisRelUp $ next - 1
+
+lineMoveVisRelDown :: Int -> BufferM ()
+lineMoveVisRelDown 0 = return ()
+lineMoveVisRelDown n | n < 0 = lineMoveVisRelUp $ negate n
+                     | otherwise = do
+    wid <- width <$> use lastActiveWindowA
+    col <- curCol
+    let jumps = col `div` wid
+        next = n - jumps
+    if next <= 0
+        then leftN (n * wid)
+        else do leftN (jumps * wid)
+                void $ gotoLnFrom $ -1
+                moveToEol
+                lineMoveVisRelDown $ next - 1
diff --git a/src/library/Yi/Buffer/Misc.hs b/src/library/Yi/Buffer/Misc.hs
--- a/src/library/Yi/Buffer/Misc.hs
+++ b/src/library/Yi/Buffer/Misc.hs
@@ -100,6 +100,7 @@
   , savingPrefCol
   , forgetPreferCol
   , movingToPrefCol
+  , movingToPrefVisCol
   , preferColA
   , markSavedB
   , addOverlayB
@@ -552,6 +553,7 @@
             , bkey__ = unique
             , undos  = emptyU
             , preferCol = Nothing
+            , preferVisCol = Nothing
             , bufferDynamic = mempty
             , pendingUpdates = []
             , selectionStyle = SelectionStyle False False
@@ -855,6 +857,18 @@
   preferColA .= Just targetCol
   return r
 
+-- | Moves to a visual column within the current line as shown
+-- on the editor (ie, moving within the current width of a
+-- single visual line)
+movingToPrefVisCol :: BufferM a -> BufferM a
+movingToPrefVisCol f = do
+  prefCol <- use preferVisColA
+  targetCol <- maybe curVisCol return prefCol
+  r <- f
+  moveToVisColB targetCol
+  preferVisColA .= Just targetCol
+  return r
+
 moveToColB :: Int -> BufferM ()
 moveToColB targetCol = do
   solPnt <- solPointB =<< pointB
@@ -864,6 +878,13 @@
       toSkip = takeWhile (\(char,col) -> char /= '\n' && col < targetCol) (zip chrs cols)
   moveTo $ solPnt +~ fromIntegral (length toSkip)
 
+moveToVisColB :: Int -> BufferM ()
+moveToVisColB targetCol = do
+  col <- curCol
+  wid <- width <$> use lastActiveWindowA
+  let jumps = col `div` wid
+  moveToColB $ jumps * wid + targetCol
+
 moveToLineColB :: Int -> Int -> BufferM ()
 moveToLineColB line col = gotoLn line >> moveToColB col
 
@@ -871,13 +892,15 @@
 pointOfLineColB line col = savingPointB $ moveToLineColB line col >> pointB
 
 forgetPreferCol :: BufferM ()
-forgetPreferCol = preferColA .= Nothing
+forgetPreferCol = preferColA .= Nothing >> preferVisColA .= Nothing
 
 savingPrefCol :: BufferM a -> BufferM a
 savingPrefCol f = do
   pc <- use preferColA
+  pv <- use preferVisColA
   result <- f
   preferColA .= pc
+  preferVisColA .= pv
   return result
 
 -- | Move point up one line
@@ -973,6 +996,10 @@
 -- (This takes into account tabs, unicode chars, etc.)
 curCol :: BufferM Int
 curCol = colOf =<< pointB
+
+-- | Current column, visually.
+curVisCol :: BufferM Int
+curVisCol = rem <$> curCol <*> (width <$> use lastActiveWindowA)
 
 colOf :: Point -> BufferM Int
 colOf p = do
diff --git a/src/library/Yi/Buffer/TextUnit.hs b/src/library/Yi/Buffer/TextUnit.hs
--- a/src/library/Yi/Buffer/TextUnit.hs
+++ b/src/library/Yi/Buffer/TextUnit.hs
@@ -98,10 +98,18 @@
 unitDelimited :: Char -> Char -> Bool -> TextUnit
 unitDelimited left right included = GenUnit Document $ \direction ->
    case (included,direction) of
-       (False, Backward) -> checkPeekB 0 [(== left)] Backward
-       (False, Forward)  -> (== right) <$> readB
-       (True,  Backward) -> checkPeekB (-1) [(== left)] Backward
-       (True,  Forward)  -> checkPeekB 0 [(== right)] Backward
+       (False, Backward) -> do
+           isCursorOnLeftChar <- (== left) <$> readB
+           when isCursorOnLeftChar rightB
+           checkPeekB 0 [(== left)] Backward
+       (False, Forward)  -> do
+           isCursorOnRightChar <- (== right) <$> readB
+           isTextUnitBlank <- checkPeekB 0 [(== left)] Backward
+           if isTextUnitBlank && isCursorOnRightChar
+           then leftB >> return True
+           else return isCursorOnRightChar
+       (True,  Backward) -> checkPeekB 0 [(== left)] Forward
+       (True,  Forward)  -> rightB >> checkPeekB 0 [(== right)] Backward
 
 isWordChar :: Char -> Bool
 isWordChar x = isAlphaNum x || x == '_'
diff --git a/src/library/Yi/Editor.hs b/src/library/Yi/Editor.hs
--- a/src/library/Yi/Editor.hs
+++ b/src/library/Yi/Editor.hs
@@ -134,10 +134,10 @@
 import           Yi.Window
 
 instance Binary Editor where
-  put (Editor bss bs supply ts dv _sl msh kr _re _dir _ev _cwa ) =
+  put (Editor bss bs supply ts dv _sl msh kr re _dir _ev _cwa ) =
     let putNE (x :| xs) = put x >> put xs
     in putNE bss >> put bs >> put supply >> put ts
-       >> put dv >> put msh >> put kr
+       >> put dv >> put msh >> put kr >> put re
   get = do
     bss <- (:|) <$> get <*> get
     bs <- get
@@ -146,6 +146,7 @@
     dv <- get
     msh <- get
     kr <- get
+    re <- get
     return $ emptyEditor { bufferStack = bss
                          , buffers = bs
                          , refSupply = supply
@@ -153,6 +154,7 @@
                          , dynamic = dv
                          , maxStatusHeight = msh
                          , killring = kr
+                         , currentRegex = re
                          }
 
 -- | The initial state
diff --git a/src/library/Yi/Eval.hs b/src/library/Yi/Eval.hs
--- a/src/library/Yi/Eval.hs
+++ b/src/library/Yi/Eval.hs
@@ -3,6 +3,7 @@
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# OPTIONS_HADDOCK show-extensions #-}
 
 -- |
@@ -33,8 +34,12 @@
 ) where
 
 import           Control.Applicative ((<$>), (<*>))
+import           Control.Concurrent
+import           Control.Monad.Catch (try)
 import           Control.Lens hiding (Action)
 import           Control.Monad hiding (mapM_)
+import           Control.Monad.Base
+import           Control.Monad.Trans (lift)
 import           Data.Array
 import           Data.Binary
 import           Data.Default
@@ -51,7 +56,6 @@
 import           Yi.Buffer
 import           Yi.Config.Simple.Types
 import           Yi.Core (errorEditor, runAction)
-import           Yi.Debug
 import           Yi.Types (YiVariable,YiConfigVariable)
 import           Yi.Editor
 import           Yi.File
@@ -108,6 +112,49 @@
     def = NamesCache []
 instance YiVariable NamesCache
 
+type HintRequest = (String, MVar (Either LHI.InterpreterError Action))
+newtype HintThreadVar = HintThreadVar (Maybe (MVar HintRequest))
+  deriving (Typeable, Default)
+
+instance Binary HintThreadVar where
+  put _ = return ()
+  get = return def
+instance YiVariable HintThreadVar
+
+getHintThread :: (MonadEditor m, MonadBase IO m) => m (MVar HintRequest)
+getHintThread = do
+  HintThreadVar x <- getEditorDyn
+  case x of
+    Just t -> return t
+    Nothing -> do
+      req <- io newEmptyMVar
+      contextFile <- Yi.Paths.getEvaluatorContextFilename
+      void . io . forkIO $ hintEvaluatorThread req contextFile
+      putEditorDyn . HintThreadVar $ Just req
+      return req
+
+hintEvaluatorThread :: MVar HintRequest -> FilePath -> IO ()
+hintEvaluatorThread request contextFile = do
+  haveUserContext <- doesFileExist contextFile
+  void $ LHI.runInterpreter $ do
+    LHI.set [LHI.searchPath LHI.:= []]
+
+    -- We no longer have Yi.Prelude, perhaps we should remove
+    -- NoImplicitPrelude?
+    LHI.set [LHI.languageExtensions LHI.:= [ LHI.OverloadedStrings
+                                           , LHI.NoImplicitPrelude
+                                           ]]
+    when haveUserContext $ do
+      LHI.loadModules [contextFile]
+      LHI.setTopLevelModules ["Env"]
+
+    -- Yi.Keymap: Action lives there
+    LHI.setImportsQ [("Yi", Nothing), ("Yi.Keymap",Just "Yi.Keymap")]
+    forever $ do
+      (s,response) <- lift $ takeMVar request
+      res <- try $ LHI.interpret ("Yi.makeAction (" ++ s ++ ")") (LHI.as :: Action)
+      lift $ putMVar response res
+
 -- Evaluator implemented by calling GHCi. This evaluator can run
 -- arbitrary expressions in the class 'YiAction'.
 --
@@ -129,23 +176,11 @@
     execAction :: String -> YiM ()
     execAction "reload" = reload
     execAction s = do
-      contextFile <- Yi.Paths.getEvaluatorContextFilename
-      haveUserContext <- io $ doesFileExist contextFile
-      res <- io $ LHI.runInterpreter $ do
-        LHI.set [LHI.searchPath LHI.:= []]
-
-        -- We no longer have Yi.Prelude, perhaps we should remove
-        -- NoImplicitPrelude?
-        LHI.set [LHI.languageExtensions LHI.:= [ LHI.OverloadedStrings
-                                               , LHI.NoImplicitPrelude
-                                               ]]
-        when haveUserContext $ do
-          LHI.loadModules [contextFile]
-          LHI.setTopLevelModules ["Env"]
-
-        -- Yi.Keymap: Action lives there
-        LHI.setImportsQ [("Yi", Nothing), ("Yi.Keymap",Just "Yi.Keymap")]
-        LHI.interpret ("Yi.makeAction (" ++ s ++ ")") (error "as" :: Action)
+      request <- getHintThread
+      res <- io $ do
+        response <- newEmptyMVar
+        putMVar request (s,response)
+        takeMVar response
       case res of
         Left err -> errorEditor (showT err)
         Right action -> runAction action
diff --git a/src/library/Yi/File.hs b/src/library/Yi/File.hs
--- a/src/library/Yi/File.hs
+++ b/src/library/Yi/File.hs
@@ -1,5 +1,7 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
 {-# OPTIONS_HADDOCK show-extensions #-}
 
 -- |
@@ -13,6 +15,7 @@
   -- * File-based actions
   editFile,
   openingNewFile,
+  openNewFile,
 
   viWrite, viWriteTo, viSafeWriteTo,
   fwriteE,        -- :: YiM ()
@@ -24,19 +27,25 @@
 
   -- * Helper functions
   setFileName,
-  deservesSave
+  deservesSave,
+
+  -- * Configuration
+  preSaveHooks
  ) where
 
 import           Control.Applicative
-import           Control.Lens hiding (act)
-import           Control.Monad (void)
+import           Control.Lens hiding (act, Action)
+import           Control.Monad (when, void)
 import           Control.Monad.Base
+import           Data.Default
 import           Data.Monoid
 import qualified Data.Text as T
+import           Data.Typeable
 import           Data.Time
 import           System.Directory
 import           System.FriendlyPath
 import           Yi.Buffer
+import           Yi.Config.Simple.Types (customVariable, Field)
 import           Yi.Core
 import           Yi.Dired
 import           Yi.Editor
@@ -44,8 +53,22 @@
 import           Yi.Monad
 import qualified Yi.Rope as R
 import           Yi.String
+import           Yi.Types
 import           Yi.Utils
 
+newtype PreSaveHooks = PreSaveHooks { _unPreSaveHooks :: [Action] }
+    deriving Typeable
+
+instance Default PreSaveHooks where
+    def = PreSaveHooks []
+
+instance YiConfigVariable PreSaveHooks
+
+makeLenses ''PreSaveHooks
+
+preSaveHooks :: Field [Action]
+preSaveHooks = customVariable . unPreSaveHooks
+
 -- | Tries to open a new buffer with 'editFile' and runs the given
 -- action on the buffer handle if it succeeds.
 --
@@ -55,6 +78,10 @@
   Left m -> printMsg m
   Right ref -> void $ withGivenBuffer ref act
 
+-- | Same as @openingNewFile@ with no action to run after.
+openNewFile :: FilePath -> YiM ()
+openNewFile = flip openingNewFile $ return ()
+
 -- | Revert to the contents of the file on disk
 revertE :: YiM ()
 revertE = do
@@ -81,22 +108,22 @@
    Just f  -> do
        bufInfo <- withCurrentBuffer bufInfoB
        let s   = bufInfoFileName bufInfo
-       fwriteE
+       succeed <- fwriteE
        let message = (showT f <>) (if f == s
                          then " written"
                          else " " <> showT s <> " written")
-       printMsg message
+       when succeed $ printMsg message
 
 -- | Try to write to a named file in the manner of vi/vim
 viWriteTo :: T.Text -> YiM ()
 viWriteTo f = do
   bufInfo <- withCurrentBuffer bufInfoB
   let s   = T.pack $ bufInfoFileName bufInfo
-  fwriteToE f
+  succeed <- fwriteToE f
   let message = f `T.append` if f == s
                              then " written"
                              else ' ' `T.cons` s `T.append` " written"
-  printMsg message
+  when succeed $ printMsg message
 
 -- | Try to write to a named file if it doesn't exist. Error out if it does.
 viSafeWriteTo :: T.Text -> YiM ()
@@ -107,11 +134,11 @@
     else viWriteTo f
 
 -- | Write current buffer to disk, if this buffer is associated with a file
-fwriteE :: YiM ()
+fwriteE :: YiM Bool
 fwriteE = fwriteBufferE =<< gets currentBuffer
 
 -- | Write a given buffer to disk if it is associated with a file.
-fwriteBufferE :: BufferRef -> YiM ()
+fwriteBufferE :: BufferRef -> YiM Bool
 fwriteBufferE bufferKey = do
   nameContents <- withGivenBuffer bufferKey $ do
     fl <- gets file
@@ -121,27 +148,32 @@
 
   case nameContents of
     (Just f, contents, conv) -> io (doesDirectoryExist f) >>= \case
-      True -> printMsg "Can't save over a directory, doing nothing."
+      True -> printMsg "Can't save over a directory, doing nothing." >> return False
       False -> do
-        liftBase $ case conv of
-          Nothing -> R.writeFileUsingText f contents
+        hooks <- view preSaveHooks <$> askCfg
+        sequence_ $ map runAction hooks
+        mayErr <- liftBase $ case conv of
+          Nothing -> R.writeFileUsingText f contents >> return Nothing
           Just cn -> R.writeFile f contents cn
-        io getCurrentTime >>= withGivenBuffer bufferKey . markSavedB
-    (Nothing, _, _)      -> printMsg "Buffer not associated with a file"
+        case mayErr of
+          Just err -> printMsg err >> return False
+          Nothing -> io getCurrentTime >>= withGivenBuffer bufferKey . markSavedB
+                     >> return True
+    (Nothing, _, _) -> printMsg "Buffer not associated with a file" >> return False
 
 -- | Write current buffer to disk as @f@. The file is also set to @f@.
-fwriteToE :: T.Text -> YiM ()
+fwriteToE :: T.Text -> YiM Bool
 fwriteToE f = do
   b <- gets currentBuffer
   setFileName b (T.unpack f)
   fwriteBufferE b
 
 -- | Write all open buffers
-fwriteAllE :: YiM ()
+fwriteAllE :: YiM Bool
 fwriteAllE =
   do allBuffs <- gets bufferSet
      let modifiedBuffers = filter (not . isUnchangedBuffer) allBuffs
-     mapM_ fwriteBufferE (fmap bkey modifiedBuffers)
+     all id <$> mapM fwriteBufferE (fmap bkey modifiedBuffers)
 
 -- | Make a backup copy of file
 backupE :: FilePath -> YiM ()
diff --git a/src/library/Yi/Keymap/Emacs.hs b/src/library/Yi/Keymap/Emacs.hs
--- a/src/library/Yi/Keymap/Emacs.hs
+++ b/src/library/Yi/Keymap/Emacs.hs
@@ -268,7 +268,7 @@
                  , ctrlCh 'q'    ?>>!
                      ((withCurrentBuffer (readOnlyA %= not)) :: EditorM ())
                  , ctrlCh 's'    ?>>! fwriteE
-                 , ctrlCh 'w'    ?>>! promptFile "Write file:" fwriteToE
+                 , ctrlCh 'w'    ?>>! promptFile "Write file:" (void . fwriteToE)
                  , ctrlCh 'x'    ?>>! (exchangePointAndMarkB >>
                                        assign highlightSelectionA True)
                  , char 'b'      ?>>! switchBufferE
diff --git a/src/library/Yi/Keymap/Emacs/Utils.hs b/src/library/Yi/Keymap/Emacs/Utils.hs
--- a/src/library/Yi/Keymap/Emacs/Utils.hs
+++ b/src/library/Yi/Keymap/Emacs/Utils.hs
@@ -121,7 +121,7 @@
                         >>! closeBufferAndWindowE
                         -- cancel
                       ] ++ [char 'q' ?>>! quitEditor | hasQuit])
-  yesAction = do fwriteBufferE (bkey firstBuffer)
+  yesAction = do void $ fwriteBufferE (bkey firstBuffer)
                  withEditor closeBufferAndWindowE
                  continue
 
diff --git a/src/library/Yi/Keymap/Vim/Ex/Commands/Edit.hs b/src/library/Yi/Keymap/Vim/Ex/Commands/Edit.hs
--- a/src/library/Yi/Keymap/Vim/Ex/Commands/Edit.hs
+++ b/src/library/Yi/Keymap/Vim/Ex/Commands/Edit.hs
@@ -36,7 +36,7 @@
     cmdShow = showEdit tab f
   , cmdAction = YiA $ do
         when tab $ withEditor newTabE
-        void . editFile $ T.unpack f
+        openNewFile $ T.unpack f
   , cmdComplete = (fmap . fmap)
                     (showEdit tab) (Common.filenameComplete f)
   }
diff --git a/src/library/Yi/Keymap/Vim/Ex/Commands/Quit.hs b/src/library/Yi/Keymap/Vim/Ex/Commands/Quit.hs
--- a/src/library/Yi/Keymap/Vim/Ex/Commands/Quit.hs
+++ b/src/library/Yi/Keymap/Vim/Ex/Commands/Quit.hs
@@ -96,4 +96,6 @@
                         <> " (add ! to override)"
 
 saveAndQuitAllE :: YiM ()
-saveAndQuitAllE = Common.forAllBuffers fwriteBufferE >> quitEditor
+saveAndQuitAllE = do
+    succeed <- fwriteAllE
+    when succeed $ quitEditor
diff --git a/src/library/Yi/Keymap/Vim/Ex/Commands/Substitute.hs b/src/library/Yi/Keymap/Vim/Ex/Commands/Substitute.hs
--- a/src/library/Yi/Keymap/Vim/Ex/Commands/Substitute.hs
+++ b/src/library/Yi/Keymap/Vim/Ex/Commands/Substitute.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# OPTIONS_HADDOCK show-extensions #-}
 
@@ -16,7 +17,10 @@
 import qualified Data.Text as T
 import qualified Text.ParserCombinators.Parsec as P
 import           Yi.Buffer.Adjusted hiding (Delete)
+import           Yi.Editor
+import           Yi.MiniBuffer
 import           Yi.Keymap
+import           Yi.Keymap.Keys
 import           Yi.Keymap.Vim.Common
 import qualified Yi.Keymap.Vim.Ex.Commands.Common as Common
 import           Yi.Keymap.Vim.Ex.Types
@@ -32,30 +36,91 @@
     void $ P.char delimiter
     to <- R.fromString <$> P.many (P.noneOf [delimiter])
     void $ P.char delimiter
-    flagChars <- P.many (P.oneOf "gi")
+    flagChars <- P.many (P.oneOf "gic")
     return $! substitute from to delimiter
         ('g' `elem` flagChars)
         ('i' `elem` flagChars)
+        ('c' `elem` flagChars)
         (not $ null percents)
 
-substitute :: R.YiString -> R.YiString -> Char -> Bool -> Bool -> Bool -> ExCommand
-substitute from to delimiter global caseInsensitive allLines = Common.pureExCommand {
+substitute :: R.YiString -> R.YiString -> Char -> Bool -> Bool -> Bool -> Bool -> ExCommand
+substitute from to delimiter global caseInsensitive confirm allLines = Common.pureExCommand {
     cmdShow = (if allLines then "%" else "")
               <>       "substitute"
               <>       (delimiter `T.cons` R.toText from)
               <>       (delimiter `T.cons` R.toText to)
               `T.snoc` delimiter
+              <>       (if confirm then "c" else "")
               <>       (if caseInsensitive then "i" else "")
               <>       (if global then "g" else "")
-  , cmdAction = BufferA $ do
-        let regex = makeSimpleSearch from
-            replace = do
-                region <- regionOfB Line
-                void $ searchAndRepRegion0 regex to global region
+  , cmdAction = EditorA $ do
+        regex <- if R.null from
+                    then getRegexE
+                    else return . Just . makeSimpleSearch $ from
+        case regex of
+            Nothing -> printMsg "No previous search pattern"
+            Just regex' -> if confirm
+                then substituteConfirm regex' to global allLines
+                else withCurrentBuffer $ do
+                    let replace = void $ regionOfB Line
+                                  >>= searchAndRepRegion0 regex' to global
+                    if allLines
+                        then withEveryLineB replace
+                        else replace
+                    moveToSol
+  }
 
-        if allLines
-        then withEveryLineB replace
-        else replace
+-- | Run substitution in confirm mode
+substituteConfirm :: SearchExp -> R.YiString -> Bool -> Bool -> EditorM ()
+substituteConfirm regex to global allLines = do
+    setRegexE regex
+    regions <- withCurrentBuffer $ findMatches regex global allLines
+    substituteMatch to 0 False regions
 
-        moveToSol
-  }
+-- | All matches to replace under given flags
+findMatches :: SearchExp -> Bool -> Bool -> BufferM [Region]
+findMatches regex global allLines = do
+    lns <- if allLines
+        then do lineCount <- lineCountB
+                lineRegions [1..lineCount]
+        else return <$> regionOfB Line
+    let f = if global then id else take 1
+    concat <$> mapM (fmap f . regexRegionB regex) lns
+
+-- | Get regions corresponding to all lines
+lineRegions :: [Int] -> BufferM [Region]
+lineRegions = mapM $ \ln -> gotoLn ln >> regionOfB Line
+
+-- | Offsets a region (to account for a region prior being modified)
+offsetRegion :: Int -> Region -> Region
+offsetRegion k reg = mkRegion (regionStart reg + k') (regionEnd reg + k')
+    where k' = fromIntegral k
+
+-- | Runs a list of matches using itself as a continuation
+substituteMatch :: R.YiString -> Int -> Bool -> [Region] -> EditorM ()
+substituteMatch _ _ _ [] = resetRegexE
+substituteMatch to co autoAll (m:ms) = do
+    let m' = offsetRegion co m
+    withCurrentBuffer . moveTo $ regionStart m'
+    len <- withCurrentBuffer $ R.length <$> readRegionB m'
+    let diff = R.length to - len
+        tex = "replace with " <> R.toText to <> " (y/n/a/q)?"
+    if autoAll
+        then do withCurrentBuffer $ replaceRegionB m' to
+                substituteMatch to (co + diff) True ms
+        else void . spawnMinibufferE tex . const $ askKeymap to co (co + diff) m ms
+
+-- | Actual choices during confirm mode.
+askKeymap :: R.YiString -> Int -> Int -> Region -> [Region] -> Keymap
+askKeymap to co co' m ms = choice [
+      char 'n' ?>>! cleanUp >> substituteMatch to co False ms
+    , char 'a' ?>>! do cleanUp
+                       replace
+                       substituteMatch to co' True ms
+    , char 'y' ?>>! do cleanUp
+                       replace
+                       substituteMatch to co' False ms
+    , char 'q' ?>>! cleanUp >> resetRegexE
+    ]
+    where cleanUp = closeBufferAndWindowE
+          replace = withCurrentBuffer $ replaceRegionB (offsetRegion co m) to
diff --git a/src/library/Yi/Keymap/Vim/Ex/Commands/Write.hs b/src/library/Yi/Keymap/Vim/Ex/Commands/Write.hs
--- a/src/library/Yi/Keymap/Vim/Ex/Commands/Write.hs
+++ b/src/library/Yi/Keymap/Vim/Ex/Commands/Write.hs
@@ -53,4 +53,4 @@
 tryWriteBuffer :: BufferRef -> YiM ()
 tryWriteBuffer buf = do
     ns <- Common.needsSaving buf
-    when ns $ fwriteBufferE buf
+    when ns . void $ fwriteBufferE buf
diff --git a/src/library/Yi/Keymap/Vim/ExMap.hs b/src/library/Yi/Keymap/Vim/ExMap.hs
--- a/src/library/Yi/Keymap/Vim/ExMap.hs
+++ b/src/library/Yi/Keymap/Vim/ExMap.hs
@@ -89,6 +89,7 @@
     resetCountE
     switchModeE Normal
     closeBufferAndWindowE
+    withCurrentBuffer $ setVisibleSelection False
 
 exitBinding :: VimBinding
 exitBinding = VimBindingE f
diff --git a/src/library/Yi/Keymap/Vim/Motion.hs b/src/library/Yi/Keymap/Vim/Motion.hs
--- a/src/library/Yi/Keymap/Vim/Motion.hs
+++ b/src/library/Yi/Keymap/Vim/Motion.hs
@@ -89,6 +89,8 @@
 linewiseMotions :: [(EventString, Bool, Maybe Int -> BufferM ())]
 linewiseMotions = fmap withDefaultCount
     [ ("j", False, void . lineMoveRel)
+    , ("gj", False, void . lineMoveVisRel)
+    , ("gk", False, void . lineMoveVisRel . negate)
     , ("k", False, void . lineMoveRel . negate)
     , ("<Down>", False, void . lineMoveRel)
     , ("<Up>", False, void . lineMoveRel . negate)
diff --git a/src/library/Yi/Keymap/Vim/NormalMap.hs b/src/library/Yi/Keymap/Vim/NormalMap.hs
--- a/src/library/Yi/Keymap/Vim/NormalMap.hs
+++ b/src/library/Yi/Keymap/Vim/NormalMap.hs
@@ -22,11 +22,12 @@
 import qualified Data.Text as T
 import           Prelude hiding (null, lookup)
 import           System.Directory (doesFileExist)
+import           System.FriendlyPath (expandTilda)
 import           Yi.Buffer.Adjusted hiding (Insert)
 import           Yi.Core (quitEditor, closeWindow)
 import           Yi.Editor
 import           Yi.Event
-import           Yi.File (editFile, fwriteE)
+import           Yi.File (openNewFile, fwriteE)
 import           Yi.History
 import           Yi.Keymap
 import           Yi.Keymap.Keys
@@ -392,7 +393,7 @@
           f _ _ = NoMatch
 
 continueSearching :: (Direction -> Direction) -> EditorM ()
-continueSearching fdir = do
+continueSearching fdir =
   getRegexE >>= \case
     Just regex -> do
       dir <- fdir <$> use searchDirectionA
@@ -452,12 +453,12 @@
 openFileUnderCursor :: Maybe (EditorM ()) -> YiM ()
 openFileUnderCursor editorAction = do
   fileName <- fmap R.toString . withCurrentBuffer $ readUnitB unitViWORD
-  fileExists <- io $ doesFileExist fileName
-  if (not fileExists) then
-      withEditor . fail $ "Can't find file \"" <> fileName <> "\""
-  else do
+  fileExists <- io $ doesFileExist =<< expandTilda fileName
+  if fileExists then do
       maybeM withEditor editorAction
-      void . editFile $ fileName
+      openNewFile $ fileName
+  else
+      withEditor . fail $ "Can't find file \"" <> fileName <> "\""
 
 recordMacroBinding :: VimBinding
 recordMacroBinding = VimBindingE (f . T.unpack . _unEv)
diff --git a/src/library/Yi/Keymap/Vim/Operator.hs b/src/library/Yi/Keymap/Vim/Operator.hs
--- a/src/library/Yi/Keymap/Vim/Operator.hs
+++ b/src/library/Yi/Keymap/Vim/Operator.hs
@@ -108,6 +108,9 @@
         withCurrentBuffer $ do
             point <- deleteRegionWithStyleB reg style
             moveTo point
+            when (style == LineWise) $ do
+                insertB '\n'
+                leftB
         switchModeE $ Insert 'c'
         return Continue
 }
diff --git a/src/library/Yi/Keymap/Vim/Tag.hs b/src/library/Yi/Keymap/Vim/Tag.hs
--- a/src/library/Yi/Keymap/Vim/Tag.hs
+++ b/src/library/Yi/Keymap/Vim/Tag.hs
@@ -23,6 +23,7 @@
     ) where
 
 import           Control.Applicative
+import           Control.Lens (view)
 import           Control.Monad
 import           Data.Binary
 import           Data.Default
@@ -181,7 +182,7 @@
 -- file exists.
 tagsFile :: YiM (Maybe FilePath)
 tagsFile = do
-    fs <- withEditor getTagsFileList
+    fs <- view tagsFileList <$> askCfg
     let g f' f = do
         case f' of
             Just _ -> return f'
diff --git a/src/library/Yi/Main.hs b/src/library/Yi/Main.hs
--- a/src/library/Yi/Main.hs
+++ b/src/library/Yi/Main.hs
@@ -133,9 +133,9 @@
                          []     -> fail "The `-l' option must come after a file argument"
 
       File filename -> if shouldOpenInTabs && not (null (startActions cfg)) then
-                         prependActions [YiA (void $ editFile filename), EditorA newTabE]
+                         prependActions [YiA $ openNewFile filename, EditorA newTabE]
                        else
-                         prependAction (void $ editFile filename)
+                         prependAction $ openNewFile filename
 
       EditorNm emul -> case lookup (fmap toLower emul) editors of
              Just modifyCfg -> return (modifyCfg cfg, cfgcon)
diff --git a/src/library/Yi/Mode/Haskell.hs b/src/library/Yi/Mode/Haskell.hs
--- a/src/library/Yi/Mode/Haskell.hs
+++ b/src/library/Yi/Mode/Haskell.hs
@@ -422,7 +422,7 @@
 -- | Load current buffer in GHCi
 ghciLoadBuffer :: YiM ()
 ghciLoadBuffer = do
-    fwriteE
+    void $ fwriteE
     f <- withCurrentBuffer (gets file)
     case f of
       Nothing -> error "Couldn't get buffer filename in ghciLoadBuffer"
diff --git a/src/library/Yi/PersistentState.hs b/src/library/Yi/PersistentState.hs
--- a/src/library/Yi/PersistentState.hs
+++ b/src/library/Yi/PersistentState.hs
@@ -16,9 +16,11 @@
 
 module Yi.PersistentState(loadPersistentState,
                           savePersistentState,
-                          maxHistoryEntries)
+                          maxHistoryEntries,
+                          persistentSearch)
 where
 
+import Control.Monad
 import Data.Typeable
 import Data.Binary
 #if __GLASGOW_HASKELL__ < 708
@@ -69,6 +71,19 @@
 maxHistoryEntries :: Field Int
 maxHistoryEntries = customVariable . unMaxHistoryEntries
 
+newtype PersistentSearch = PersistentSearch { _unPersistentSearch :: Bool }
+  deriving(Typeable, Binary)
+
+instance Default PersistentSearch where
+  def = PersistentSearch True
+
+instance YiConfigVariable PersistentSearch
+
+makeLenses ''PersistentSearch
+
+persistentSearch :: Field Bool
+persistentSearch = customVariable . unPersistentSearch
+
 -- | Trims per-command histories to contain at most N completions each.
 trimHistories :: Int -> Histories -> Histories
 trimHistories maxHistory (Histories m) = Histories $ M.map trimH m
@@ -116,6 +131,8 @@
     maybePState <- readPersistentState
     case maybePState of
       Nothing     -> return ()
-      Just pState -> do withEditor $ putEditorDyn                $ histories     pState
-                        withEditor $ assign killringA            $ aKillring     pState
-                        withEditor $ maybe (return ()) setRegexE $ aCurrentRegex pState
+      Just pState -> do putEditorDyn                $ histories     pState
+                        assign killringA            $ aKillring     pState
+                        PersistentSearch keepSearch <- askConfigVariableA
+                        when keepSearch . withEditor $
+                            maybe (return ()) setRegexE $ aCurrentRegex pState
diff --git a/src/library/Yi/Tag.hs b/src/library/Yi/Tag.hs
--- a/src/library/Yi/Tag.hs
+++ b/src/library/Yi/Tag.hs
@@ -28,11 +28,11 @@
               , getTags
               , setTags
               , resetTags
-              , getTagsFileList
-              , setTagsFileList
+              , tagsFileList
               ) where
 
 import           Control.Applicative
+import           Control.Lens
 import           Data.Binary
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.UTF8 as BS8
@@ -44,7 +44,6 @@
 import           Data.Default
 import qualified Data.Foldable as F
 import           Data.List (isPrefixOf)
-import           Data.List.Split (splitOn)
 import           Data.Map (Map, fromListWith, lookup, keys)
 import           Data.Maybe (mapMaybe)
 import qualified Data.Text as T
@@ -54,16 +53,28 @@
 import           System.FilePath (takeFileName, takeDirectory, (</>))
 import           System.FriendlyPath
 import           Yi.Editor
-import           Yi.Types (YiVariable)
+import           Yi.Config.Simple.Types (Field, customVariable)
+import           Yi.Types (YiVariable, YiConfigVariable)
 
+newtype TagsFileList  = TagsFileList { _unTagsFileList :: [FilePath] }
+    deriving Typeable
 
+instance Default TagsFileList where
+    def = TagsFileList ["tags"]
+
+instance YiConfigVariable TagsFileList
+
+makeLenses ''TagsFileList
+
+tagsFileList :: Field [FilePath]
+tagsFileList = customVariable . unTagsFileList
+
 newtype Tags  = Tags (Maybe TagTable) deriving Typeable
+
 instance Default Tags where
     def = Tags Nothing
 
-newtype TagsFileList  = TagsFileList [FilePath] deriving Typeable
-instance Default TagsFileList where
-    def = TagsFileList ["tags"]
+instance YiVariable Tags
 
 newtype Tag = Tag { _unTag :: T.Text } deriving (Show, Eq, Ord)
 
@@ -151,16 +162,6 @@
   Tags t <- getEditorDyn
   return t
 
-setTagsFileList :: String -> EditorM ()
-setTagsFileList fps = do
-  resetTags
-  putEditorDyn $ TagsFileList (splitOn "," fps)
-
-getTagsFileList :: EditorM [FilePath]
-getTagsFileList = do
-  TagsFileList fps <- getEditorDyn
-  return fps
-
 #if __GLASGOW_HASKELL__ < 708
 $(derive makeBinary ''Tags)
 $(derive makeBinary ''TagTable)
@@ -173,7 +174,3 @@
 instance Binary TagTable
 instance Binary TagsFileList
 #endif
-
-instance YiVariable Tags
-
-instance YiVariable TagsFileList
diff --git a/src/library/Yi/Types.hs b/src/library/Yi/Types.hs
--- a/src/library/Yi/Types.hs
+++ b/src/library/Yi/Types.hs
@@ -213,6 +213,7 @@
                 , undos  :: !URList               -- ^ undo/redo list
                 , bufferDynamic :: !DynamicState.DynamicState  -- ^ dynamic components
                 , preferCol :: !(Maybe Int)       -- ^ prefered column to arrive at when we do a lineDown / lineUp
+                , preferVisCol :: !(Maybe Int)    -- ^ prefered column to arrive at visually (ie, respecting wrap)
                 , pendingUpdates :: ![UIUpdate]   -- ^ updates that haven't been synched in the UI yet
                 , selectionStyle :: !SelectionStyle
                 , keymapProcess :: !KeymapProcess
@@ -233,15 +234,15 @@
 
 
 instance Binary Yi.Types.Attributes where
-    put (Yi.Types.Attributes n b u bd pc pu selectionStyle_
+    put (Yi.Types.Attributes n b u bd pc pv pu selectionStyle_
          _proc wm law lst ro ins _dc _pfw isTransacPresent transacAccum fv cn) = do
       let putTime (UTCTime x y) = B.put (fromEnum x) >> B.put (fromEnum y)
       B.put n >> B.put b >> B.put u >> B.put bd
-      B.put pc >> B.put pu >> B.put selectionStyle_ >> B.put wm
+      B.put pc >> B.put pv >> B.put pu >> B.put selectionStyle_ >> B.put wm
       B.put law >> putTime lst >> B.put ro >> B.put ins >> B.put _dc
       B.put isTransacPresent >> B.put transacAccum >> B.put fv >> B.put cn
     get = Yi.Types.Attributes <$> B.get <*> B.get <*> B.get <*>
-          B.get <*> B.get <*> B.get <*> B.get <*> pure I.End <*> B.get <*> B.get
+          B.get <*> B.get <*> B.get <*> B.get <*> B.get <*> pure I.End <*> B.get <*> B.get
           <*> getTime <*> B.get <*> B.get <*> B.get
           <*> pure (const False) <*> B.get <*> B.get <*> B.get <*> B.get
       where
diff --git a/src/library/Yi/UI/Pango.hs b/src/library/Yi/UI/Pango.hs
--- a/src/library/Yi/UI/Pango.hs
+++ b/src/library/Yi/UI/Pango.hs
@@ -597,7 +597,7 @@
     let metrics = winMetrics wi
         lineHeight = ascent metrics + descent metrics
         charWidth = max (approximateCharWidth metrics) (approximateDigitWidth metrics)
-        width = round $ fromIntegral wid / charWidth
+        width = round $ fromIntegral wid / charWidth - 1
         height = round $ fromIntegral h / lineHeight
         b0 = findBufferWith (bufkey win) e
     rgn <- shownRegion ui f wi b0
diff --git a/src/library/Yi/UI/SimpleLayout.hs b/src/library/Yi/UI/SimpleLayout.hs
--- a/src/library/Yi/UI/SimpleLayout.hs
+++ b/src/library/Yi/UI/SimpleLayout.hs
@@ -125,22 +125,28 @@
         dispLnCount = h' - wrapCount
 
 
-coordsOfCharacterB :: Size2D -> Point -> Point
-    -> BufferM (Maybe Point2D)
+coordsOfCharacterB :: Size2D -> Point -> Point -> BufferM (Maybe Point2D)
 coordsOfCharacterB _ topLeft char | topLeft > char = return Nothing
 coordsOfCharacterB (Size2D w h) (Point topLeft) (Point char)
     | char - topLeft >= w * h = return Nothing
 coordsOfCharacterB (Size2D w h) (Point topLeft) (Point char) = savingPointB $ do
     ts <- fmap tabSize indentSettingsB
     text <- fmap (R.toString . R.take (w * h)) (streamB Forward (Point topLeft))
-    let go !x !y n t | x >= w = go (x - w) (y + 1) n t
-        go _  !y _ _ | y >= h = Nothing
+    let go _  !y _ _ | y >= h = Nothing
         go !x !y 0 _ = Just (Point2D x y)
-        go !x !y !n (c : t) =
-            case c of
-                '\t' -> go (x + ts) y (n - 1) t
-                '\n' -> go 0 (y + 1) (n - 1) t
-                _ -> go (x + 1) y (n - 1) t
+        go !x !y !n (c : d : t) =
+            case (c, d, (compare x wOffset)) of
+                ('\t',  _ , _) -> go (x + ts) y (n - 1) (d:t)
+                ('\n',  _ , _) -> go 0 (y + 1) (n - 1) (d:t)
+                (  _ ,'\n',EQ) -> go x y (n - 1) (d:t)
+                (  _ ,  _ ,EQ) -> go (x - wOffset) (y + 1) (n - 1) (d:t)
+                (  _ ,  _ , _) -> go (x + 1) y (n - 1) (d:t)
+            where wOffset = w - 1
+        go !x !y !n (c : []) =
+            case (c, (compare x wOffset)) of
+                ('\n', _) -> go 0 (y + 1) (n - 1) [c]
+                (  _ , _) -> go (x + 1) y (n - 1) [c]
+            where wOffset = w - 1
         go !x !y _ _ = Just (Point2D x y)
     return (go 0 0 (char - topLeft) text)
 
diff --git a/src/library/Yi/UI/Vty.hs b/src/library/Yi/UI/Vty.hs
--- a/src/library/Yi/UI/Vty.hs
+++ b/src/library/Yi/UI/Vty.hs
@@ -232,7 +232,7 @@
                              point))
 
         rendered =
-            drawText h' w
+            drawText wsty h' w
                      tabWidth
                      ([(c, wsty) | c <- T.unpack prompt] ++ bufData ++ [(' ', wsty)])
                      -- we always add one character which can be used to position the cursor at the end of file
@@ -272,19 +272,20 @@
 stys sty ((endPos, sty') : xs) cs = [ sty | _ <- previous ] <> stys sty' xs later
     where (previous, later) = break ((endPos <=) . fst) cs
 
-drawText :: Int    -- ^ The height of the part of the window we are in
+drawText :: Vty.Attr -- ^ "Ground" attribute.
+         -> Int    -- ^ The height of the part of the window we are in
          -> Int    -- ^ The width of the part of the window we are in
          -> Int    -- ^ The number of spaces to represent a tab character with.
          -> [(Char, Vty.Attr)]  -- ^ The data to draw.
          -> [Vty.Image]
-drawText h w tabWidth bufData
+drawText wsty h w tabWidth bufData
     | h == 0 || w == 0 = []
     | otherwise        = renderedLines
     where
 
     -- the number of lines that taking wrapping into account,
     -- we use this to calculate the number of lines displayed.
-    wrapped = concatMap (wrapLine w) $ map (concatMap expandGraphic) $ take h $ lines' bufData
+    wrapped = concatMap (wrapLine w) $ map addSpace $ map (concatMap expandGraphic) $ take h $ lines' bufData
     lns0 = take h wrapped
 
     -- fill lines with blanks, so the selection looks ok.
@@ -298,15 +299,24 @@
                       Vty.charFill a ' ' (w - length l) 1
                       where (_, a) = last l
 
+
+    addSpace :: [(Char, Vty.Attr)] -> [(Char, Vty.Attr)]
+    addSpace [] = [(' ', wsty)]
+    addSpace l = case (mod lineLength w) of
+                    0 -> l
+                    _ -> l ++ [(' ', wsty)]
+                 where
+                    lineLength = length l
+
     -- | Cut a string in lines separated by a '\n' char. Note
-    -- that we add a blank character where the \n was, so the
-    -- cursor can be positioned there.
+    -- that we remove the newline entirely since it is no longer
+    -- significant for drawing text.
 
     lines' :: [(Char, a)] -> [[(Char, a)]]
     lines' [] =  []
     lines' s  = case s' of
                   []          -> [l]
-                  ((_,x):s'') -> (l++[(' ',x)]) : lines' s''
+                  ((_,x):s'') -> l : lines' s''
                 where
                 (l, s') = break ((== '\n') . fst) s
 
@@ -316,8 +326,9 @@
 
     expandGraphic ('\t', p) = replicate tabWidth (' ', p)
     expandGraphic (c, p)
-        | ord c < 32 = [('^', p), (chr (ord c + 64), p)]
+        | numeric < 32 = [('^', p), (chr (numeric + 64), p)]
         | otherwise = [(c, p)]
+        where numeric = ord c
 
 renderTabBar :: UIStyle -> [(T.Text, Bool)] -> Vty.Image
 renderTabBar uiStyle = Vty.horizCat . fmap render
diff --git a/src/tests/vimtests/change/V3jc.test b/src/tests/vimtests/change/V3jc.test
new file mode 100644
--- /dev/null
+++ b/src/tests/vimtests/change/V3jc.test
@@ -0,0 +1,15 @@
+-- Input
+(2,3)
+aaaaaa
+bbbbbb
+cccccc
+dddddd
+eeeeee
+ffffff
+-- Output
+(2,1)
+aaaaaa
+
+ffffff
+-- Events
+V3jc<Esc>
diff --git a/src/tests/vimtests/change/cc.test b/src/tests/vimtests/change/cc.test
new file mode 100644
--- /dev/null
+++ b/src/tests/vimtests/change/cc.test
@@ -0,0 +1,12 @@
+-- Input
+(1,11)
+Lorem ipsum dolor sit amet
+abc def ghi
+qwe rty uiop
+-- Output
+(1,3)
+man
+abc def ghi
+qwe rty uiop
+-- Events
+ccman<Esc>
diff --git a/src/tests/vimtests/insertion/O3.test b/src/tests/vimtests/insertion/O3.test
deleted file mode 100644
--- a/src/tests/vimtests/insertion/O3.test
+++ /dev/null
@@ -1,11 +0,0 @@
--- Input
-(2,1)
-
-
--- Output
-(2,1)
-
-
-
--- Events
-O<Esc>
diff --git a/src/tests/vimtests/insertion/O4.test b/src/tests/vimtests/insertion/O4.test
new file mode 100644
--- /dev/null
+++ b/src/tests/vimtests/insertion/O4.test
@@ -0,0 +1,11 @@
+-- Input
+(2,1)
+
+
+-- Output
+(2,1)
+
+
+
+-- Events
+O<Esc>
diff --git a/yi.cabal b/yi.cabal
--- a/yi.cabal
+++ b/yi.cabal
@@ -1,5 +1,5 @@
 name:           yi
-version:        0.11.0
+version:        0.11.1
 category:       Development, Editor
 synopsis:       The Haskell-Scriptable Editor
 description:
@@ -287,7 +287,8 @@
     word-trie >= 0.2.0.4,
     yi-language >= 0.1.0.7,
     oo-prototypes,
-    yi-rope >= 0.6.0.0 && < 0.7
+    yi-rope >= 0.7.0.0 && < 0.8,
+    exceptions
 
   ghc-options: -Wall -fno-warn-orphans
   ghc-prof-options: -prof -auto-all -rtsopts
