{-# LANGUAGE OverloadedStrings #-}

module Prettyprinter.Custom ( brackets'
                                        , braces'
                                        , parens'
                                        , sexp
                                        ) where

import Prettyprinter

-- | An area bracketed by two delimiters. When on multiple lines the delimiters are not indented but the content is.
section' :: Doc a -> Doc a -> Doc a -> Doc a
-- The subtlety here is that the nest call surrounds the first delimiter and the content, but not
-- the final one. This is because of how nest behaves, where it doesn't indent until it hits the first
-- line break. So we need to include the first delimiter so that the main content gets indented, but not
-- the final delimiter.
section' :: Doc a -> Doc a -> Doc a -> Doc a
section' Doc a
c1 Doc a
c2 Doc a
d = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
group (Doc a -> Doc a) -> Doc a -> Doc a
forall a b. (a -> b) -> a -> b
$ Int -> Doc a -> Doc a
forall ann. Int -> Doc ann -> Doc ann
nest Int
2 (Doc a
c1 Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
d) Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
c2

-- | An area bracketed by two delimiters. When on one line, there are spaces between the delimiters
-- and the content, when on multiple lines the delimiters are not indented but the content is.
section :: Doc a -> Doc a -> Doc a -> Doc a
section :: Doc a -> Doc a -> Doc a -> Doc a
section Doc a
c1 Doc a
c2 = Doc a -> Doc a -> Doc a -> Doc a
forall a. Doc a -> Doc a -> Doc a -> Doc a
section' (Doc a
c1 Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
forall ann. Doc ann
line) (Doc a
forall ann. Doc ann
line Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
c2)

-- | This prints a document enclosed by brackets, possibly indenting the output on
-- a new line if it does not fit.
brackets' :: Doc a -> Doc a
brackets' :: Doc a -> Doc a
brackets' = Doc a -> Doc a -> Doc a -> Doc a
forall a. Doc a -> Doc a -> Doc a -> Doc a
section Doc a
"[" Doc a
"]"

-- | This prints a document enclosed by braces, possibly indenting the output on
-- a new line if it does not fit.
braces' :: Doc a -> Doc a
braces' :: Doc a -> Doc a
braces' = Doc a -> Doc a -> Doc a -> Doc a
forall a. Doc a -> Doc a -> Doc a -> Doc a
section Doc a
"{" Doc a
"}"

-- | This prints a document enclosed by parentheses, aligning the opening and
-- closing parentheses.
parens' :: Doc a -> Doc a
parens' :: Doc a -> Doc a
parens' = Doc a -> Doc a -> Doc a -> Doc a
forall a. Doc a -> Doc a -> Doc a -> Doc a
section Doc a
"(" Doc a
")"

-- | Print a "sexp", i.e. something like "(keyword arg1 ... argN)".
sexp :: Doc a -> [Doc a] -> Doc a
sexp :: Doc a -> [Doc a] -> Doc a
sexp Doc a
a [Doc a]
es =
    -- This is a bit funny, because we want the keyword to "stick" to the opening parenthesis
    -- when it's split over multiple lines. So we include it in the "initial" segment. But then
    -- we also have to have a space after that rather than no space. So we start with "(keyword"
    -- and a line-or-space, but end with a line-or-nothing and ")".
    Doc a -> Doc a -> Doc a -> Doc a
forall a. Doc a -> Doc a -> Doc a -> Doc a
section' (Doc a
"(" Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
a Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
forall ann. Doc ann
line) (Doc a
forall ann. Doc ann
line' Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
")") ([Doc a] -> Doc a
forall ann. [Doc ann] -> Doc ann
sep [Doc a]
es)