|
@@ -17,16 +17,21 @@ module Data.SCargot.Print
|
|
|
|
|
|
, basicPrint
|
|
|
, flatPrint
|
|
|
+ , unboundIndentPrint
|
|
|
) where
|
|
|
|
|
|
+import qualified Data.Foldable as F
|
|
|
import Data.Monoid ((<>))
|
|
|
+import qualified Data.Sequence as Seq
|
|
|
import Data.Text (Text)
|
|
|
import qualified Data.Text as T
|
|
|
import qualified Data.Text.Lazy as TL
|
|
|
import qualified Data.Text.Lazy.Builder as B
|
|
|
+import qualified Data.Traversable as T
|
|
|
|
|
|
import Data.SCargot.Repr
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
data Indent
|
|
@@ -54,6 +59,7 @@ data Indent
|
|
|
|
|
|
deriving (Eq, Show)
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -69,12 +75,16 @@ data SExprPrinter atom carrier = SExprPrinter
|
|
|
, indentAmount :: Int
|
|
|
|
|
|
, maxWidth :: Maybe Int
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ , indentPrint :: Bool
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
|
|
|
flatPrint :: (atom -> Text) -> SExprPrinter atom (SExpr atom)
|
|
|
flatPrint printer = SExprPrinter
|
|
@@ -83,9 +93,10 @@ flatPrint printer = SExprPrinter
|
|
|
, swingIndent = const Swing
|
|
|
, indentAmount = 2
|
|
|
, maxWidth = Nothing
|
|
|
+ , indentPrint = False
|
|
|
}
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
basicPrint :: (atom -> Text) -> SExprPrinter atom (SExpr atom)
|
|
@@ -95,8 +106,140 @@ basicPrint printer = SExprPrinter
|
|
|
, swingIndent = const Swing
|
|
|
, indentAmount = 2
|
|
|
, maxWidth = Just 80
|
|
|
+ , indentPrint = True
|
|
|
+ }
|
|
|
+
|
|
|
+unboundIndentPrint :: (atom -> Text) -> SExprPrinter atom (SExpr atom)
|
|
|
+unboundIndentPrint printer = SExprPrinter
|
|
|
+ { atomPrinter = printer
|
|
|
+ , fromCarrier = id
|
|
|
+ , swingIndent = const Swing
|
|
|
+ , indentAmount = 2
|
|
|
+ , maxWidth = Nothing
|
|
|
+ , indentPrint = True
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+data Intermediate
|
|
|
+ = IAtom Text
|
|
|
+ | IList Indent (Seq.Seq Intermediate) (Maybe Text)
|
|
|
+ | IEmpty
|
|
|
+
|
|
|
+
|
|
|
+toIntermediate :: SExprPrinter a (SExpr a) -> SExpr a -> Intermediate
|
|
|
+toIntermediate
|
|
|
+ SExprPrinter { atomPrinter = printAtom
|
|
|
+ , swingIndent = swing
|
|
|
+ } = headOf
|
|
|
+ where
|
|
|
+ headOf (SAtom a) = IAtom (printAtom a)
|
|
|
+ headOf SNil = IEmpty
|
|
|
+ headOf (SCons x xs) =
|
|
|
+ gather (swing x) (Seq.singleton (headOf x)) xs
|
|
|
+ gather sw rs SNil =
|
|
|
+ IList sw rs Nothing
|
|
|
+ gather sw rs (SAtom a) =
|
|
|
+ IList sw rs (Just (printAtom a))
|
|
|
+ gather sw rs (SCons x xs) =
|
|
|
+ gather sw (rs Seq.|> headOf x) xs
|
|
|
+
|
|
|
+
|
|
|
+unboundIndentPrintSExpr :: SExprPrinter a (SExpr a) -> SExpr a -> TL.Text
|
|
|
+unboundIndentPrintSExpr spec = finalize . go . toIntermediate spec
|
|
|
+ where
|
|
|
+ finalize = B.toLazyText . F.foldMap (<> B.fromString "\n")
|
|
|
+
|
|
|
+ go :: Intermediate -> Seq.Seq B.Builder
|
|
|
+ go (IAtom t) = Seq.singleton (B.fromText t)
|
|
|
+ go IEmpty = Seq.singleton (B.fromString "()")
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ go (IList iv values rest)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ | Just strings <- T.traverse ppBasic values =
|
|
|
+ Seq.singleton (B.fromString "(" <> buildUnwords strings <> pTail rest)
|
|
|
+
|
|
|
+
|
|
|
+ | Swing <- iv =
|
|
|
+
|
|
|
+
|
|
|
+ let x Seq.:< xs = Seq.viewl values
|
|
|
+ butLast = insertParen (go x) <> fmap doIndent (F.foldMap go xs)
|
|
|
+ in handleTail rest butLast
|
|
|
+
|
|
|
+
|
|
|
+ | SwingAfter n <- iv =
|
|
|
+ let (hs, xs) = Seq.splitAt n values
|
|
|
+ hd = B.fromString "(" <> buildUnwords (F.foldMap go hs)
|
|
|
+ butLast = hd Seq.<| fmap doIndent (F.foldMap go xs)
|
|
|
+ in handleTail rest butLast
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ | otherwise =
|
|
|
+ let x Seq.:< xs = Seq.viewl values
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ len = 2 + F.maximum (fmap (TL.length . B.toLazyText) (go x))
|
|
|
+ in case Seq.viewl xs of
|
|
|
+
|
|
|
+
|
|
|
+ Seq.EmptyL -> insertParen (insertCloseParen (go x))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ y Seq.:< ys ->
|
|
|
+ let hd = B.fromString "(" <> buildUnwords (F.foldMap go (Seq.fromList [x, y]))
|
|
|
+ butLast = hd Seq.<| fmap (doIndentOf (fromIntegral len)) (F.foldMap go ys)
|
|
|
+ in handleTail rest butLast
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ doIndent :: B.Builder -> B.Builder
|
|
|
+ doIndent = doIndentOf (indentAmount spec)
|
|
|
+
|
|
|
+ doIndentOf :: Int -> B.Builder -> B.Builder
|
|
|
+ doIndentOf n b = B.fromText (T.replicate n " ") <> b
|
|
|
+
|
|
|
+ insertParen :: Seq.Seq B.Builder -> Seq.Seq B.Builder
|
|
|
+ insertParen s = case Seq.viewl s of
|
|
|
+ Seq.EmptyL -> s
|
|
|
+ x Seq.:< xs -> (B.fromString "(" <> x) Seq.<| xs
|
|
|
+
|
|
|
+ handleTail :: Maybe Text -> Seq.Seq B.Builder -> Seq.Seq B.Builder
|
|
|
+ handleTail Nothing = insertCloseParen
|
|
|
+ handleTail (Just t) =
|
|
|
+ (Seq.|> (B.fromString "." <> B.fromText t <> B.fromString ")"))
|
|
|
+
|
|
|
+ insertCloseParen :: Seq.Seq B.Builder -> Seq.Seq B.Builder
|
|
|
+ insertCloseParen s = case Seq.viewr s of
|
|
|
+ Seq.EmptyR -> Seq.singleton (B.fromString ")")
|
|
|
+ xs Seq.:> x -> xs Seq.|> (x <> B.fromString ")")
|
|
|
+
|
|
|
+ buildUnwords sq =
|
|
|
+ case Seq.viewl sq of
|
|
|
+ Seq.EmptyL -> mempty
|
|
|
+ t Seq.:< ts -> t <> F.foldMap (\ x -> B.fromString " " <> x) ts
|
|
|
+
|
|
|
+ pTail Nothing = B.fromString ")"
|
|
|
+ pTail (Just t) = B.fromString ". " <> B.fromText t <> B.fromString ")"
|
|
|
+
|
|
|
+ ppBasic (IAtom t) = Just (B.fromText t)
|
|
|
+ ppBasic (IEmpty) = Just (B.fromString "()")
|
|
|
+ ppBasic _ = Nothing
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -108,6 +251,7 @@ basicPrint printer = SExprPrinter
|
|
|
setFromCarrier :: (c -> b) -> SExprPrinter a b -> SExprPrinter a c
|
|
|
setFromCarrier fc pr = pr { fromCarrier = fromCarrier pr . fc }
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -116,6 +260,7 @@ setFromCarrier fc pr = pr { fromCarrier = fromCarrier pr . fc }
|
|
|
setMaxWidth :: Int -> SExprPrinter atom carrier -> SExprPrinter atom carrier
|
|
|
setMaxWidth n pr = pr { maxWidth = Just n }
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -125,6 +270,7 @@ setMaxWidth n pr = pr { maxWidth = Just n }
|
|
|
removeMaxWidth :: SExprPrinter atom carrier -> SExprPrinter atom carrier
|
|
|
removeMaxWidth pr = pr { maxWidth = Nothing }
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -136,6 +282,7 @@ removeMaxWidth pr = pr { maxWidth = Nothing }
|
|
|
setIndentAmount :: Int -> SExprPrinter atom carrier -> SExprPrinter atom carrier
|
|
|
setIndentAmount n pr = pr { indentAmount = n }
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -149,19 +296,23 @@ setIndentAmount n pr = pr { indentAmount = n }
|
|
|
setIndentStrategy :: (SExpr atom -> Indent) -> SExprPrinter atom carrier -> SExprPrinter atom carrier
|
|
|
setIndentStrategy st pr = pr { swingIndent = st }
|
|
|
|
|
|
+
|
|
|
|
|
|
joinLines :: [Text] -> Text
|
|
|
joinLines = T.intercalate "\n"
|
|
|
|
|
|
+
|
|
|
|
|
|
indent :: Int -> Text -> Text
|
|
|
indent n ts = T.replicate n " " <> ts
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
indentAll :: Int -> [Text] -> Text
|
|
|
indentAll n = ("\n" <>) . joinLines . map (indent n)
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
indentSubsequent :: Int -> [Text] -> Text
|
|
@@ -170,6 +321,7 @@ indentSubsequent _ [t] = t
|
|
|
indentSubsequent n (t:ts) = joinLines (t : go ts)
|
|
|
where go = map (indent n)
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -179,9 +331,12 @@ 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))
|
|
|
+ Nothing
|
|
|
+ | indentPrint -> TL.toStrict (unboundIndentPrintSExpr pr (fromCarrier expr))
|
|
|
+ | otherwise -> flatPrintSExpr (fmap atomPrinter (fromCarrier expr))
|
|
|
Just _ -> indentPrintSExpr pr expr
|
|
|
|
|
|
+
|
|
|
indentPrintSExpr :: SExprPrinter a (SExpr a) -> SExpr a -> Text
|
|
|
indentPrintSExpr SExprPrinter { .. } = pHead 0
|
|
|
where
|