module Yi.Buffer.HighLevel where
import Control.Monad.RWS.Strict (ask)
import Control.Monad.State
import Data.Char
import Data.List (isPrefixOf, sort, lines, drop, filter, length, takeWhile, dropWhile, reverse)
import qualified Data.Rope as R
import Data.Maybe (fromMaybe, listToMaybe)
import Data.Time (UTCTime)
import Prelude (FilePath, map)
import Yi.Prelude
import Yi.Buffer.Basic
import Yi.Buffer.Misc
import Yi.Buffer.Normal
import Yi.Buffer.Region
import Yi.String
import Yi.Window
moveToSol :: BufferM ()
moveToSol = maybeMoveB Line Backward
moveToEol :: BufferM ()
moveToEol = maybeMoveB Line Forward
topB :: BufferM ()
topB = moveTo 0
botB :: BufferM ()
botB = moveTo =<< sizeB
leftOnEol :: BufferM ()
leftOnEol = do
eol <- atEol
sol <- atSol
when (eol && not sol) leftB
moveXorSol :: Int -> BufferM ()
moveXorSol x = replicateM_ x $ do c <- atSol; when (not c) leftB
moveXorEol :: Int -> BufferM ()
moveXorEol x = replicateM_ x $ do c <- atEol; when (not c) rightB
nextWordB :: BufferM ()
nextWordB = moveB unitWord Forward
prevWordB :: BufferM ()
prevWordB = moveB unitWord Backward
nextCInc :: Char -> BufferM ()
nextCInc c = doUntilB_ ((c ==) <$> readB) rightB
nextCExc :: Char -> BufferM ()
nextCExc c = nextCInc c >> leftB
prevCInc :: Char -> BufferM ()
prevCInc c = doUntilB_ ((c ==) <$> readB) leftB
prevCExc :: Char -> BufferM ()
prevCExc c = prevCInc c >> rightB
firstNonSpaceB :: BufferM ()
firstNonSpaceB = do moveToSol
untilB_ ((||) <$> atEol <*> ((not . isSpace) <$> readB)) rightB
lastNonSpaceB :: BufferM ()
lastNonSpaceB = do moveToEol
untilB_ ((||) <$> atSol <*> ((not . isSpace) <$> readB)) leftB
moveNonspaceOrSol :: BufferM ()
moveNonspaceOrSol = do prev <- readPreviousOfLnB
if and . map isSpace $ prev then moveToSol else firstNonSpaceB
nextNParagraphs :: Int -> BufferM ()
nextNParagraphs n = replicateM_ n $ moveB unitEmacsParagraph Forward
prevNParagraphs :: Int -> BufferM ()
prevNParagraphs n = replicateM_ n $ moveB unitEmacsParagraph Backward
goUnmatchedB :: Direction -> Char -> Char -> BufferM ()
goUnmatchedB dir cStart' cStop' = stepB >> readB >>= go (0::Int)
where go opened c | c == cStop && opened == 0 = return ()
| c == cStop = stepB >> readB >>= go (opened1)
| c == cStart = stepB >> readB >>= go (opened+1)
| otherwise = stepB >> readB >>= go opened
(stepB, cStart, cStop) | dir == Forward = (rightB, cStart', cStop')
| otherwise = (leftB, cStop', cStart')
atSol :: BufferM Bool
atSol = atBoundaryB Line Backward
atEol :: BufferM Bool
atEol = atBoundaryB Line Forward
atSof :: BufferM Bool
atSof = atBoundaryB Document Backward
atEof :: BufferM Bool
atEof = atBoundaryB Document Forward
getLineAndCol :: BufferM (Int, Int)
getLineAndCol = (,) <$> curLn <*> curCol
readLnB :: BufferM String
readLnB = readUnitB Line
readCharB :: BufferM (Maybe Char)
readCharB = fmap listToMaybe (readUnitB Character)
readRestOfLnB :: BufferM String
readRestOfLnB = readRegionB =<< regionOfPartB Line Forward
readPreviousOfLnB :: BufferM String
readPreviousOfLnB = readRegionB =<< regionOfPartB Line Backward
hasWhiteSpaceBefore :: BufferM Bool
hasWhiteSpaceBefore = prevPointB >>= readAtB >>= return . isSpace
prevPointB :: BufferM Point
prevPointB = do
sof <- atSof
if sof then pointB
else do p <- pointB
return $ Point (fromPoint p 1)
nextPointB :: BufferM Point
nextPointB = do
eof <- atEof
if eof then pointB
else do p <- pointB
return $ Point (fromPoint p + 1)
readPrevWordB :: BufferM String
readPrevWordB = readPrevUnitB unitViWordOnLine
bdeleteB :: BufferM ()
bdeleteB = deleteB Character Backward
killWordB :: BufferM ()
killWordB = deleteB unitWord Forward
bkillWordB :: BufferM ()
bkillWordB = deleteB unitWord Backward
uppercaseWordB :: BufferM ()
uppercaseWordB = transformB (fmap toUpper) unitWord Forward
lowercaseWordB :: BufferM ()
lowercaseWordB = transformB (fmap toLower) unitWord Forward
capitaliseWordB :: BufferM ()
capitaliseWordB = transformB capitalizeFirst unitWord Forward
deleteToEol :: BufferM ()
deleteToEol = deleteRegionB =<< regionOfPartB Line Forward
deleteLineForward :: BufferM ()
deleteLineForward =
do moveToSol
deleteToEol
deleteN 1
swapB :: BufferM ()
swapB = do eol <- atEol
when eol leftB
transposeB Character Forward
deleteTrailingSpaceB :: BufferM ()
deleteTrailingSpaceB = modifyRegionClever deleteSpaces =<< regionOfB Document
where deleteSpaces = mapLines $ reverse . dropWhile (' ' ==) . reverse
setSelectionMarkPointB :: Point -> BufferM ()
setSelectionMarkPointB p = flip setMarkPointB p =<< selMark <$> askMarks
getSelectionMarkPointB :: BufferM Point
getSelectionMarkPointB = getMarkPointB =<< selMark <$> askMarks
exchangePointAndMarkB :: BufferM ()
exchangePointAndMarkB = do m <- getSelectionMarkPointB
p <- pointB
setSelectionMarkPointB p
moveTo m
getBookmarkB :: String -> BufferM Mark
getBookmarkB = getMarkB . Just
data BufferFileInfo =
BufferFileInfo { bufInfoFileName :: FilePath
, bufInfoSize :: Int
, bufInfoLineNo :: Int
, bufInfoColNo :: Int
, bufInfoCharNo :: Point
, bufInfoPercent :: String
, bufInfoModified :: Bool
}
bufInfoB :: BufferM BufferFileInfo
bufInfoB = do
s <- sizeB
p <- pointB
m <- gets isUnchangedBuffer
l <- curLn
c <- curCol
nm <- gets identString
let bufInfo = BufferFileInfo { bufInfoFileName = nm
, bufInfoSize = fromIntegral s
, bufInfoLineNo = l
, bufInfoColNo = c
, bufInfoCharNo = p
, bufInfoPercent = getPercent p s
, bufInfoModified = not m
}
return bufInfo
upScreensB :: Int -> BufferM ()
upScreensB = scrollScreensB . negate
downScreensB :: Int -> BufferM ()
downScreensB = scrollScreensB
upScreenB :: BufferM ()
upScreenB = scrollScreensB (1)
downScreenB :: BufferM ()
downScreenB = scrollScreensB 1
scrollScreensB :: Int -> BufferM ()
scrollScreensB n = do
h <- askWindow height
scrollB $ n * max 0 (h 3)
scrollByB :: (Int -> Int) -> Int -> BufferM ()
scrollByB f n = do h <- askWindow height
scrollB $ n * f h
vimScrollB :: Int -> BufferM ()
vimScrollB n = do scrollB n
discard $ lineMoveRel n
vimScrollByB :: (Int -> Int) -> Int -> BufferM ()
vimScrollByB f n = do h <- askWindow height
vimScrollB $ n * f h
scrollToCursorB :: BufferM ()
scrollToCursorB = do
MarkSet f i _ <- markLines
h <- askWindow height
let m = f + (h `div` 2)
scrollB $ i m
scrollCursorToTopB :: BufferM ()
scrollCursorToTopB = do
MarkSet f i _ <- markLines
scrollB $ i f
scrollCursorToBottomB :: BufferM ()
scrollCursorToBottomB = do
MarkSet _ i _ <- markLines
r <- winRegionB
t <- lineOf (regionEnd r 1)
scrollB $ i t
scrollB :: Int -> BufferM ()
scrollB n = do
MarkSet fr _ _ <- askMarks
savingPointB $ do
moveTo =<< getMarkPointB fr
discard $ gotoLnFrom n
setMarkPointB fr =<< pointB
w <- askWindow wkey
modA pointFollowsWindowA (\old w' -> if w == w' then True else old w')
snapInsB :: BufferM ()
snapInsB = do
movePoint <- getA pointFollowsWindowA
w <- askWindow wkey
when (movePoint w) $ do
r <- winRegionB
p <- pointB
moveTo $ max (regionStart r) $ min (regionEnd r) $ p
indexOfSolAbove :: Int -> BufferM Point
indexOfSolAbove n = pointAt $ gotoLnFrom (negate n)
snapScreenB :: BufferM Bool
snapScreenB = do
movePoint <- getA pointFollowsWindowA
w <- askWindow wkey
if movePoint w then return False else do
inWin <- pointInWindowB =<< pointB
if inWin then return False else do
h <- askWindow height
let gap = h `div` 2
i <- indexOfSolAbove gap
f <- fromMark <$> askMarks
setMarkPointB f i
return True
downFromTosB :: Int -> BufferM ()
downFromTosB n = do
moveTo =<< getMarkPointB =<< fromMark <$> askMarks
replicateM_ n lineDown
upFromBosB :: Int -> BufferM ()
upFromBosB n = do
r <- winRegionB
moveTo (regionEnd r 1)
moveToSol
replicateM_ n lineUp
middleB :: BufferM ()
middleB = do
w <- ask
f <- fromMark <$> askMarks
moveTo =<< getMarkPointB f
replicateM_ (height w `div` 2) lineDown
pointInWindowB :: Point -> BufferM Bool
pointInWindowB p = nearRegion p <$> winRegionB
getRawestSelectRegionB :: BufferM Region
getRawestSelectRegionB = do
m <- getSelectionMarkPointB
p <- pointB
return $ mkRegion p m
getRawSelectRegionB :: BufferM Region
getRawSelectRegionB = do
s <- getA highlightSelectionA
if s then getRawestSelectRegionB else do
p <- pointB
return $ mkRegion p p
getSelectRegionB :: BufferM Region
getSelectRegionB = do
regionStyle <- getA regionStyleA
r <- getRawSelectRegionB
mkRegionOfStyleB (regionStart r) (regionEnd r) regionStyle
setSelectRegionB :: Region -> BufferM ()
setSelectRegionB region = do
setSelectionMarkPointB $ regionStart region
moveTo $ regionEnd region
extendSelectRegionB :: Region -> BufferM ()
extendSelectRegionB region = (setSelectRegionB . unionRegion region) =<< getSelectRegionB
deleteBlankLinesB :: BufferM ()
deleteBlankLinesB =
do isThisBlank <- isBlank <$> readLnB
when isThisBlank $ do
p <- pointB
discard $ whileB (isBlank <$> getNextLineB Backward) lineUp
q <- pointB
deleteRegionB $ mkRegion p q
lineStreamB :: Direction -> BufferM [String]
lineStreamB dir = drop 1 . fmap rev . lines' . R.toString <$> (streamB dir =<< pointB)
where rev = case dir of
Forward -> id
Backward -> reverse
getMaybeNextLineB :: Direction -> BufferM (Maybe String)
getMaybeNextLineB dir = listToMaybe <$> lineStreamB dir
getNextLineB :: Direction -> BufferM String
getNextLineB dir = fromMaybe "" <$> getMaybeNextLineB dir
getNextLineWhichB :: Direction -> (String -> Bool) -> BufferM (Maybe String)
getNextLineWhichB dir cond = listToMaybe . filter cond <$> lineStreamB dir
getNextNonBlankLineB :: Direction -> BufferM String
getNextNonBlankLineB dir = fromMaybe "" <$> getNextLineWhichB dir (not . isBlank)
modifySelectionB :: (String -> String) -> BufferM ()
modifySelectionB = modifyExtendedSelectionB Character
modifyExtendedSelectionB :: TextUnit -> (String -> String) -> BufferM ()
modifyExtendedSelectionB unit transform
= modifyRegionB transform =<< unitWiseRegion unit =<< getSelectRegionB
linePrefixSelectionB :: String
-> BufferM ()
linePrefixSelectionB s =
modifyExtendedSelectionB Line $ skippingLast $ mapLines (s++)
where skippingLast f xs = f (init xs) ++ [last xs]
unLineCommentSelectionB :: String
-> String
-> BufferM ()
unLineCommentSelectionB s1 s2 =
modifyExtendedSelectionB Line $ mapLines unCommentLine
where
unCommentLine :: String -> String
unCommentLine line
| isPrefixOf s1 line = drop (length s1) line
| isPrefixOf s2 line = drop (length s2) line
| otherwise = line
toggleCommentSelectionB :: String -> String -> BufferM ()
toggleCommentSelectionB insPrefix delPrefix = do
l <- readUnitB Line
if delPrefix `isPrefixOf` l
then unLineCommentSelectionB insPrefix delPrefix
else linePrefixSelectionB insPrefix
justifySelectionWithTopB :: BufferM ()
justifySelectionWithTopB =
modifySelectionB justifyLines
where
justifyLines :: String -> String
justifyLines input =
case lines input of
[] -> ""
[ one ] -> one
(top : _) -> mapLines justifyLine input
where
topIndent = takeWhile isSpace top
justifyLine :: String -> String
justifyLine "" = ""
justifyLine l = topIndent ++ dropWhile isSpace l
replaceBufferContent :: String -> BufferM ()
replaceBufferContent newvalue = do
r <- regionOfB Document
replaceRegionB r newvalue
fillRegion :: Region -> BufferM ()
fillRegion = modifyRegionClever (unlines' . fillText 80)
fillParagraph :: BufferM ()
fillParagraph = fillRegion =<< regionOfB unitParagraph
sortLines :: BufferM ()
sortLines = modifyExtendedSelectionB Line (onLines sort)
revertB :: String -> UTCTime -> BufferM ()
revertB s now = do
r <- regionOfB Document
replaceRegionClever r s
markSavedB now