-- | This module provides a two-dimensional text zipper data structure.
-- This structure represents a body of text and an editing cursor
-- which can be moved throughout the text, along with a set of editing
-- transformations.
--
-- Text zippers are generalized over the set of data types that might be
-- used to store lists of characters (e.g., 'String', 'T.Text', etc.).
-- As a result, the most general way to create a text zipper is to use
-- 'mkZipper' and provide all of the functions required to manipulate
-- the underlying text data.
--
-- Implementations using 'T.Text' and 'String' are provided.
module Data.Text.Zipper
    ( TextZipper

    -- * Construction and extraction
    , mkZipper
    , textZipper
    , stringZipper
    , clearZipper
    , vectorZipper
    , getText
    , currentLine
    , cursorPosition
    , lineLengths
    , getLineLimit

    -- * Navigation functions
    , moveCursor
    , moveCursorClosest
    , moveRight
    , moveLeft
    , moveUp
    , moveDown
    , gotoEOL
    , gotoBOL
    , gotoEOF
    , gotoBOF

    -- * Inspection functions
    , currentChar
    , nextChar
    , previousChar

    -- * Editing functions
    , insertChar
    , insertMany
    , deletePrevChar
    , deleteChar
    , breakLine
    , killToEOL
    , killToBOL
    , killToEOF
    , killToBOF
    , transposeChars
    )
where

import Control.Applicative ((<$>))
import Control.DeepSeq
import Data.Char (isPrint)
import Data.Monoid
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.Text.Zipper.Vector as V

data TextZipper a =
    TZ { TextZipper a -> a
toLeft :: a
       , TextZipper a -> a
toRight :: a
       , TextZipper a -> [a]
above :: [a]
       , TextZipper a -> [a]
below :: [a]
       , TextZipper a -> Char -> a
fromChar :: Char -> a
       , TextZipper a -> Int -> a -> a
drop_ :: Int -> a -> a
       , TextZipper a -> Int -> a -> a
take_ :: Int -> a -> a
       , TextZipper a -> a -> Int
length_ :: a -> Int
       , TextZipper a -> a -> Char
last_ :: a -> Char
       , TextZipper a -> a -> a
init_ :: a -> a
       , TextZipper a -> a -> Bool
null_ :: a -> Bool
       , TextZipper a -> a -> [a]
lines_ :: a -> [a]
       , TextZipper a -> a -> [Char]
toList_ :: a -> [Char]
       , TextZipper a -> Maybe Int
lineLimit :: Maybe Int
       }

instance (NFData a) => NFData (TextZipper a) where
    rnf :: TextZipper a -> ()
rnf TextZipper a
z = (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
z) a -> a -> a
forall a b. NFData a => a -> b -> b
`deepseq`
            (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
z) a -> [a] -> [a]
forall a b. NFData a => a -> b -> b
`deepseq`
            (TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
z) [a] -> [a] -> [a]
forall a b. NFData a => a -> b -> b
`deepseq`
            (TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
z) [a] -> () -> ()
forall a b. NFData a => a -> b -> b
`deepseq`
            ()

-- | Get the line limit, if any, for a zipper.
getLineLimit :: TextZipper a -> Maybe Int
getLineLimit :: TextZipper a -> Maybe Int
getLineLimit = TextZipper a -> Maybe Int
forall a. TextZipper a -> Maybe Int
lineLimit

instance (Eq a) => Eq (TextZipper a) where
    TextZipper a
a == :: TextZipper a -> TextZipper a -> Bool
== TextZipper a
b = [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and [ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
b
                 , TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
b
                 , TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
a [a] -> [a] -> Bool
forall a. Eq a => a -> a -> Bool
== TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
b
                 , TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
a [a] -> [a] -> Bool
forall a. Eq a => a -> a -> Bool
== TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
b
                 ]

instance (Show a) => Show (TextZipper a) where
    show :: TextZipper a -> [Char]
show TextZipper a
tz = [[Char]] -> [Char]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ [Char]
"TextZipper { "
                     , [Char]
"above = "
                     , [a] -> [Char]
forall a. Show a => a -> [Char]
show ([a] -> [Char]) -> [a] -> [Char]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
                     , [Char]
", below = "
                     , [a] -> [Char]
forall a. Show a => a -> [Char]
show ([a] -> [Char]) -> [a] -> [Char]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
                     , [Char]
", toLeft = "
                     , a -> [Char]
forall a. Show a => a -> [Char]
show (a -> [Char]) -> a -> [Char]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz
                     , [Char]
", toRight = "
                     , a -> [Char]
forall a. Show a => a -> [Char]
show (a -> [Char]) -> a -> [Char]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz
                     , [Char]
" }"
                     ]

-- | Create a zipper using a custom text storage type. Takes the initial
-- text as well as all of the functions necessary to manipulate the
-- underlying text values.
mkZipper :: (Monoid a) =>
            (Char -> a)
         -- ^A singleton constructor.
         -> (Int -> a -> a)
         -- ^'drop'.
         -> (Int -> a -> a)
         -- ^'take'.
         -> (a -> Int)
         -- ^'length'.
         -> (a -> Char)
         -- ^'last'.
         -> (a -> a)
         -- ^'init'.
         -> (a -> Bool)
         -- ^'null'.
         -> (a -> [a])
         -- ^'lines'.
         -> (a -> [Char])
         -- ^'toList'.
         -> [a]
         -- ^The initial lines of text.
         -> Maybe Int
         -- ^Limit to this many lines of text ('Nothing' means no limit).
         -> TextZipper a
mkZipper :: (Char -> a)
-> (Int -> a -> a)
-> (Int -> a -> a)
-> (a -> Int)
-> (a -> Char)
-> (a -> a)
-> (a -> Bool)
-> (a -> [a])
-> (a -> [Char])
-> [a]
-> Maybe Int
-> TextZipper a
mkZipper Char -> a
fromCh Int -> a -> a
drp Int -> a -> a
tk a -> Int
lngth a -> Char
lst a -> a
int a -> Bool
nl a -> [a]
linesFunc a -> [Char]
toListF [a]
ls Maybe Int
lmt =
    let limitedLs :: [a]
limitedLs = case Maybe Int
lmt of
          Maybe Int
Nothing -> [a]
ls
          Just Int
n -> Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
n [a]
ls
        (a
first, [a]
rest) = if [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
limitedLs
                        then (a
forall a. Monoid a => a
mempty, [a]
forall a. Monoid a => a
mempty)
                        else ([a] -> a
forall a. [a] -> a
head [a]
limitedLs, [a] -> [a]
forall a. [a] -> [a]
tail [a]
limitedLs)
    in a
-> a
-> [a]
-> [a]
-> (Char -> a)
-> (Int -> a -> a)
-> (Int -> a -> a)
-> (a -> Int)
-> (a -> Char)
-> (a -> a)
-> (a -> Bool)
-> (a -> [a])
-> (a -> [Char])
-> Maybe Int
-> TextZipper a
forall a.
a
-> a
-> [a]
-> [a]
-> (Char -> a)
-> (Int -> a -> a)
-> (Int -> a -> a)
-> (a -> Int)
-> (a -> Char)
-> (a -> a)
-> (a -> Bool)
-> (a -> [a])
-> (a -> [Char])
-> Maybe Int
-> TextZipper a
TZ a
forall a. Monoid a => a
mempty a
first [] [a]
rest Char -> a
fromCh Int -> a -> a
drp Int -> a -> a
tk a -> Int
lngth a -> Char
lst a -> a
int a -> Bool
nl a -> [a]
linesFunc a -> [Char]
toListF Maybe Int
lmt

-- | Get the text contents of the zipper.
getText :: (Monoid a) => TextZipper a -> [a]
getText :: TextZipper a -> [a]
getText TextZipper a
tz = [[a]] -> [a]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
                    , [TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz]
                    , TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
                    ]

-- | Return the lengths of the lines in the zipper.
lineLengths :: (Monoid a) => TextZipper a -> [Int]
lineLengths :: TextZipper a -> [Int]
lineLengths TextZipper a
tz = (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz) (a -> Int) -> [a] -> [Int]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [[a]] -> [a]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
                                         , [TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz]
                                         , TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
                                         ]

-- | 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 :: TextZipper a -> (Int, Int)
cursorPosition :: TextZipper a -> (Int, Int)
cursorPosition TextZipper a
tz = ([a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([a] -> Int) -> [a] -> Int
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz, TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (a -> Int) -> a -> Int
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz)

-- | 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 a) => (Int, Int) -> TextZipper a -> TextZipper a
moveCursor :: (Int, Int) -> TextZipper a -> TextZipper a
moveCursor (Int
row, Int
col) TextZipper a
tz =
    let t :: [a]
t = TextZipper a -> [a]
forall a. Monoid a => TextZipper a -> [a]
getText TextZipper a
tz
    in if Int
row Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0
           Bool -> Bool -> Bool
|| Int
row Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
t
           Bool -> Bool -> Bool
|| Int
col Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0
           Bool -> Bool -> Bool
|| Int
col Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz ([a]
t [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
row)
       then TextZipper a
tz
       else TextZipper a
tz { above :: [a]
above = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
row [a]
t
               , below :: [a]
below = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop (Int
row Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [a]
t
               , toLeft :: a
toLeft = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz Int
col ([a]
t [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
row)
               , toRight :: a
toRight = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz Int
col ([a]
t [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
row)
               }

-- | Move the cursor to the specified row and column. Invalid cursor
-- positions will be reinterpreted as the closest valid position. Valid
-- cursor positions range as described for 'cursorPosition'.
moveCursorClosest :: (Monoid a) => (Int, Int) -> TextZipper a -> TextZipper a
moveCursorClosest :: (Int, Int) -> TextZipper a -> TextZipper a
moveCursorClosest (Int
row, Int
col) TextZipper a
tz =
    let t :: [a]
t = TextZipper a -> [a]
forall a. Monoid a => TextZipper a -> [a]
getText TextZipper a
tz
        bestRow :: Int
bestRow = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
t Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 Int
row
        bestCol :: Int
bestCol = if Int
bestRow Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
t
                  then Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz ([a]
t [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
bestRow)) (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 Int
col
                  else Int
0
    in TextZipper a
tz { above :: [a]
above = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
bestRow [a]
t
          , below :: [a]
below = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop (Int
bestRow Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [a]
t
          , toLeft :: a
toLeft = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz Int
bestCol ([a]
t [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
bestRow)
          , toRight :: a
toRight = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz Int
bestCol ([a]
t [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
bestRow)
          }

isFirstLine :: TextZipper a -> Bool
isFirstLine :: TextZipper a -> Bool
isFirstLine = [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([a] -> Bool) -> (TextZipper a -> [a]) -> TextZipper a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TextZipper a -> [a]
forall a. TextZipper a -> [a]
above

isLastLine :: TextZipper a -> Bool
isLastLine :: TextZipper a -> Bool
isLastLine = (Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0) (Int -> Bool) -> (TextZipper a -> Int) -> TextZipper a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([a] -> Int) -> (TextZipper a -> [a]) -> TextZipper a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TextZipper a -> [a]
forall a. TextZipper a -> [a]
below

nextLine :: TextZipper a -> a
nextLine :: TextZipper a -> a
nextLine = [a] -> a
forall a. [a] -> a
head ([a] -> a) -> (TextZipper a -> [a]) -> TextZipper a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TextZipper a -> [a]
forall a. TextZipper a -> [a]
below

-- | The line of text on which the zipper's cursor currently resides.
currentLine :: (Monoid a) => TextZipper a -> a
currentLine :: TextZipper a -> a
currentLine TextZipper a
tz = (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) a -> a -> a
forall a. Monoid a => a -> a -> a
`mappend` (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)

-- | Insert a character at the current cursor position.
--
-- If the character is a newline, break the current line.
--
-- If the character is non-printable, ignore it.
--
-- Otherwise insert the character and move the cursor one position to
-- the right.
insertChar :: (Monoid a) => Char -> TextZipper a -> TextZipper a
insertChar :: Char -> TextZipper a -> TextZipper a
insertChar Char
ch TextZipper a
tz = TextZipper a
-> (TextZipper a -> TextZipper a)
-> Maybe (TextZipper a)
-> TextZipper a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe TextZipper a
tz TextZipper a -> TextZipper a
forall a. a -> a
id (Maybe (TextZipper a) -> TextZipper a)
-> Maybe (TextZipper a) -> TextZipper a
forall a b. (a -> b) -> a -> b
$ Char -> TextZipper a -> Maybe (TextZipper a)
forall a. Monoid a => Char -> TextZipper a -> Maybe (TextZipper a)
insertChar_ Char
ch TextZipper a
tz

insertChar_ :: (Monoid a) => Char -> TextZipper a -> Maybe (TextZipper a)
insertChar_ :: Char -> TextZipper a -> Maybe (TextZipper a)
insertChar_ Char
ch TextZipper a
tz
    | Char
ch Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n' = TextZipper a -> Maybe (TextZipper a)
forall a. Monoid a => TextZipper a -> Maybe (TextZipper a)
breakLine_ TextZipper a
tz
    | Char -> Bool
isPrint Char
ch = TextZipper a -> Maybe (TextZipper a)
forall a. a -> Maybe a
Just (TextZipper a -> Maybe (TextZipper a))
-> TextZipper a -> Maybe (TextZipper a)
forall a b. (a -> b) -> a -> b
$ TextZipper a
tz { toLeft :: a
toLeft = TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz a -> a -> a
forall a. Monoid a => a -> a -> a
`mappend` (TextZipper a -> Char -> a
forall a. TextZipper a -> Char -> a
fromChar TextZipper a
tz Char
ch) }
    | Bool
otherwise  = Maybe (TextZipper a)
forall a. Maybe a
Nothing

-- | Insert many characters at the current cursor position. Move the
-- cursor to the end of the inserted text.
insertMany :: (Monoid a) => a -> TextZipper a -> TextZipper a
insertMany :: a -> TextZipper a -> TextZipper a
insertMany a
str TextZipper a
tz =
    let go :: [Char] -> TextZipper a -> TextZipper a
go [] TextZipper a
z = TextZipper a
z
        go (Char
c:[Char]
cs) TextZipper a
z = TextZipper a
-> (TextZipper a -> TextZipper a)
-> Maybe (TextZipper a)
-> TextZipper a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe TextZipper a
z ([Char] -> TextZipper a -> TextZipper a
go [Char]
cs) (Maybe (TextZipper a) -> TextZipper a)
-> Maybe (TextZipper a) -> TextZipper a
forall a b. (a -> b) -> a -> b
$ Char -> TextZipper a -> Maybe (TextZipper a)
forall a. Monoid a => Char -> TextZipper a -> Maybe (TextZipper a)
insertChar_ Char
c TextZipper a
z
    in [Char] -> TextZipper a -> TextZipper a
forall a. Monoid a => [Char] -> TextZipper a -> TextZipper a
go (TextZipper a -> a -> [Char]
forall a. TextZipper a -> a -> [Char]
toList_ TextZipper a
tz a
str) TextZipper a
tz

-- | Insert a line break at the current cursor position.
breakLine :: (Monoid a) => TextZipper a -> TextZipper a
breakLine :: TextZipper a -> TextZipper a
breakLine TextZipper a
tz = TextZipper a
-> (TextZipper a -> TextZipper a)
-> Maybe (TextZipper a)
-> TextZipper a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe TextZipper a
tz TextZipper a -> TextZipper a
forall a. a -> a
id (Maybe (TextZipper a) -> TextZipper a)
-> Maybe (TextZipper a) -> TextZipper a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> Maybe (TextZipper a)
forall a. Monoid a => TextZipper a -> Maybe (TextZipper a)
breakLine_ TextZipper a
tz

breakLine_ :: (Monoid a) => TextZipper a -> Maybe (TextZipper a)
breakLine_ :: TextZipper a -> Maybe (TextZipper a)
breakLine_ TextZipper a
tz =
    -- Plus two because we count the current line and the line we are
    -- about to create; if that number of lines exceeds the limit,
    -- ignore this operation.
    let modified :: TextZipper a
modified = TextZipper a
tz { above :: [a]
above = TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz]
                      , toLeft :: a
toLeft = a
forall a. Monoid a => a
mempty
                      }
    in case TextZipper a -> Maybe Int
forall a. TextZipper a -> Maybe Int
lineLimit TextZipper a
tz of
          Just Int
lim -> if [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
lim
                      then Maybe (TextZipper a)
forall a. Maybe a
Nothing
                      else TextZipper a -> Maybe (TextZipper a)
forall a. a -> Maybe a
Just TextZipper a
modified
          Maybe Int
Nothing -> TextZipper a -> Maybe (TextZipper a)
forall a. a -> Maybe a
Just TextZipper a
modified

-- | Move the cursor to the end of the current line.
gotoEOL :: (Monoid a) => TextZipper a -> TextZipper a
gotoEOL :: TextZipper a -> TextZipper a
gotoEOL TextZipper a
tz = TextZipper a
tz { toLeft :: a
toLeft = TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz
                , toRight :: a
toRight = a
forall a. Monoid a => a
mempty
                }

-- | Move the cursor to the end of a text zipper.
gotoEOF :: (Monoid a) => TextZipper a -> TextZipper a
gotoEOF :: TextZipper a -> TextZipper a
gotoEOF TextZipper a
tz =
    TextZipper a
tz { toLeft :: a
toLeft = a
end
       , toRight :: a
toRight = a
forall a. Monoid a => a
mempty
       , above :: [a]
above = [a]
top
       , below :: [a]
below = [a]
forall a. Monoid a => a
mempty
       }
   where
       tx :: [a]
tx = TextZipper a -> [a]
forall a. Monoid a => TextZipper a -> [a]
getText TextZipper a
tz
       ([a]
top, a
end) = if [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
tx
                    then ([a]
forall a. Monoid a => a
mempty, a
forall a. Monoid a => a
mempty)
                    else ([a] -> [a]
forall a. [a] -> [a]
init [a]
tx, [a] -> a
forall a. [a] -> a
last [a]
tx)

-- | 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 a) => TextZipper a -> TextZipper a
killToEOL :: TextZipper a -> TextZipper a
killToEOL TextZipper a
tz
    | (TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (a -> Bool) -> a -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) Bool -> Bool -> Bool
&& (TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (a -> Bool) -> a -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz) Bool -> Bool -> Bool
&&
      (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([a] -> Bool) -> [a] -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz) =
          TextZipper a
tz { toRight :: a
toRight = [a] -> a
forall a. [a] -> a
head ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
             , below :: [a]
below = [a] -> [a]
forall a. [a] -> [a]
tail ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
             }
    | Bool
otherwise = TextZipper a
tz { toRight :: a
toRight = a
forall a. Monoid a => a
mempty
                     }

-- | Remove all text from the cursor position to the beginning of the
-- current line.
killToBOL :: Monoid a => TextZipper a -> TextZipper a
killToBOL :: TextZipper a -> TextZipper a
killToBOL TextZipper a
tz = TextZipper a
tz { toLeft :: a
toLeft = a
forall a. Monoid a => a
mempty
                  }

-- | Remove all text from the cursor position to the end of the text
-- zipper. If the cursor is at the beginning of a line and the line is
-- empty, the entire line will be removed.
killToEOF :: (Monoid a) => TextZipper a -> TextZipper a
killToEOF :: TextZipper a -> TextZipper a
killToEOF TextZipper a
tz =
    TextZipper a
tz { toRight :: a
toRight = a
forall a. Monoid a => a
mempty
       , below :: [a]
below = [a]
forall a. Monoid a => a
mempty
       }

-- | Remove all text from the cursor position to the beginning of the
-- text zipper.
killToBOF :: Monoid a => TextZipper a -> TextZipper a
killToBOF :: TextZipper a -> TextZipper a
killToBOF TextZipper a
tz =
    TextZipper a
tz { toLeft :: a
toLeft = a
forall a. Monoid a => a
mempty
       , above :: [a]
above = [a]
forall a. Monoid a => a
mempty
       }

-- | Delete the character preceding the cursor position, and move the
-- cursor backwards by one character.
deletePrevChar :: (Eq a, Monoid a) => TextZipper a -> TextZipper a
deletePrevChar :: TextZipper a -> TextZipper a
deletePrevChar TextZipper a
tz
    | TextZipper a -> TextZipper a
forall a. Monoid a => TextZipper a -> TextZipper a
moveLeft TextZipper a
tz TextZipper a -> TextZipper a -> Bool
forall a. Eq a => a -> a -> Bool
== TextZipper a
tz = TextZipper a
tz
    | Bool
otherwise = TextZipper a -> TextZipper a
forall a. Monoid a => TextZipper a -> TextZipper a
deleteChar (TextZipper a -> TextZipper a) -> TextZipper a -> TextZipper a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> TextZipper a
forall a. Monoid a => TextZipper a -> TextZipper a
moveLeft TextZipper a
tz

-- | 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 a) => TextZipper a -> TextZipper a
deleteChar :: TextZipper a -> TextZipper a
deleteChar TextZipper a
tz
    -- Can we just remove a char from the current line?
    | (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)) =
        TextZipper a
tz { toRight :: a
toRight = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz Int
1 (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz
           }
    -- Do we need to collapse the previous line onto the current one?
    | TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz) Bool -> Bool -> Bool
&& (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([a] -> Bool) -> [a] -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz) =
        TextZipper a
tz { toRight :: a
toRight = [a] -> a
forall a. [a] -> a
head ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           , below :: [a]
below = [a] -> [a]
forall a. [a] -> [a]
tail ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           }
    | Bool
otherwise = TextZipper a
tz

-- | Get the Char on which the cursor currently resides. If the cursor
-- is at the end of the text or the text is empty return @Nothing@.
currentChar :: TextZipper a -> Maybe Char
currentChar :: TextZipper a -> Maybe Char
currentChar TextZipper a
tz
  | Bool -> Bool
not (TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)) =
    Char -> Maybe Char
forall a. a -> Maybe a
Just (TextZipper a -> a -> Char
forall a. TextZipper a -> a -> Char
last_ TextZipper a
tz (TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz Int
1 (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)))
  | Bool
otherwise = Maybe Char
forall a. Maybe a
Nothing

-- | Get the Char after the cursor position. If the cursor is at the end
-- of a line return the first character of the next line, or if that one
-- is empty as well, return @Nothing@.
nextChar :: (Monoid a) => TextZipper a -> Maybe Char
nextChar :: TextZipper a -> Maybe Char
nextChar TextZipper a
tz = TextZipper a -> Maybe Char
forall a. TextZipper a -> Maybe Char
currentChar (TextZipper a -> TextZipper a
forall a. Monoid a => TextZipper a -> TextZipper a
moveRight TextZipper a
tz)

-- | Get the Char before the cursor position. If the cursor is at the
-- beginning of the text, return @Nothing@
previousChar :: (Monoid a) => TextZipper a -> Maybe Char
previousChar :: TextZipper a -> Maybe Char
previousChar TextZipper a
tz
  -- Only return Nothing if we are at the beginning of a line and only empty
  -- lines are above
  | (Int, Int) -> Int
forall a b. (a, b) -> b
snd (TextZipper a -> (Int, Int)
forall a. TextZipper a -> (Int, Int)
cursorPosition TextZipper a
tz) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
&& (a -> Bool) -> [a] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz) (TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz) =
    Maybe Char
forall a. Maybe a
Nothing
  | Bool
otherwise =
    TextZipper a -> Maybe Char
forall a. TextZipper a -> Maybe Char
currentChar (TextZipper a -> TextZipper a
forall a. Monoid a => TextZipper a -> TextZipper a
moveLeft TextZipper a
tz)

-- | Move the cursor to the beginning of the current line.
gotoBOL :: (Monoid a) => TextZipper a -> TextZipper a
gotoBOL :: TextZipper a -> TextZipper a
gotoBOL TextZipper a
tz = TextZipper a
tz { toLeft :: a
toLeft = a
forall a. Monoid a => a
mempty
                , toRight :: a
toRight = TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz
                }

-- | Move the cursor to the beginning of a text zipper.
gotoBOF :: (Monoid a) => TextZipper a -> TextZipper a
gotoBOF :: TextZipper a -> TextZipper a
gotoBOF TextZipper a
tz =
    TextZipper a
tz { toLeft :: a
toLeft = a
forall a. Monoid a => a
mempty
       , toRight :: a
toRight = a
first
       , above :: [a]
above = [a]
forall a. Monoid a => a
mempty
       , below :: [a]
below = [a]
rest
       }
    where
        tx :: [a]
tx = TextZipper a -> [a]
forall a. Monoid a => TextZipper a -> [a]
getText TextZipper a
tz
        (a
first, [a]
rest) = if [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
tx
                        then (a
forall a. Monoid a => a
mempty, [a]
forall a. Monoid a => a
mempty)
                        else ([a] -> a
forall a. [a] -> a
head [a]
tx, [a] -> [a]
forall a. [a] -> [a]
tail [a]
tx)

-- | 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 a) => TextZipper a -> TextZipper a
moveRight :: TextZipper a -> TextZipper a
moveRight TextZipper a
tz
    -- Are we able to keep moving right on the current line?
    | Bool -> Bool
not (TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)) =
        TextZipper a
tz { toLeft :: a
toLeft = TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz
                      a -> a -> a
forall a. Monoid a => a -> a -> a
`mappend` (TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz Int
1 (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)
           , toRight :: a
toRight = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz Int
1 (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)
           }
    -- If we are going to go beyond the end of the current line, can
    -- we move to the next one?
    | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz) =
        TextZipper a
tz { above :: [a]
above = TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz]
           , below :: [a]
below = [a] -> [a]
forall a. [a] -> [a]
tail ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           , toLeft :: a
toLeft = a
forall a. Monoid a => a
mempty
           , toRight :: a
toRight = TextZipper a -> a
forall a. TextZipper a -> a
nextLine TextZipper a
tz
           }
    | Bool
otherwise = TextZipper a
tz

-- | 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 a) => TextZipper a -> TextZipper a
moveLeft :: TextZipper a -> TextZipper a
moveLeft TextZipper a
tz
    -- Are we able to keep moving left on the current line?
    | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) =
        TextZipper a
tz { toLeft :: a
toLeft = TextZipper a -> a -> a
forall a. TextZipper a -> a -> a
init_ TextZipper a
tz (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz
           , toRight :: a
toRight = TextZipper a -> Char -> a
forall a. TextZipper a -> Char -> a
fromChar TextZipper a
tz (TextZipper a -> a -> Char
forall a. TextZipper a -> a -> Char
last_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz))
                       a -> a -> a
forall a. Monoid a => a -> a -> a
`mappend` TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz
           }
    -- If we are going to go beyond the beginning of the current line,
    -- can we move to the end of the previous one?
    | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz) =
        TextZipper a
tz { above :: [a]
above = [a] -> [a]
forall a. [a] -> [a]
init ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
           , below :: [a]
below = TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz a -> [a] -> [a]
forall a. a -> [a] -> [a]
: TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           , toLeft :: a
toLeft = [a] -> a
forall a. [a] -> a
last ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
           , toRight :: a
toRight = a
forall a. Monoid a => a
mempty
           }
    | Bool
otherwise = TextZipper a
tz

-- | 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 a) => TextZipper a -> TextZipper a
moveUp :: TextZipper a -> TextZipper a
moveUp TextZipper a
tz
    -- Is there a line above at least as long as the current one?
    | (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> Bool
forall a. TextZipper a -> Bool
isFirstLine TextZipper a
tz) Bool -> Bool -> Bool
&&
      (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (a -> Int) -> a -> Int
forall a b. (a -> b) -> a -> b
$ [a] -> a
forall a. [a] -> a
last ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) =
        TextZipper a
tz { below :: [a]
below = TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz a -> [a] -> [a]
forall a. a -> [a] -> [a]
: TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           , above :: [a]
above = [a] -> [a]
forall a. [a] -> [a]
init ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
           , toLeft :: a
toLeft = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (a -> Int) -> a -> Int
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) ([a] -> a
forall a. [a] -> a
last ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz)
           , toRight :: a
toRight = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (a -> Int) -> a -> Int
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) ([a] -> a
forall a. [a] -> a
last ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz)
           }
    -- Or if there is a line above, just go to the end of it
    | (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> Bool
forall a. TextZipper a -> Bool
isFirstLine TextZipper a
tz) =
        TextZipper a
tz { above :: [a]
above = [a] -> [a]
forall a. [a] -> [a]
init ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
           , below :: [a]
below = TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz a -> [a] -> [a]
forall a. a -> [a] -> [a]
: TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           , toLeft :: a
toLeft = [a] -> a
forall a. [a] -> a
last ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz
           , toRight :: a
toRight = a
forall a. Monoid a => a
mempty
           }
    -- If nothing else, go to the beginning of the current line
    | Bool
otherwise = TextZipper a -> TextZipper a
forall a. Monoid a => TextZipper a -> TextZipper a
gotoBOL TextZipper a
tz

-- | 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 a) => TextZipper a -> TextZipper a
moveDown :: TextZipper a -> TextZipper a
moveDown TextZipper a
tz
    -- Is there a line below at least as long as the current one?
    | (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> Bool
forall a. TextZipper a -> Bool
isLastLine TextZipper a
tz) Bool -> Bool -> Bool
&&
      (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (a -> Int) -> a -> Int
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
nextLine TextZipper a
tz) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) =
        TextZipper a
tz { below :: [a]
below = [a] -> [a]
forall a. [a] -> [a]
tail ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           , above :: [a]
above = TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz]
           , toLeft :: a
toLeft = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (a -> Int) -> a -> Int
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) (TextZipper a -> a
forall a. TextZipper a -> a
nextLine TextZipper a
tz)
           , toRight :: a
toRight = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz (TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (a -> Int) -> a -> Int
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) (TextZipper a -> a
forall a. TextZipper a -> a
nextLine TextZipper a
tz)
           }
    -- Or if there is a line below, just go to the end of it
    | (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ TextZipper a -> Bool
forall a. TextZipper a -> Bool
isLastLine TextZipper a
tz) =
        TextZipper a
tz { above :: [a]
above = TextZipper a -> [a]
forall a. TextZipper a -> [a]
above TextZipper a
tz [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [TextZipper a -> a
forall a. Monoid a => TextZipper a -> a
currentLine TextZipper a
tz]
           , below :: [a]
below = [a] -> [a]
forall a. [a] -> [a]
tail ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ TextZipper a -> [a]
forall a. TextZipper a -> [a]
below TextZipper a
tz
           , toLeft :: a
toLeft = TextZipper a -> a
forall a. TextZipper a -> a
nextLine TextZipper a
tz
           , toRight :: a
toRight = a
forall a. Monoid a => a
mempty
           }
    -- If nothing else, go to the end of the current line
    | Bool
otherwise = TextZipper a -> TextZipper a
forall a. Monoid a => TextZipper a -> TextZipper a
gotoEOL TextZipper a
tz

-- | Transpose the character before the cursor with the one at the
-- cursor position and move the cursor one position to the right. If
-- the cursor is at the end of the current line, transpose the current
-- line's last two characters.
transposeChars :: (Monoid a) => TextZipper a -> TextZipper a
transposeChars :: TextZipper a -> TextZipper a
transposeChars TextZipper a
tz
    | TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) = TextZipper a
tz
    | TextZipper a -> a -> Bool
forall a. TextZipper a -> a -> Bool
null_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz) =
        if TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
2
        then TextZipper a
tz
        else let prefixLen :: Int
prefixLen = TextZipper a -> a -> Int
forall a. TextZipper a -> a -> Int
length_ TextZipper a
tz (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2
                 prefix :: a
prefix = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz Int
prefixLen (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz)
                 lastTwo :: a
lastTwo = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz Int
prefixLen (TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz)
                 a :: a
a = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz Int
1 a
lastTwo
                 b :: a
b = TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz Int
1 a
lastTwo
             in TextZipper a
tz { toLeft :: a
toLeft = a
prefix a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
b a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
a
                   }
    | Bool
otherwise = TextZipper a
tz { toLeft :: a
toLeft = (TextZipper a -> a -> a
forall a. TextZipper a -> a -> a
init_ TextZipper a
tz (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz) a -> a -> a
forall a. Semigroup a => a -> a -> a
<>
                                (TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
take_ TextZipper a
tz Int
1 (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz) a -> a -> a
forall a. Semigroup a => a -> a -> a
<>
                                (TextZipper a -> Char -> a
forall a. TextZipper a -> Char -> a
fromChar TextZipper a
tz (Char -> a) -> Char -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a -> Char
forall a. TextZipper a -> a -> Char
last_ TextZipper a
tz (a -> Char) -> a -> Char
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toLeft TextZipper a
tz)
                     , toRight :: a
toRight = (TextZipper a -> Int -> a -> a
forall a. TextZipper a -> Int -> a -> a
drop_ TextZipper a
tz Int
1 (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ TextZipper a -> a
forall a. TextZipper a -> a
toRight TextZipper a
tz)
                     }

-- | Construct a zipper from list values.
stringZipper :: [String] -> Maybe Int -> TextZipper String
stringZipper :: [[Char]] -> Maybe Int -> TextZipper [Char]
stringZipper =
    (Char -> [Char])
-> (Int -> ShowS)
-> (Int -> ShowS)
-> ([Char] -> Int)
-> ([Char] -> Char)
-> ShowS
-> ([Char] -> Bool)
-> ([Char] -> [[Char]])
-> ShowS
-> [[Char]]
-> Maybe Int
-> TextZipper [Char]
forall a.
Monoid a =>
(Char -> a)
-> (Int -> a -> a)
-> (Int -> a -> a)
-> (a -> Int)
-> (a -> Char)
-> (a -> a)
-> (a -> Bool)
-> (a -> [a])
-> (a -> [Char])
-> [a]
-> Maybe Int
-> TextZipper a
mkZipper (Char -> ShowS
forall a. a -> [a] -> [a]
:[]) Int -> ShowS
forall a. Int -> [a] -> [a]
drop Int -> ShowS
forall a. Int -> [a] -> [a]
take [Char] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char] -> Char
forall a. [a] -> a
last ShowS
forall a. [a] -> [a]
init [Char] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char] -> [[Char]]
lines ShowS
forall a. a -> a
id

-- | Construct a zipper from vectors of characters.
vectorZipper :: [V.Vector Char] -> Maybe Int -> TextZipper (V.Vector Char)
vectorZipper :: [Vector Char] -> Maybe Int -> TextZipper (Vector Char)
vectorZipper =
    (Char -> Vector Char)
-> (Int -> Vector Char -> Vector Char)
-> (Int -> Vector Char -> Vector Char)
-> (Vector Char -> Int)
-> (Vector Char -> Char)
-> (Vector Char -> Vector Char)
-> (Vector Char -> Bool)
-> (Vector Char -> [Vector Char])
-> (Vector Char -> [Char])
-> [Vector Char]
-> Maybe Int
-> TextZipper (Vector Char)
forall a.
Monoid a =>
(Char -> a)
-> (Int -> a -> a)
-> (Int -> a -> a)
-> (a -> Int)
-> (a -> Char)
-> (a -> a)
-> (a -> Bool)
-> (a -> [a])
-> (a -> [Char])
-> [a]
-> Maybe Int
-> TextZipper a
mkZipper Char -> Vector Char
forall a. a -> Vector a
V.singleton Int -> Vector Char -> Vector Char
forall a. Int -> Vector a -> Vector a
V.drop Int -> Vector Char -> Vector Char
forall a. Int -> Vector a -> Vector a
V.take Vector Char -> Int
forall a. Vector a -> Int
V.length Vector Char -> Char
forall a. Vector a -> a
V.last Vector Char -> Vector Char
forall a. Vector a -> Vector a
V.init Vector Char -> Bool
forall a. Vector a -> Bool
V.null Vector Char -> [Vector Char]
V.vecLines Vector Char -> [Char]
forall a. Vector a -> [a]
V.toList

-- | Empty a zipper.
clearZipper :: (Monoid a) => TextZipper a -> TextZipper a
clearZipper :: TextZipper a -> TextZipper a
clearZipper TextZipper a
tz =
    TextZipper a
tz { toLeft :: a
toLeft = a
forall a. Monoid a => a
mempty
       , toRight :: a
toRight = a
forall a. Monoid a => a
mempty
       , above :: [a]
above = []
       , below :: [a]
below = []
       }

-- | Construct a zipper from 'T.Text' values.
textZipper :: [T.Text] -> Maybe Int -> TextZipper T.Text
textZipper :: [Text] -> Maybe Int -> TextZipper Text
textZipper =
    (Char -> Text)
-> (Int -> Text -> Text)
-> (Int -> Text -> Text)
-> (Text -> Int)
-> (Text -> Char)
-> (Text -> Text)
-> (Text -> Bool)
-> (Text -> [Text])
-> (Text -> [Char])
-> [Text]
-> Maybe Int
-> TextZipper Text
forall a.
Monoid a =>
(Char -> a)
-> (Int -> a -> a)
-> (Int -> a -> a)
-> (a -> Int)
-> (a -> Char)
-> (a -> a)
-> (a -> Bool)
-> (a -> [a])
-> (a -> [Char])
-> [a]
-> Maybe Int
-> TextZipper a
mkZipper Char -> Text
T.singleton Int -> Text -> Text
T.drop Int -> Text -> Text
T.take Text -> Int
T.length Text -> Char
T.last Text -> Text
T.init Text -> Bool
T.null Text -> [Text]
T.lines Text -> [Char]
T.unpack