-- | __Warning: internal module!__ This means that the API may change
-- arbitrarily between versions without notice. Depending on this module may
-- lead to unexpected breakages, so proceed with caution!
--
-- This module provides debugging helpers for inspecting 'Doc's.
--
-- Use the @pretty-simple@ package to get a nicer layout for 'show'n
-- 'Diag's:
--
-- > > Text.Pretty.Simple.pPrintNoColor . diag $ align (vcat ["foo", "bar"])
-- > Column
-- >    [
-- >        ( 10
-- >        , Nesting
-- >            [
-- >                ( 10
-- >                , Cat ( Text 3 "foo" )
-- >                    ( Cat ( FlatAlt Line Empty ) ( Text 3 "bar" ) )
-- >                )
-- >            ]
-- >        )
-- >    ]


module Prettyprinter.Internal.Debug where

import Data.Text (Text)
import Prettyprinter.Internal (PageWidth, Doc)
import qualified Prettyprinter.Internal as Doc

-- | A variant of 'Doc' for debugging.
--
-- Unlike in the 'Doc' type, the 'Column', 'WithPageWidth' and 'Nesting'
-- constructors don't contain functions but are \"sampled\" to allow
-- simple inspection with 'show'.
data Diag ann =
    Fail
    | Empty
    | Char !Char
    | Text !Int !Text
    | Line
    | FlatAlt (Diag ann) (Diag ann)
    | Cat (Diag ann) (Diag ann)
    | Nest !Int (Diag ann)
    | Union (Diag ann) (Diag ann)
    | Column [(Int, Diag ann)]
      -- ^ 'Doc': @(Int -> Diag ann)@
    | WithPageWidth [(PageWidth, Diag ann)]
      -- ^ 'Doc': @(PageWidth -> Diag ann)@
    | Nesting [(Int, Diag ann)]
      -- ^ 'Doc': @(Int -> Diag ann)@
    | Annotated ann (Diag ann)
    deriving Int -> Diag ann -> ShowS
[Diag ann] -> ShowS
Diag ann -> String
(Int -> Diag ann -> ShowS)
-> (Diag ann -> String) -> ([Diag ann] -> ShowS) -> Show (Diag ann)
forall ann. Show ann => Int -> Diag ann -> ShowS
forall ann. Show ann => [Diag ann] -> ShowS
forall ann. Show ann => Diag ann -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall ann. Show ann => Int -> Diag ann -> ShowS
showsPrec :: Int -> Diag ann -> ShowS
$cshow :: forall ann. Show ann => Diag ann -> String
show :: Diag ann -> String
$cshowList :: forall ann. Show ann => [Diag ann] -> ShowS
showList :: [Diag ann] -> ShowS
Show

-- | Convert a 'Doc' to its diagnostic representation.
--
-- The functions in the 'Column', 'WithPageWidth' and 'Nesting' constructors are
-- sampled with some default values.
--
-- Use `diag'` to control the function inputs yourself.
--
-- >>> diag $ Doc.align (Doc.vcat ["foo", "bar"])
-- Column [(10,Nesting [(10,Cat (Text 3 "foo") (Cat (FlatAlt Line Empty) (Text 3 "bar")))])]
diag :: Doc ann -> Diag ann
diag :: forall ann. Doc ann -> Diag ann
diag = [Int] -> [PageWidth] -> [Int] -> Doc ann -> Diag ann
forall ann. [Int] -> [PageWidth] -> [Int] -> Doc ann -> Diag ann
diag' [Int
10] [PageWidth
Doc.defaultPageWidth] [Int
10]

diag'
    :: [Int]
       -- ^ Cursor positions for the 'Column' constructor
    -> [PageWidth]
       -- ^ For 'WithPageWidth'
    -> [Int]
       -- ^ Nesting levels for 'Nesting'
    -> Doc ann
    -> Diag ann
diag' :: forall ann. [Int] -> [PageWidth] -> [Int] -> Doc ann -> Diag ann
diag' [Int]
columns [PageWidth]
pageWidths [Int]
nestings = Doc ann -> Diag ann
forall ann. Doc ann -> Diag ann
go
  where
    go :: Doc ann -> Diag ann
go Doc ann
doc = case Doc ann
doc of
        Doc ann
Doc.Fail -> Diag ann
forall ann. Diag ann
Fail
        Doc ann
Doc.Empty -> Diag ann
forall ann. Diag ann
Empty
        Doc.Char Char
c -> Char -> Diag ann
forall ann. Char -> Diag ann
Char Char
c
        Doc.Text Int
l Text
t -> Int -> Text -> Diag ann
forall ann. Int -> Text -> Diag ann
Text Int
l Text
t
        Doc ann
Doc.Line -> Diag ann
forall ann. Diag ann
Line
        Doc.FlatAlt Doc ann
a Doc ann
b -> Diag ann -> Diag ann -> Diag ann
forall ann. Diag ann -> Diag ann -> Diag ann
FlatAlt (Doc ann -> Diag ann
go Doc ann
a) (Doc ann -> Diag ann
go Doc ann
b)
        Doc.Cat Doc ann
a Doc ann
b -> Diag ann -> Diag ann -> Diag ann
forall ann. Diag ann -> Diag ann -> Diag ann
Cat (Doc ann -> Diag ann
go Doc ann
a) (Doc ann -> Diag ann
go Doc ann
b)
        Doc.Nest Int
i Doc ann
d -> Int -> Diag ann -> Diag ann
forall ann. Int -> Diag ann -> Diag ann
Nest Int
i (Doc ann -> Diag ann
go Doc ann
d)
        Doc.Union Doc ann
a Doc ann
b -> Diag ann -> Diag ann -> Diag ann
forall ann. Diag ann -> Diag ann -> Diag ann
Union (Doc ann -> Diag ann
go Doc ann
a) (Doc ann -> Diag ann
go Doc ann
b)
        Doc.Column Int -> Doc ann
f -> [(Int, Diag ann)] -> Diag ann
forall ann. [(Int, Diag ann)] -> Diag ann
Column ((Int -> Doc ann) -> [Int] -> [(Int, Diag ann)]
forall a ann. (a -> Doc ann) -> [a] -> [(a, Diag ann)]
apply Int -> Doc ann
f [Int]
columns)
        Doc.WithPageWidth PageWidth -> Doc ann
f -> [(PageWidth, Diag ann)] -> Diag ann
forall ann. [(PageWidth, Diag ann)] -> Diag ann
WithPageWidth ((PageWidth -> Doc ann) -> [PageWidth] -> [(PageWidth, Diag ann)]
forall a ann. (a -> Doc ann) -> [a] -> [(a, Diag ann)]
apply PageWidth -> Doc ann
f [PageWidth]
pageWidths)
        Doc.Nesting Int -> Doc ann
f -> [(Int, Diag ann)] -> Diag ann
forall ann. [(Int, Diag ann)] -> Diag ann
Nesting ((Int -> Doc ann) -> [Int] -> [(Int, Diag ann)]
forall a ann. (a -> Doc ann) -> [a] -> [(a, Diag ann)]
apply Int -> Doc ann
f [Int]
nestings)
        Doc.Annotated ann
ann Doc ann
d -> ann -> Diag ann -> Diag ann
forall ann. ann -> Diag ann -> Diag ann
Annotated ann
ann (Doc ann -> Diag ann
go Doc ann
d)

    apply :: (a -> Doc ann) -> [a] -> [(a, Diag ann)]
    apply :: forall a ann. (a -> Doc ann) -> [a] -> [(a, Diag ann)]
apply a -> Doc ann
f = (a -> (a, Diag ann)) -> [a] -> [(a, Diag ann)]
forall a b. (a -> b) -> [a] -> [b]
map (\a
x -> (a
x, Doc ann -> Diag ann
forall ann. Doc ann -> Diag ann
go (a -> Doc ann
f a
x)))