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 (dropWord8, lengthWord8, takeWord8) -- | Class of data types containing `Text` that can be accessed. class TextContainer a where -- | Extract a `Text` from its container. getText :: a -> Text -- | As a trivial instance, each `Text` contains itself. instance TextContainer Text where getText = id -- | 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) -- | As a trivial instance, each `Text` can be split directly. instance SeparableTextContainer Text where splitTextAt8 n t = (t1, t2) where t1 = takeWord8 (fromIntegral n) t t2 = dropWord8 (fromIntegral n) t -- | 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