module Text.PrettyPrint.Compact.Core(Layout(..),Document(..),Doc) where
import Data.List (intercalate,sort,groupBy)
import Data.Function (on)
import Data.Monoid
import Data.Sequence (singleton, Seq, viewl, viewr, ViewL(..), ViewR(..))
import qualified Data.Sequence as S
newtype L = L (Seq String)
deriving (Eq,Ord)
instance Monoid L where
mempty = L (singleton "")
L (viewr -> xs :> x) `mappend` L (viewl -> y :< ys) = L (xs <> singleton (x ++ y) <> fmap (indent ++) ys)
where n = length x
indent = Prelude.replicate n ' '
newtype N = N {fromN :: String}
instance Monoid N where
mempty = N ""
mappend (N x) (N y) = N (x ++ "\n" ++ y)
instance Layout L where
render (L xs) = fromN $ foldMap N xs
text = L . singleton
flush (L xs) = L (xs <> singleton "")
class Monoid d => Layout d where
text :: String -> d
flush :: d -> d
render :: d -> String
class Layout d => Document d where
(<|>) :: d -> d -> d
data M = M {height :: Int,
lastWidth :: Int,
maxWidth :: Int
}
deriving (Show,Eq,Ord)
instance Monoid M where
mempty = text ""
a `mappend` b =
M {maxWidth = max (maxWidth a) (maxWidth b + lastWidth a),
height = height a + height b,
lastWidth = lastWidth a + lastWidth b}
instance Layout M where
text s = M {height = 0, maxWidth = length s, lastWidth = length s}
flush a = M {maxWidth = maxWidth a,
height = height a + 1,
lastWidth = 0}
render = error "don't use this render"
fits :: M -> Bool
fits x = maxWidth x <= 80
class Poset a where
(≺) :: a -> a -> Bool
instance Poset M where
M c1 l1 s1 ≺ M c2 l2 s2 = c1 <= c2 && l1 <= l2 && s1 <= s2
merge :: Ord a => [a] -> [a] -> [a]
merge [] xs = xs
merge xs [] = xs
merge (x:xs) (y:ys)
| x <= y = x:merge xs (y:ys)
| otherwise = y:merge (x:xs) ys
mergeAll :: Ord a => [[a]] -> [a]
mergeAll [] = []
mergeAll (x:xs) = merge x (mergeAll xs)
bests :: forall a. (Ord a, Poset a) => [[a]] -> [a]
bests = pareto' [] . mergeAll
pareto' :: Poset a => [a] -> [a] -> [a]
pareto' acc [] = Prelude.reverse acc
pareto' acc (x:xs) = if any (≺ x) acc
then pareto' acc xs
else pareto' (x:acc) xs
newtype Doc = MkDoc [(M,L)]
quasifilter :: (a -> Bool) -> [a] -> [a]
quasifilter p xs = let fxs = filter p xs in if null fxs then take 1 xs else fxs
instance Monoid Doc where
mempty = text ""
MkDoc xs `mappend` MkDoc ys = MkDoc $ bests [ quasifilter (fits . fst) [x <> y | y <- ys] | x <- xs]
instance Layout Doc where
flush (MkDoc xs) = MkDoc $ bests $ map sort $ groupBy ((==) `on` (height . fst)) $ (map flush xs)
text s = MkDoc [text s]
render (MkDoc []) = error "No suitable layout found."
render (MkDoc (x:_)) = render x
instance Document Doc where
MkDoc m1 <|> MkDoc m2 = MkDoc (bests [m1,m2])
instance (Layout a, Layout b) => Layout (a,b) where
text s = (text s, text s)
flush (a,b) = (flush a, flush b)
render = render . snd
instance (Document a, Document b) => Document (a,b) where
(a,b) <|> (c,d) = (a<|>c,b<|>d)
instance (Poset a) => Poset (a,b) where
(a,_) ≺ (b,_) = a ≺ b