module Text.PrettyPrint.MPPPC.TwoDim.Combinators.Flow where import Prelude hiding ( length , reverse , take , unwords , words ) import qualified Data.List as List ( foldl' , length , map , reverse ) import qualified Data.List.Split as List ( chunk ) import Text.PrettyPrint.MPPPC.Printable import Text.PrettyPrint.MPPPC.TwoDim.Combinators.Align import Text.PrettyPrint.MPPPC.TwoDim.Combinators.Layout import Text.PrettyPrint.MPPPC.TwoDim.Combinators.Prim import Text.PrettyPrint.MPPPC.TwoDim.Pretty data Word s t = Printable s t => Word { wLen :: Int , getWord :: s } data Line s t = Printable s t => Line { lLen :: Int , getWords :: [Word s t] } data ParaContent s t = Printable s t => ParaContent { fullLines :: [Line s t] , lastLine :: Line s t } data Para s t = Printable s t => Para { paraWidth :: Int , paraContent :: ParaContent s t } para :: Printable s t => Alignment -> Int -> s -> Pretty s t para a n t = (\ ss -> mkParaBox a (List.length ss) ss) $ flow n t columns :: Printable s t => Alignment -> Int -> Int -> s -> [Pretty s t] columns a w h t = map (mkParaBox a h) . List.chunk h $ flow w t mkParaBox :: Printable s t => Alignment -> Int -> [s] -> Pretty s t mkParaBox a n = alignVert top n . vcat a . map text flow :: forall s t. Printable s t => Int -> s -> [s] flow n t = List.map (take n) . getLines $ List.foldl' addWordP (emptyPara n) (List.map mkWord . words $ t) emptyPara :: Printable s t => Int -> Para s t emptyPara pw = Para pw (ParaContent [] (Line 0 [])) getLines :: forall s t. Para s t -> [s] getLines (Para _ (ParaContent ls l)) | lLen l == 0 = process ls | otherwise = process (l:ls) where process :: [Line s t] -> [s] process = map (unwords . List.reverse . map getWord . getWords) . List.reverse mkLine :: Printable s t => [Word s t] -> Line s t mkLine ws = Line (sum (map wLen ws) + List.length ws - 1) ws startLine :: Printable s t => Word s t -> Line s t startLine = mkLine . (:[]) mkWord :: Printable s t => s -> Word s t mkWord w = Word (length w) w addWordP :: Para s t -> Word s t -> Para s t addWordP (Para pw (ParaContent fl l)) w | wordFits pw w l = Para pw (ParaContent fl (addWordL w l)) | otherwise = Para pw (ParaContent (l:fl) (startLine w)) addWordL :: Word s t -> Line s t -> Line s t addWordL w (Line len ws) = Line (len + wLen w + 1) (w:ws) wordFits :: Int -> Word s t -> Line s t -> Bool wordFits pw w l = lLen l == 0 || lLen l + wLen w + 1 <= pw