/
Rectangle.hs
91 lines (75 loc) · 3.08 KB
/
Rectangle.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
-- Copyright (C) 2008 JP Bernardy
-- | emacs-style rectangle manipulation functions.
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, length, zipWith, transpose)
import Yi.Buffer
import Yi.Editor
import Yi.String
import Text.Regex.TDFA
alignRegion :: String -> BufferM ()
alignRegion str = modifyRegionClever (alignText str) =<< unitWiseRegion Line =<< getSelectRegionB
where regexSplit :: String -> String -> [String]
regexSplit regex l = case l =~ regex of
AllTextSubmatches (_:matches) -> matches
_ -> error "regexSplit: text does not match"
alignText :: String -> String -> String
alignText regex text = unlines' ls'
where ls, ls' :: [String]
ls = lines' text
columns, columns' :: [[String]]
columns = fmap (regexSplit regex) ls
columnsWidth :: [Int]
columnsWidth = fmap (maximum . fmap length) $ transpose columns
columns' = fmap (zipWith padLeft columnsWidth) columns
ls' = fmap concat columns'
-- | Align each line of the region on the given regex.
-- Fails if it is not found in any line.
alignRegionOn :: String -> BufferM ()
alignRegionOn s = alignRegion ("^(.*)(" ++ s ++ ")(.*)")
-- | 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)
-- | Split a list at the boundaries given
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