|
@@ -19,6 +19,7 @@ module Data.SCargot.Print
|
|
|
, flatPrint
|
|
|
) where
|
|
|
|
|
|
+import Control.Applicative
|
|
|
import Data.Monoid ((<>))
|
|
|
import Data.Text (Text)
|
|
|
import qualified Data.Text as T
|
|
@@ -180,10 +181,10 @@ indentSubsequent n (t:ts) = joinLines (t : go ts)
|
|
|
prettyPrintSExpr :: SExprPrinter a (SExpr a) -> SExpr a -> Text
|
|
|
prettyPrintSExpr pr@SExprPrinter { .. } expr = case maxWidth of
|
|
|
Nothing -> flatPrintSExpr (fmap atomPrinter (fromCarrier expr))
|
|
|
- Just _ -> indentPrintSExpr pr expr
|
|
|
+ Just w -> indentPrintSExpr2 pr w expr
|
|
|
|
|
|
-indentPrintSExpr :: SExprPrinter a (SExpr a) -> SExpr a -> Text
|
|
|
-indentPrintSExpr SExprPrinter { .. } = pHead 0
|
|
|
+indentPrintSExpr :: SExprPrinter a (SExpr a) -> Int -> SExpr a -> Text
|
|
|
+indentPrintSExpr SExprPrinter { .. } _ = pHead 0
|
|
|
where
|
|
|
pHead _ SNil = "()"
|
|
|
pHead _ (SAtom a) = atomPrinter a
|
|
@@ -218,44 +219,205 @@ indentPrintSExpr SExprPrinter { .. } = pHead 0
|
|
|
, T.length flat + ind > maxAmt = " " <> indented
|
|
|
| otherwise = " " <> flat
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+type IndentSpec = Int
|
|
|
+type Indenting = Maybe IndentSpec
|
|
|
+
|
|
|
+data PPS = PPS { indentWc :: IndentSpec
|
|
|
+ , remWidth :: Int
|
|
|
+ , numClose :: Int
|
|
|
+ }
|
|
|
+ deriving Show
|
|
|
+
|
|
|
+data SElem = SText Int T.Text
|
|
|
+ | SPair Int SElem SElem
|
|
|
+ | SDecl Int SElem [SElem]
|
|
|
+ | SJoin Int [SElem]
|
|
|
+ deriving (Show, Eq)
|
|
|
+
|
|
|
+sElemSize :: SElem -> Int
|
|
|
+sElemSize (SText n _) = n
|
|
|
+sElemSize (SPair n _ _) = n
|
|
|
+sElemSize (SDecl n _ _) = n
|
|
|
+sElemSize (SJoin n _) = n
|
|
|
+
|
|
|
+indentPrintSExpr2 :: SExprPrinter a (SExpr a) -> Int -> SExpr a -> Text
|
|
|
+indentPrintSExpr2 SExprPrinter { .. } maxW sexpr =
|
|
|
+ let atomTextTree = selems sexpr
|
|
|
+ pretty = fmap addIndent $ fst $ pHead (PPS 0 maxW 0) atomTextTree
|
|
|
+
|
|
|
+ in T.unlines pretty
|
|
|
+ where
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ selems SNil = SText 2 "()"
|
|
|
+ selems (SAtom a) = let p = atomPrinter a in SText (T.length p) p
|
|
|
+ selems (SCons l r) =
|
|
|
+ let l' = selems l
|
|
|
+ lsz = sElemSize l'
|
|
|
+ r' = selems r
|
|
|
+ rsz = sElemSize r'
|
|
|
+ bsz = lsz + rsz
|
|
|
+ in case r of
|
|
|
+ SNil -> SJoin lsz [l']
|
|
|
+ SAtom _ -> SPair bsz l' r'
|
|
|
+ _ -> case l of
|
|
|
+ SAtom _ -> case r' of
|
|
|
+ SJoin _ rl' -> SDecl bsz l' rl'
|
|
|
+ SDecl _ d dl -> SDecl bsz l' (d:dl)
|
|
|
+ _ -> SDecl bsz l' [r']
|
|
|
+ _ -> SJoin bsz $ prefixOnto l' r'
|
|
|
+
|
|
|
+ prefixOnto e (SJoin _ l) = e:l
|
|
|
+ prefixOnto e (SDecl _ l r) = e:l:r
|
|
|
+ prefixOnto e r = [e,r]
|
|
|
+
|
|
|
+ addIndent (Nothing, t) = t
|
|
|
+ addIndent (Just n, t) = indent n t
|
|
|
+
|
|
|
+ nextIndent = incrIndent indentAmount
|
|
|
+ incrIndent v n = n + v
|
|
|
+
|
|
|
+ pHead :: PPS -> SElem -> ( [(Indenting, Text)], PPS )
|
|
|
+ pHead pps (SText _ t) = ( [(Nothing, t)]
|
|
|
+ , pps { remWidth = remWidth pps - T.length t})
|
|
|
+ pHead pps (SPair _ e1 e2) =
|
|
|
+ let (t1,pps1) = pHead pps e1
|
|
|
+ (t2,pps2) = pTail ppsNextLine e2
|
|
|
+ (t3,pps3) = pTail ppsSameLine e2
|
|
|
+ ppsNextLine = pps { remWidth = remWidth pps - T.length sep }
|
|
|
+ ppsSameLine = pps1 { remWidth = remWidth pps1 - T.length sep }
|
|
|
+ sep = " . "
|
|
|
+ t1h = head t1
|
|
|
+ wrapJoin i l rs = wrapT i (snd l <> sep) rs
|
|
|
+ sameLine l r p = (wrapJoin (indentWc pps) l r, p)
|
|
|
+ separateLines l r p = (wrapTWith False "(" "" (indentWc pps) "" l ++
|
|
|
+ wrapTWith True sep ")" (indentWc pps) "" r, p)
|
|
|
+ in if length t1 > 1 || remWidth pps3 < numClose pps + 5
|
|
|
+ then separateLines t1 t2 pps2
|
|
|
+ else sameLine t1h t3 pps3
|
|
|
+
|
|
|
+
|
|
|
+ pHead pps (SJoin _ []) = ( [], pps )
|
|
|
+ pHead pps (SJoin els others) =
|
|
|
+ let (t1,_) = pHead pps $ head others
|
|
|
+ (t3,pps3) = foldl pTail' ([], pps) others
|
|
|
+ pTail' :: ([(Indenting, Text)], PPS)
|
|
|
+ -> SElem
|
|
|
+ -> ([(Indenting, Text)], PPS)
|
|
|
+ pTail' (rl,pp) ne = let (rt,pr) = pTail pp ne
|
|
|
+ hrl = head rl
|
|
|
+ hrt = head rt
|
|
|
+ in if length rt == 1
|
|
|
+ then case length rl of
|
|
|
+ 0 -> (rt, pr)
|
|
|
+ 1 -> ((fst hrl, snd hrl <> " " <> snd hrt):[], pr)
|
|
|
+ _ -> (rl <> rt, pr)
|
|
|
+ else (rl <> rt, pr)
|
|
|
+ sameLine parts pEnd = (wrapT (indentWc ppsSame) "" parts, pEnd)
|
|
|
+ ppsNext = pps { indentWc = nextIndent (indentWc pps)
|
|
|
+ , remWidth = remWidth pps - indentAmount
|
|
|
+ }
|
|
|
+ ppsSame = pps { indentWc = nextIndent (indentWc pps)
|
|
|
+ , remWidth = remWidth pps - indentAmount
|
|
|
+ }
|
|
|
+ ppsMulti = pps { indentWc = nextIndent (indentWc pps)
|
|
|
+ , remWidth = remWidth pps - indentAmount
|
|
|
+ }
|
|
|
+ pps3' = pps3
|
|
|
+ separateLines elems pEnd =
|
|
|
+ let lr = concatMap (fst . pTail pEnd) elems
|
|
|
+ in (wrapTWith False "(" ")" (indentWc ppsNext) "" lr, pEnd)
|
|
|
+ in if els > remWidth pps3 || length t1 > 1 || remWidth pps3 < numClose pps + 5
|
|
|
+ then separateLines others ppsMulti
|
|
|
+ else sameLine t3 pps3'
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ pHead pps (SDecl els first others) =
|
|
|
+ let (t1,pps1) = pHead pp2 first
|
|
|
+ (to1,_) = pTail pps1 (head others)
|
|
|
+ firstPlusFits = sElemSize first + sElemSize (head others) < (remWidth pps - 4)
|
|
|
+ allFits = els < (remWidth pps - length others - 3)
|
|
|
+ tryFirstArgSameLine = case swingIndent (SCons SNil (SCons SNil SNil)) of
|
|
|
+ Align -> True
|
|
|
+ _ -> False
|
|
|
+ pp2 = pps { indentWc = nextIndent (indentWc pps)
|
|
|
+ , remWidth = remWidth pps - 1 - indentAmount
|
|
|
+ , numClose = numClose pps + 1
|
|
|
+ }
|
|
|
+ pp2next = pp2
|
|
|
+ pp2solo = pp2
|
|
|
+ t1h = head t1
|
|
|
+ pps1' = pps1 { indentWc = incrIndent (T.length (snd t1h) + 1)
|
|
|
+ (indentWc pps1)
|
|
|
+ , remWidth = remWidth pps1 - T.length (snd t1h) - 1
|
|
|
+ }
|
|
|
+ tothers = concatMap (fst . pTail pp2next) others
|
|
|
+ tothers' = concatMap (fst . pTail pps1') $ tail others
|
|
|
+ (others', ppone) = foldl foldPTail ([],pps1) others
|
|
|
+ (others'', ppone') = foldl foldPTail ([],pps1') $ tail others
|
|
|
+ foldPTail (tf,ppf) o = let (ot,opp) = pTail ppf o
|
|
|
+ tf1 = head tf
|
|
|
+ tr = if length ot == 1
|
|
|
+ then case length tf of
|
|
|
+ 0 -> ot
|
|
|
+ 1 -> [(fst tf1, snd tf1 <> " " <> snd (head ot))]
|
|
|
+ _ -> tf ++ ot
|
|
|
+ else tf ++ ot
|
|
|
+ in (tr, opp)
|
|
|
+ separateLines l r p =
|
|
|
+ let wr = if null r then []
|
|
|
+ else wrapTWith True "" ")" (indentWc p) "" r
|
|
|
+ cl = if null r then ")" else ""
|
|
|
+ in (wrapTWith False "(" cl (indentWc pps) "" l <> wr, pp2)
|
|
|
+ maybeSameLine l (r1,p1) (rn,p2) =
|
|
|
+ if length r1 <= 1 && remWidth p1 > numClose p1
|
|
|
+ then (wrapT (indentWc pps) (snd l <> " ") r1, p1)
|
|
|
+ else separateLines [l] rn p2
|
|
|
+ in if allFits && length t1 < 2
|
|
|
+ then maybeSameLine t1h (others',ppone) (tothers,pp2solo)
|
|
|
+ else if (tryFirstArgSameLine && firstPlusFits &&
|
|
|
+ length t1 < 2 &&
|
|
|
+ length to1 < 2 &&
|
|
|
+ not (null to1) && not (null others))
|
|
|
+ then maybeSameLine (fst t1h,
|
|
|
+ snd t1h <> " " <> snd (head to1)) (others'',ppone') (tothers',pps1')
|
|
|
+ else separateLines t1 tothers pp2
|
|
|
+
|
|
|
+
|
|
|
+ pTail = pHead
|
|
|
+
|
|
|
+
|
|
|
+wrapTWith :: Bool -> T.Text -> T.Text -> IndentSpec
|
|
|
+ -> T.Text
|
|
|
+ -> [(Indenting, T.Text)]
|
|
|
+ -> [(Indenting, T.Text)]
|
|
|
+wrapTWith isContinued st en ind hstart ts =
|
|
|
+ let th = head ts
|
|
|
+ tt = last ts
|
|
|
+ tb = init $ tail ts
|
|
|
+ tp l = (fst l <|> Just ind, snd l)
|
|
|
+ fi = if isContinued then Just ind else Nothing
|
|
|
+ in if length ts > 1
|
|
|
+ then (((fi, st <> hstart <> snd th) : map tp tb) ++
|
|
|
+ [ tp $ (fst tt, snd tt <> en) ])
|
|
|
+ else [(fi, st <> hstart <> snd th <> en)]
|
|
|
+
|
|
|
+wrapT :: IndentSpec -> T.Text -> [(Indenting, T.Text)] -> [(Indenting, T.Text)]
|
|
|
+wrapT = wrapTWith False "(" ")"
|
|
|
+
|
|
|
|
|
|
|
|
|
|