diff --git a/bench/MainBenchmarkSuite.hs b/bench/MainBenchmarkSuite.hs
--- a/bench/MainBenchmarkSuite.hs
+++ b/bench/MainBenchmarkSuite.hs
@@ -177,6 +177,7 @@
   , onTextGroup "all bad, (== '中')" $ F.all (== '中')
   , onTextGroup "init" F.init
   , onTextGroup "tail" F.tail
+  , onTextGroup "replicate 50" (F.replicate 50)
   ] ++ splitBench
     ++ wordsBench
     ++ spanBreakBench
diff --git a/src/Yi/Rope.hs b/src/Yi/Rope.hs
--- a/src/Yi/Rope.hs
+++ b/src/Yi/Rope.hs
@@ -46,6 +46,7 @@
    Yi.Rope.words, Yi.Rope.unwords,
    Yi.Rope.split, Yi.Rope.init, Yi.Rope.tail,
    Yi.Rope.span, Yi.Rope.break, Yi.Rope.foldl',
+   Yi.Rope.replicate, Yi.Rope.replicateChar,
 
    -- * IO
    Yi.Rope.readFile, Yi.Rope.readFile', Yi.Rope.writeFile,
@@ -343,6 +344,7 @@
   where
     go t = case viewl t of
       EmptyL -> T.empty
+      Chunk 0 _ :< ts -> go ts
       Chunk l x :< ts ->
         let r = TX.dropWhile p x
             l' = TX.length r
@@ -368,6 +370,7 @@
   where
     go t = case viewr t of
       EmptyR -> T.empty
+      ts :> Chunk 0 _ -> go ts
       ts :> Chunk l x ->
         let r = TX.dropWhileEnd p x
             l' = TX.length r
@@ -383,18 +386,19 @@
   where
     go t = case viewl t of
       EmptyL -> T.empty
+      Chunk 0 _ :< ts -> go ts
       Chunk l x :< ts ->
         let r = TX.takeWhile p x
             l' = TX.length r
         in case compare l' l of
           -- We took the whole chunk, keep taking more.
-          EQ -> Chunk l x <| go ts
+          EQ -> Chunk l x -| go ts
           -- We took some stuff but not everything so we're done.
           -- Alternatively, we took more than the size chunk so
           -- preserve this wonder. This should only ever happen if you
           -- use unsafe functions and Chunk size goes out of sync with
           -- actual text length.
-          _ -> Chunk l' r <| ts
+          _ -> T.singleton $ Chunk l' r
 
 -- | Like 'Yi.Rope.takeWhile' but takes from the end instead.
 takeWhileEnd :: (Char -> Bool) -> YiString -> YiString
@@ -402,9 +406,10 @@
   where
     go t = case viewr t of
       EmptyR -> T.empty
+      ts :> Chunk 0 _ -> go ts
       ts :> Chunk l x -> case compare l' l of
         EQ -> go ts |> Chunk l x
-        _ -> ts |> Chunk l' r
+        _ -> T.singleton $ Chunk l' r
         where
           -- no TX.takeWhileEnd – https://github.com/bos/text/issues/89
           r = TX.reverse . TX.takeWhile p . TX.reverse $ x
@@ -691,6 +696,20 @@
       EmptyL -> acc
       Chunk _ x :< ts -> let r = TX.foldl' f acc x
                          in r `seq` go r ts
+
+-- | Replicate the given YiString set number of times, concatenating
+-- the results. Also see 'Yi.Rope.replicateChar'.
+replicate :: Int -> YiString -> YiString
+replicate n t | n <= 0 = mempty
+              | otherwise = t <> Yi.Rope.replicate (n - 1) t
+
+-- | Replicate the given character set number of times and pack the
+-- result into a 'YiString'.
+--
+-- >>> replicateChar 4 ' '
+-- "    "
+replicateChar :: Int -> Char -> YiString
+replicateChar n = fromText . TX.replicate n . TX.singleton
 
 -- | Helper function doing conversions of to and from underlying
 -- 'TX.Text'. You should aim to implement everything in terms of
diff --git a/test/Yi/RopeSpec.hs b/test/Yi/RopeSpec.hs
--- a/test/Yi/RopeSpec.hs
+++ b/test/Yi/RopeSpec.hs
@@ -99,3 +99,5 @@
     prop "\\p -> R.foldl' p ~ T.foldl' p $ \\x _ -> x + 1" $ \t ->
       let f x _ = x + (1 :: Integer)
       in (R.foldl' f 0 . R.fromText) t `shouldBe` T.foldl' f 0 t
+    prop "R.replicate ~ T.replicate" $ \n ->
+      R.replicate n `isLikeT` T.replicate n
diff --git a/yi-rope.cabal b/yi-rope.cabal
--- a/yi-rope.cabal
+++ b/yi-rope.cabal
@@ -1,5 +1,5 @@
 name:                yi-rope
-version:             0.2.2.0
+version:             0.3.0.0
 synopsis:            A rope data structure used by Yi
 description:         A rope data structure used by Yi
 license:             GPL-3
