{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE Safe #-}
module WEditor.Internal.Para (
EditingPara,
VisibleParaAfter,
VisibleParaBefore,
appendToPara,
atParaBack,
atParaFront,
catLinesAfter,
catLinesBefore,
countLinesAfter,
countLinesBefore,
editPara,
getAfterLines,
getBeforeLines,
getCurrentLine,
getParaCharCount,
getParaCursorChar,
getParaCursorLine,
getParaEditChar,
modifyPara,
moveParaCursor,
paraCursorMovable,
parseParaAfter,
parseParaBefore,
prependToPara,
reparsePara,
seekParaBack,
seekParaFront,
setParaCursorChar,
setParaEditChar,
splitPara,
unparsePara,
unparseParaAfter,
unparseParaBefore,
viewAfterLines,
viewBeforeLines,
viewParaAfter,
viewParaBefore,
) where
import WEditor.Base.Editor
import WEditor.Base.Line
import WEditor.Base.Para
import WEditor.Base.Parser
import WEditor.Internal.Line
data VisibleParaBefore c b =
VisibleParaBefore {
vpbLines :: [VisibleLine c b],
vpbSize :: Int
}
deriving (Show)
data VisibleParaAfter c b =
VisibleParaAfter {
vpaLines :: [VisibleLine c b]
}
deriving (Show)
data EditingPara c b =
EditingPara {
epBefore :: [VisibleLine c b],
epEditing :: EditingLine c b,
epAfter :: [VisibleLine c b],
epSizeBefore :: Int
}
deriving (Show)
viewBeforeLines :: VisibleParaBefore c b -> [VisibleLine c b]
viewBeforeLines = reverse . vpbLines
viewAfterLines :: VisibleParaAfter c b -> [VisibleLine c b]
viewAfterLines = vpaLines
visibleParaBefore :: [VisibleLine c b] -> VisibleParaBefore c b
visibleParaBefore ls = VisibleParaBefore ls (sum $ map (length . vlText) ls)
parseParaBefore :: FixedFontParser a c => a -> UnparsedPara c -> VisibleParaBefore c (BreakType a)
parseParaBefore parser (UnparsedPara cs) =
VisibleParaBefore (reverse $ breakLines parser cs) (length cs)
parseParaAfter :: FixedFontParser a c => a -> UnparsedPara c -> VisibleParaAfter c (BreakType a)
parseParaAfter parser (UnparsedPara cs) = VisibleParaAfter $ breakLines parser cs
unparseParaBefore :: VisibleParaBefore c b -> UnparsedPara c
unparseParaBefore (VisibleParaBefore ls _) = UnparsedPara $ joinLines $ reverse ls
unparseParaAfter :: VisibleParaAfter c b -> UnparsedPara c
unparseParaAfter (VisibleParaAfter ls) = UnparsedPara $ joinLines ls
editPara :: FixedFontParser a c => a -> UnparsedPara c -> EditingPara c (BreakType a)
editPara parser (UnparsedPara cs) = EditingPara [] (editLine line) after 0 where
(line:after) = nonempty $ breakLines parser cs
nonempty [] = [emptyLine parser]
nonempty ls = ls
unparsePara :: EditingPara c b -> UnparsedPara c
unparsePara (EditingPara bs l as _) = UnparsedPara $ joinLines ls where
ls = reverse bs ++ [viewLine l] ++ as
reparsePara :: FixedFontParser a c =>
a -> EditingPara c (BreakType a) -> EditingPara c (BreakType a)
reparsePara parser (EditingPara bs l as n) = reparseParaTail parser revised where
revised = EditingPara bs2 l2 as (sum $ map (length . vlText) bs2)
bs' = reverse $ breakLines parser $ joinLines (reverse bs)
(l2,bs2)
| null bs' = (l,[])
| otherwise = (head bs' `prependToLine` l,tail bs')
viewParaBefore :: EditingPara c b -> VisibleParaBefore c b
viewParaBefore (EditingPara bs l as _) = visibleParaBefore ls where
ls = reverse as ++ [viewLine l] ++ bs
viewParaAfter :: EditingPara c b -> VisibleParaAfter c b
viewParaAfter (EditingPara bs l as _) = VisibleParaAfter ls where
ls = reverse bs ++ [viewLine l] ++ as
getBeforeLines :: EditingPara c b -> VisibleParaBefore c b
getBeforeLines = visibleParaBefore . epBefore
getCurrentLine :: EditingPara c b -> VisibleLine c b
getCurrentLine = viewLine . epEditing
getAfterLines :: EditingPara c b -> VisibleParaAfter c b
getAfterLines = VisibleParaAfter . epAfter
catLinesBefore :: [VisibleParaBefore c b] -> [VisibleLine c b]
catLinesBefore = concat . map vpbLines
catLinesAfter :: [VisibleParaAfter c b] -> [VisibleLine c b]
catLinesAfter = concat . map vpaLines
countLinesBefore :: Int -> [VisibleParaBefore c b] -> Int
countLinesBefore n = length . take n . catLinesBefore
countLinesAfter :: Int -> [VisibleParaAfter c b] -> Int
countLinesAfter n = length . take n . catLinesAfter
getParaCursorLine :: EditingPara c b -> Int
getParaCursorLine = length . epBefore
getParaCursorChar :: EditingPara c b -> Int
getParaCursorChar = getLineCursor . epEditing
setParaCursorChar :: Int -> EditingPara c b -> EditingPara c b
setParaCursorChar k e@(EditingPara bs l as n) = (EditingPara bs (setLineCursor k l) as n)
getParaCharCount :: EditingPara c b -> Int
getParaCharCount = length . upText . unparsePara
getParaEditChar :: EditingPara c b -> Int
getParaEditChar (EditingPara _ l _ n) = n + getLineCursor l
setParaEditChar :: Int -> EditingPara c b -> EditingPara c b
setParaEditChar k p
| getParaEditChar p > k && not (atParaFront p) = setParaEditChar k $ moveParaCursor MovePrev p
| getParaEditChar p < k && not (atParaBack p) = setParaEditChar k $ moveParaCursor MoveNext p
| otherwise = p
splitPara :: FixedFontParser a c => a -> EditingPara c (BreakType a) -> (UnparsedPara c,UnparsedPara c)
splitPara parser (EditingPara bs l as _) = let (b,a) = splitLineAtCursor (splitLine parser) l in
(unparseParaBefore $ VisibleParaBefore (b:bs) 0,
unparseParaAfter $ VisibleParaAfter (a:as))
paraCursorMovable :: MoveDirection -> EditingPara c b -> Bool
paraCursorMovable d
| d == MoveUp = not . atParaTop
| d == MoveDown = not . atParaBottom
| d == MovePrev = not . atParaFront
| d == MoveNext = not . atParaBack
| d == MoveHome || d == MoveEnd = const True
| otherwise = const False
moveParaCursor :: MoveDirection -> EditingPara c b -> EditingPara c b
moveParaCursor d p@(EditingPara bs l as n) = revised where
revised
| d == MoveHome || d == MoveEnd = EditingPara bs (moveLineCursor d l) as n
| d == MoveUp && atParaTop p = EditingPara bs (moveLineCursor MoveUp l) as n
| d == MoveDown && atParaBottom p = EditingPara bs (moveLineCursor MoveDown l) as n
| not (paraCursorMovable d p) = p
| d == MoveUp =
setParaCursorChar (getLineCursor l) $
EditingPara (tail bs) (editLine $ head bs) (viewLine l:as) (n-length (vlText $ head bs))
| d == MoveDown =
setParaCursorChar (getLineCursor l) $
EditingPara (viewLine l:bs) (editLine $ head as) (tail as) (n+length (vlText $ viewLine l))
| lineCursorMovable d l = EditingPara bs (moveLineCursor d l) as n
| d == MovePrev = setBack $ moveParaCursor MoveUp p
| d == MoveNext = setFront $ moveParaCursor MoveDown p
setBack (EditingPara bs l as n) = (EditingPara bs (moveLineCursor MoveDown l) as n)
setFront (EditingPara bs l as n) = (EditingPara bs (moveLineCursor MoveUp l) as n)
atParaFront :: EditingPara c b -> Bool
atParaFront p@(EditingPara _ l _ _) = atParaTop p && atLineFront l
atParaBack :: EditingPara c b -> Bool
atParaBack p@(EditingPara _ l _ _) = atParaBottom p && atLineBack l
seekParaFront :: EditingPara c b -> EditingPara c b
seekParaFront p
| atParaFront p = p
| otherwise = seekParaFront $ moveParaCursor MoveUp p
seekParaBack :: EditingPara c b -> EditingPara c b
seekParaBack p
| atParaBack p = p
| otherwise = seekParaBack $ moveParaCursor MoveDown p
appendToPara :: FixedFontParser a c
=> a -> EditingPara c (BreakType a) -> VisibleParaAfter c (BreakType a) -> EditingPara c (BreakType a)
appendToPara parser (EditingPara bs l as n) (VisibleParaAfter cs) = reparseParaTail parser revised where
revised = EditingPara bs l (as ++ cs) n
prependToPara :: FixedFontParser a c
=> a -> VisibleParaBefore c (BreakType a) -> EditingPara c (BreakType a) -> EditingPara c (BreakType a)
prependToPara parser (VisibleParaBefore cs _) (EditingPara bs l as _) = reparseParaTail parser revised where
revised = EditingPara bs2 l2 as n2
(VisibleParaBefore bs' n') = parseParaBefore parser $ unparseParaBefore $ visibleParaBefore (bs ++ cs)
(l2,bs2,n2) = if null bs'
then (l,[],0)
else (head bs' `prependToLine` l,tail bs',n'-length (vlText $ head bs'))
modifyPara :: FixedFontParser a c
=> a -> EditAction c -> EditDirection -> EditingPara c (BreakType a) -> EditingPara c (BreakType a)
modifyPara parser m d p = reparseParaTail parser revised where
(EditingPara bs l as n) = mergeForEdit p
revised = EditingPara bs (modifyLine m d l) as n
reparseParaTail :: FixedFontParser a c
=> a -> EditingPara c (BreakType a) -> EditingPara c (BreakType a)
reparseParaTail parser p@(EditingPara bs l as n) = setParaEditChar offset revised where
offset = getParaEditChar p
revised = EditingPara bs (editLine line) after n
(line:after) = breakLines parser $ joinLines (viewLine l:as)
mergeForEdit :: EditingPara c b -> EditingPara c b
mergeForEdit (EditingPara bs l as n) = EditingPara bs2 l2 as2 n2 where
l2 = addAfter as $ addBefore bs l where
addAfter (v:_) l = l `appendToLine` v
addAfter _ l = l
addBefore (v:_) l = v `prependToLine` l
addBefore _ l = l
bs2 = if null bs then [] else tail bs
as2 = if null as then [] else tail as
n2 = n - (if null bs then 0 else length (vlText $ head bs))
atParaTop :: EditingPara c b -> Bool
atParaTop (EditingPara bs _ _ _) = null bs
atParaBottom :: EditingPara c b -> Bool
atParaBottom (EditingPara _ _ as _) = null as