    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE PackageImports #-}
    {-# OPTIONS_HADDOCK show-extensions #-}

    -- |
    -- Module      :  Yi.Rectangle
    -- License     :  GPL-2
    -- Maintainer  :  yi-devel@googlegroups.com
    -- Stability   :  experimental
    -- Portability :  portable
    --
    -- emacs-style rectangle manipulation functions.

    module Yi.Rectangle where

    import           Control.Monad       (forM_)
    import           Data.List           (sort, transpose)
    import           Data.Monoid         ((<>))
    import qualified Data.Text           as T (Text, concat, justifyLeft, length)
    import           Yi.Buffer
    import           Yi.Editor           (EditorM, getRegE, setRegE, withCurrentBuffer)
    import qualified Yi.Rope             as R
    import           Yi.String           (lines', mapLines, unlines')

    -- | Get the selected region as a rectangle.
    -- Returns the region extended to lines, plus the start and end columns of the rectangle.
    getRectangle :: BufferM (Region, Int, Int)
    getRectangle :: BufferM (Region, Int, Int)
    getRectangle = do
        r <- BufferM Region
    getSelectRegionB
        extR <- unitWiseRegion Line r
        [lowCol,highCol] <- sort <$> mapM colOf [regionStart r, regionEnd r]
        return (extR, lowCol, highCol)

    -- | Split text at the boundaries given
    multiSplit :: [Int] -> R.YiString -> [R.YiString]
    multiSplit :: [Int] -> YiString -> [YiString]
    multiSplit [] YiString
    l = [YiString
    l]
    multiSplit (Int
    x:[Int]
    xs) YiString
    l = YiString
    left YiString -> [YiString] -> [YiString]
    forall a. a -> [a] -> [a]
    : [Int] -> YiString -> [YiString]
    multiSplit ((Int -> Int) -> [Int] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Int -> Int -> Int
    forall a. Num a => a -> a -> a
    subtract Int
    x) [Int]
    xs) YiString
    right
        where (YiString
    left, YiString
    right) = Int -> YiString -> (YiString, YiString)
    R.splitAt Int
    x YiString
    l

    onRectangle :: (Int -> Int -> R.YiString -> R.YiString) -> BufferM ()
    onRectangle :: (Int -> Int -> YiString -> YiString) -> BufferM ()
    onRectangle Int -> Int -> YiString -> YiString
    f = do
      (reg, l, r) <- BufferM (Region, Int, Int)
    getRectangle
      modifyRegionB (mapLines (f l r)) reg

    openRectangle :: BufferM ()
    openRectangle :: BufferM ()
    openRectangle = (Int -> Int -> YiString -> YiString) -> BufferM ()
    onRectangle Int -> Int -> YiString -> YiString
    openLine
      where
        openLine :: Int -> Int -> YiString -> YiString
    openLine Int
    l Int
    r YiString
    line =
          YiString
    left YiString -> YiString -> YiString
    forall a. Semigroup a => a -> a -> a
    <> Int -> Char -> YiString
    R.replicateChar (Int
    r Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    l) Char
    ' ' YiString -> YiString -> YiString
    forall a. Semigroup a => a -> a -> a
    <> YiString
    right
              where (YiString
    left, YiString
    right) = Int -> YiString -> (YiString, YiString)
    R.splitAt Int
    l YiString
    line

    stringRectangle :: R.YiString -> BufferM ()
    stringRectangle :: YiString -> BufferM ()
    stringRectangle YiString
    inserted = (Int -> Int -> YiString -> YiString) -> BufferM ()
    onRectangle Int -> Int -> YiString -> YiString
    stringLine
      where stringLine :: Int -> Int -> YiString -> YiString
    stringLine Int
    l Int
    r YiString
    line = YiString
    left YiString -> YiString -> YiString
    forall a. Semigroup a => a -> a -> a
    <> YiString
    inserted YiString -> YiString -> YiString
    forall a. Semigroup a => a -> a -> a
    <> YiString
    right
              where [YiString
    left,YiString
    _,YiString
    right] = [Int] -> YiString -> [YiString]
    multiSplit [Int
    l,Int
    r] YiString
    line

    killRectangle :: EditorM ()
    killRectangle :: EditorM ()
    killRectangle = do
      cutted <- BufferM [YiString] -> EditorM [YiString]
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM [YiString] -> EditorM [YiString])
    -> BufferM [YiString] -> EditorM [YiString]
    forall a b. (a -> b) -> a -> b
    $ do
          (reg, l, r) <- BufferM (Region, Int, Int)
    getRectangle
          text <- readRegionB reg
          let (cutted, rest) = unzip $ fmap cut $ R.lines' text

              cut :: R.YiString -> (R.YiString, R.YiString)
              cut YiString
    line = let [YiString
    left,YiString
    mid,YiString
    right] = [Int] -> YiString -> [YiString]
    multiSplit [Int
    l,Int
    r] YiString
    line
                         in (YiString
    mid, YiString
    left YiString -> YiString -> YiString
    forall a. Semigroup a => a -> a -> a
    <> YiString
    right)
          replaceRegionB reg (R.unlines rest)
          return cutted
      setRegE (R.unlines cutted)

    yankRectangle :: EditorM ()
    yankRectangle :: EditorM ()
    yankRectangle = do
      text <- YiString -> [YiString]
    R.lines' (YiString -> [YiString]) -> EditorM YiString -> EditorM [YiString]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> EditorM YiString
    getRegE
      withCurrentBuffer $ forM_ text $ \YiString
    t -> do
        BufferM () -> BufferM ()
    forall a. BufferM a -> BufferM a
    savingPointB (BufferM () -> BufferM ()) -> BufferM () -> BufferM ()
    forall a b. (a -> b) -> a -> b
    $ YiString -> BufferM ()
    insertN YiString
    t
        BufferM ()
    lineDown