yi-0.3: Yi/Buffer/HighLevel.hs
{-# LANGUAGE DeriveDataTypeable #-}
-- Copyright (C) 2008 JP Bernardy
module Yi.Buffer.HighLevel where
import Control.Applicative
import Control.Monad.State
import Data.Char
import Data.Dynamic
import Yi.Buffer
import Yi.Buffer.Normal
import Yi.Buffer.Region
import Yi.String
import Yi.Window
import Yi.Dynamic
-- ---------------------------------------------------------------------
-- Movement operations
-- | Move point to start of line
moveToSol :: BufferM ()
moveToSol = maybeMoveB Line Backward
-- | Move point to end of line
moveToEol :: BufferM ()
moveToEol = maybeMoveB Line Forward
-- | Move cursor to origin
topB :: BufferM ()
topB = moveTo 0
-- | Move cursor to end of buffer
botB :: BufferM ()
botB = moveTo =<< sizeB
-- | Move @x@ chars back, or to the sol, whichever is less
moveXorSol :: Int -> BufferM ()
moveXorSol x = replicateM_ x $ do c <- atSol; when (not c) leftB
-- | Move @x@ chars forward, or to the eol, whichever is less
moveXorEol :: Int -> BufferM ()
moveXorEol x = replicateM_ x $ do c <- atEol; when (not c) rightB
-- | Move to first char of next word forwards
nextWordB :: BufferM ()
nextWordB = moveB Word Forward
-- | Move to first char of next word backwards
prevWordB :: BufferM ()
prevWordB = moveB Word Backward
-- * Char-based movement actions.
-- | Move to the next occurence of @c@
nextCInc :: Char -> BufferM ()
nextCInc c = doUntilB_ ((c ==) <$> readB) rightB
-- | Move to the character before the next occurence of @c@
nextCExc :: Char -> BufferM ()
nextCExc c = nextCInc c >> leftB
-- | Move to the previous occurence of @c@
prevCInc :: Char -> BufferM ()
prevCInc c = doUntilB_ ((c ==) <$> readB) leftB
-- | Move to the character after the previous occurence of @c@
prevCExc :: Char -> BufferM ()
prevCExc c = prevCInc c >> rightB
-- | Move to first non-space character in this line
firstNonSpaceB :: BufferM ()
firstNonSpaceB = do moveToSol
untilB_ ((||) <$> atEol <*> ((not . isSpace) <$> readB)) rightB
------------
-- | Move down next @n@ paragraphs
nextNParagraphs :: Int -> BufferM ()
nextNParagraphs n = replicateM_ n $ moveB Paragraph Forward
-- | Move up prev @n@ paragraphs
prevNParagraphs :: Int -> BufferM ()
prevNParagraphs n = replicateM_ n $ moveB Paragraph Backward
-----------------------------------------------------------------------
-- Queries
-- | Return true if the current point is the start of a line
atSol :: BufferM Bool
atSol = atBoundaryB Line Backward
-- | Return true if the current point is the end of a line
atEol :: BufferM Bool
atEol = atBoundaryB Line Forward
-- | True if point at start of file
atSof :: BufferM Bool
atSof = atBoundaryB Document Backward
-- | True if point at end of file
atEof :: BufferM Bool
atEof = atBoundaryB Document Forward
-- | Get the current line and column number
getLineAndCol :: BufferM (Int, Int)
getLineAndCol = do
lineNo <- curLn
colNo <- offsetFromSol
return (lineNo, colNo)
-- | Read the line the point is on
readLnB :: BufferM String
readLnB = readUnitB Line
-- | Read from point to end of line
readRestOfLnB :: BufferM String
readRestOfLnB = readRegionB =<< regionOfPartB Line Forward
--------------------------
-- Deletes
-- | Delete one character backward
bdeleteB :: BufferM ()
bdeleteB = deleteB Character Backward
-- | Delete forward whitespace or non-whitespace depending on
-- the character under point.
killWordB :: BufferM ()
killWordB = deleteB Word Forward
-- | Delete backward whitespace or non-whitespace depending on
-- the character before point.
bkillWordB :: BufferM ()
bkillWordB = deleteB Word Backward
----------------------------------------
-- Transform operations
-- | capitalise the word under the cursor
uppercaseWordB :: BufferM ()
uppercaseWordB = transformB (map toUpper) Word Forward
-- | lowerise word under the cursor
lowercaseWordB :: BufferM ()
lowercaseWordB = transformB (map toLower) Word Forward
-- | capitalise the first letter of this word
capitaliseWordB :: BufferM ()
capitaliseWordB = transformB capitalizeFirst Word Forward
-- | Delete to the end of line, excluding it.
deleteToEol :: BufferM ()
deleteToEol = deleteRegionB =<< regionOfPartB Line Forward
-- | Transpose two characters, (the Emacs C-t action)
swapB :: BufferM ()
swapB = do eol <- atEol
when eol leftB
transposeB Character Forward
-- ----------------------------------------------------
-- | Marks
-- | Set the current buffer mark
setSelectionMarkPointB :: Int -> BufferM ()
setSelectionMarkPointB pos = do m <- getSelectionMarkB; setMarkPointB m pos
-- | Get the current buffer mark
getSelectionMarkPointB :: BufferM Int
getSelectionMarkPointB = do m <- getSelectionMarkB; getMarkPointB m
-- | Exchange point & mark.
-- Maybe this is better put in Emacs\/Mg common file
exchangePointAndMarkB :: BufferM ()
exchangePointAndMarkB = do m <- getSelectionMarkPointB
p <- pointB
setSelectionMarkPointB p
moveTo m
getBookmarkB :: String -> BufferM Mark
getBookmarkB nm = getMarkB (Just nm)
-- ---------------------------------------------------------------------
-- Buffer operations
data BufferFileInfo =
BufferFileInfo { bufInfoFileName :: FilePath
, bufInfoSize :: Int
, bufInfoLineNo :: Int
, bufInfoColNo :: Int
, bufInfoCharNo :: Int
, bufInfoPercent :: String
, bufInfoModified :: Bool
}
-- | File info, size in chars, line no, col num, char num, percent
bufInfoB :: BufferM BufferFileInfo
bufInfoB = do
s <- sizeB
p <- pointB
m <- isUnchangedB
l <- curLn
c <- offsetFromSol
nm <- gets name
let bufInfo = BufferFileInfo { bufInfoFileName = nm
, bufInfoSize = s
, bufInfoLineNo = l
, bufInfoColNo = c
, bufInfoCharNo = p
, bufInfoPercent = getPercent p s
, bufInfoModified = not m
}
return bufInfo
-----------------------------
-- Window-related operations
-- | Scroll up 1 screen
upScreenB :: BufferM ()
upScreenB = upScreensB 1
-- | Scroll down 1 screen
downScreenB :: BufferM ()
downScreenB = downScreensB 1
-- | Scroll up n screens
upScreensB :: Int -> BufferM ()
upScreensB = moveScreenB Forward
-- | Scroll down n screens
downScreensB :: Int -> BufferM ()
downScreensB = moveScreenB Backward
moveScreenB :: Direction -> Int -> BufferM ()
moveScreenB dir n = do h <- askWindow height
case dir of
Forward -> gotoLnFrom (- (n * (h - 1)))
Backward -> gotoLnFrom $ n * (h - 1)
moveToSol
-- | Move to @n@ lines down from top of screen
downFromTosB :: Int -> BufferM ()
downFromTosB n = do
moveTo =<< askWindow tospnt
replicateM_ n lineDown
-- | Move to @n@ lines up from the bottom of the screen
upFromBosB :: Int -> BufferM ()
upFromBosB n = do
moveTo =<< askWindow bospnt
moveToSol
replicateM_ n lineUp
-- | Move to middle line in screen
middleB :: BufferM ()
middleB = do
w <- askWindow id
moveTo (tospnt w)
replicateM_ (height w `div` 2) lineDown
-- | Extend the given region to boundaries of the text unit.
-- For instance one can extend the selection to to complete lines, or
-- paragraphs.
extendRegionToBoundaries :: TextUnit -> BoundarySide -> BoundarySide -> Region -> BufferM Region
extendRegionToBoundaries unit bs1 bs2 region = do
moveTo $ regionStart region
genMaybeMoveB unit (Backward, bs1) Backward
start <- pointB
moveTo $ regionEnd region
genMoveB unit (Forward, bs2) Forward
stop <- pointB
return $ mkRegion start stop
unitWiseRegion :: TextUnit -> Region -> BufferM Region
unitWiseRegion unit = extendRegionToBoundaries unit InsideBound OutsideBound
-- TODO: either decide this is evil and contain it to Vim, or embrace it and move it to the
-- Buffer record.
newtype SelectionStyle = SelectionStyle TextUnit
deriving (Typeable)
instance Initializable SelectionStyle where
initial = SelectionStyle Character
-- | Get the current region boundaries
getSelectRegionB :: BufferM Region
getSelectRegionB = do
m <- getMarkPointB =<< getSelectionMarkB
p <- pointB
let region = mkRegion m p
SelectionStyle unit <- getDynamicB
unitWiseRegion unit region