{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-| Module : Data.Text.TextZipper.Edit Copyright : (c) Kwang Yul Seo 2016 License : BSD-style (see the file LICENSE) Maintainer : kwangyul.seo@gmail.com Stability : experimental Portability : portable This module provides a monadic interface to 'Data.Text.Zipper.TextZipper'. -} module Data.Text.Zipper.Edit ( Edit , EditT , execEditT , execEdit -- * Extraction functions , clearZipper , getText , currentLine , cursorPosition , lineLengths , getLineLimit -- * Navigation and editing functions , moveCursor , insertChar , breakLine , killToBOL , killToEOL , gotoEOL , gotoBOL , deletePrevChar , deleteChar , moveRight , moveLeft , moveUp , moveDown ) where import Control.Monad.Identity import Control.Monad.State import Data.Text.Zipper (TextZipper) import qualified Data.Text.Zipper as TZ -- | An edit monad type Edit t a = EditT t Identity a -- | An edit transformer monad newtype EditT t m a = EditT { unEdit :: StateT (TextZipper t) m a } deriving (Functor, Applicative, Monad, MonadState (TextZipper t)) -- | Execute the edit session with the given zipper and return the -- modified zipper. execEditT :: (Monoid t, Monad m) => EditT t m a -> TextZipper t -> m (TextZipper t) execEditT e = execStateT (unEdit e) -- | Execute the edit session with the given zipper and return the -- modified zipper. execEdit :: (Monoid t) => Edit t a -> TextZipper t -> TextZipper t execEdit e tz = runIdentity (execEditT e tz) -- | Empty a zipper. clearZipper :: (Monoid t, Monad m) => EditT t m () clearZipper = do z <- get put (TZ.clearZipper z) -- | Get the text contents of the zipper. getText :: (Monoid t, Monad m) => EditT t m [t] getText = TZ.getText <$> get -- | The line of text on which the zipper's cursor currently resides. currentLine :: (Monoid t, Monad m) => EditT t m t currentLine = TZ.currentLine <$> get -- | Get the cursor position of the zipper; returns @(row, col)@. -- @row@ ranges from @[0..num_rows-1]@ inclusive; @col@ ranges from -- @[0..length of current line]@ inclusive. Column values equal to -- line width indicate a cursor that is just past the end of a line of -- text. cursorPosition :: (Monoid t, Monad m) => EditT t m (Int, Int) cursorPosition = TZ.cursorPosition <$> get -- | Return the lengths of the lines in the zipper. lineLengths :: (Monoid t, Monad m) => EditT t m [Int] lineLengths = TZ.lineLengths <$> get -- | Get the line limit, if any, for a zipper. getLineLimit :: (Monoid t, Monad m) => EditT t m (Maybe Int) getLineLimit = TZ.getLineLimit <$> get -- | Move the cursor to the specified row and column. Invalid cursor -- positions will be ignored. Valid cursor positions range as -- described for 'cursorPosition'. moveCursor :: (Monoid t, Monad m) => (Int, Int) -> EditT t m () moveCursor c = do z <- get put (TZ.moveCursor c z) -- | Insert a character at the current cursor position. Move the -- cursor one position to the right. insertChar :: (Monoid t, Monad m) => Char -> EditT t m () insertChar ch = do z <- get put (TZ.insertChar ch z) -- |Insert a line break at the current cursor position. breakLine :: (Monoid t, Monad m) => EditT t m () breakLine = do z <- get put (TZ.breakLine z) -- | Remove all text from the cursor position to the beginning of the -- current line. killToBOL :: (Monoid t, Monad m) => EditT t m () killToBOL = do z <- get put (TZ.killToBOL z) -- | Remove all text from the cursor position to the end of the current -- line. If the cursor is at the beginning of a line and the line is -- empty, the entire line will be removed. killToEOL :: (Monoid t, Monad m) => EditT t m () killToEOL = do z <- get put (TZ.killToEOL z) -- | Move the cursor to the end of the current line. gotoEOL :: (Monoid t, Monad m) => EditT t m () gotoEOL = do z <- get put (TZ.gotoEOL z) -- | Move the cursor to the beginning of the current line. gotoBOL :: (Monoid t, Monad m) => EditT t m () gotoBOL = do z <- get put (TZ.gotoBOL z) -- | Delete the character preceding the cursor position, and move the -- cursor backwards by one character. deletePrevChar :: (Eq t, Monoid t, Monad m) => EditT t m () deletePrevChar = do z <- get put (TZ.deletePrevChar z) -- | Delete the character at the cursor position. Leaves the cursor -- position unchanged. If the cursor is at the end of a line of text, -- this combines the line with the line below. deleteChar :: (Monoid t, Monad m) => EditT t m () deleteChar = do z <- get put (TZ.deleteChar z) -- | Move the cursor right by one position. If the cursor is at the -- end of a line, the cursor is moved to the first position of the -- following line (if any). moveRight :: (Monoid t, Monad m) => EditT t m () moveRight = do z <- get put (TZ.moveRight z) -- | Move the cursor left by one position. If the cursor is at the -- beginning of a line, the cursor is moved to the last position of -- the preceding line (if any). moveLeft :: (Monoid t, Monad m) => EditT t m () moveLeft = do z <- get put (TZ.moveLeft z) -- | Move the cursor up by one row. If there no are rows above the -- current one, move to the first position of the current row. If the -- row above is shorter, move to the end of that row. moveUp :: (Monoid t, Monad m) => EditT t m () moveUp = do z <- get put (TZ.moveUp z) -- | Move the cursor down by one row. If there are no rows below the -- current one, move to the last position of the current row. If the -- row below is shorter, move to the end of that row. moveDown :: (Monoid t, Monad m) => EditT t m () moveDown = do z <- get put (TZ.moveDown z)