packages feed

yi-0.4.6: Yi/Rectangle.hs

-- Copyright (C) 2008 JP Bernardy
module Yi.Rectangle where

import Yi.Prelude
import Prelude (subtract)
import Data.List (splitAt, unzip)
import Control.Applicative
import Data.Char
import Data.List (sort)

import Yi.Buffer
import Yi.Buffer.HighLevel
import Yi.Buffer.Normal
import Yi.Buffer.Region
import Yi.Editor
import Yi.String

-- | 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 = do
    r <- getSelectRegionB
    extR <- unitWiseRegion Line r
    [lowCol,highCol] <- sort <$> mapM colOf [regionStart r, regionEnd r]
    return (extR, lowCol, highCol)

multiSplit :: [Int] -> [a] -> [[a]]
multiSplit [] l = [l]
multiSplit (x:xs) l = left : multiSplit (fmap (subtract x) xs) right
    where (left,right) = splitAt x l

onRectangle :: (Int -> Int -> String -> String) -> BufferM ()
onRectangle f = do
    (reg, l, r) <- getRectangle 
    modifyRegionB (mapLines (f l r)) reg

openRectangle :: BufferM ()
openRectangle = onRectangle openLine
    where openLine l r line = left ++ replicate (r-l) ' ' ++ right
              where (left,right) = splitAt l line

stringRectangle :: String -> BufferM ()
stringRectangle inserted = onRectangle stringLine
    where stringLine l r line = left ++ inserted ++ right
              where [left,_,right] = multiSplit [l,r] line

killRectangle :: EditorM ()
killRectangle = do
    cutted <- withBuffer0 $ do
        (reg, l, r) <- getRectangle
        text <- readRegionB reg
        let (cutted, rest) = unzip $ fmap cut $ lines' text
            cut line = let [left,mid,right] = multiSplit [l,r] line in (mid, left ++ right)
        replaceRegionB reg (unlines' rest)
        return cutted
    setRegE (unlines' cutted)

yankRectangle :: EditorM ()
yankRectangle = do
    text <- lines' <$> getRegE
    withBuffer0 $ do
        forM_ text $ \t -> do
            savingPointB $ insertN t
            lineDown