module Data.Text.ParagraphLayout.Internal.TextContainer
(SeparableTextContainer
,TextContainer
,collapse
,getText
,splitTextAt8
,splitTextsAt8
)
where
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Foreign (lengthWord8)
-- | Class of data types containing `Text` that can be accessed.
class TextContainer a where
-- | Extract a `Text` from its container.
getText :: a -> Text
-- | Class of data types containing `Text` that can be split at a given number
-- of `Data.Word.Word8` units from the start of the text.
class TextContainer a => SeparableTextContainer a where
-- | Split the given `SeparableTextContainer` at the given number of
-- `Data.Word.Word8` units from the start of the text, preserving whatever
-- constraints the instance requires.
splitTextAt8 :: Int -> a -> (a, a)
-- | Treat a list of text containers as a contiguous sequence,
-- and make a split at the given number of `Data.Word.Word8` from the beginning
-- of this sequence.
--
-- If @n@ falls on a container boundary, the total number of output containers
-- will equal the number of input containers; otherwise, it will be one larger.
splitTextsAt8 :: SeparableTextContainer a => Int -> [a] -> ([a], [a])
splitTextsAt8 n rs = (pre, post)
where
pre = reverse rpre
(rpre, post) = splitTextsAt8' n [] rs
splitTextsAt8' :: SeparableTextContainer a => Int -> [a] -> [a] -> ([a], [a])
splitTextsAt8' _ rpre [] = (rpre, [])
splitTextsAt8' n rpre (r:rs)
| n <= 0 = (rpre, r:rs)
| n >= l = splitTextsAt8' (n - l) (r:rpre) (rs)
| otherwise = let (r1, r2) = splitTextAt8 n r in (r1:rpre, r2:rs)
where
l = lengthWord8 $ getText r
-- | If the first container in the list is empty, remove it.
collapse :: SeparableTextContainer a => NonEmpty a -> [a]
collapse (x :| xs)
| Text.null (getText x) = xs
| otherwise = x:xs