{-# 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