Implement indentation in the encoder

This commit is contained in:
Eugen Wissner 2019-08-03 23:57:27 +02:00
parent 989e418cc2
commit 7a8a90aba8
2 changed files with 69 additions and 41 deletions

View File

@ -3,9 +3,11 @@
-- | This module defines a printer for the @GraphQL@ language. -- | This module defines a printer for the @GraphQL@ language.
module Language.GraphQL.Encoder module Language.GraphQL.Encoder
( Formatter(..) ( Formatter
, definition , definition
, document , document
, minified
, pretty
) where ) where
import Data.Foldable (fold) import Data.Foldable (fold)
@ -17,9 +19,19 @@ import Language.GraphQL.AST
-- | Instructs the encoder whether a GraphQL should be minified or pretty -- | Instructs the encoder whether a GraphQL should be minified or pretty
-- printed. -- printed.
--
-- Use 'pretty' and 'minified' to construct the formatter.
data Formatter data Formatter
= Minified = Minified
| Pretty Int | Pretty Word
-- Constructs a formatter for pretty printing.
pretty :: Formatter
pretty = Pretty 0
-- Constructs a formatter for minifying.
minified :: Formatter
minified = Minified
-- | Converts a 'Document' into a string. -- | Converts a 'Document' into a string.
document :: Formatter -> Document -> Text document :: Formatter -> Document -> Text
@ -81,17 +93,25 @@ variable :: Name -> Text
variable var = "$" <> var variable var = "$" <> var
selectionSet :: Formatter -> SelectionSet -> Text selectionSet :: Formatter -> SelectionSet -> Text
selectionSet formatter@(Pretty _) = bracesNewLines (selection formatter) . NonEmpty.toList selectionSet formatter
selectionSet Minified = bracesCommas (selection Minified) . NonEmpty.toList = bracesList formatter (selection formatter)
. NonEmpty.toList
selectionSetOpt :: Formatter -> SelectionSetOpt -> Text selectionSetOpt :: Formatter -> SelectionSetOpt -> Text
selectionSetOpt formatter@(Pretty _) = bracesNewLines $ selection formatter selectionSetOpt formatter = bracesList formatter $ selection formatter
selectionSetOpt Minified = bracesCommas $ selection Minified
selection :: Formatter -> Selection -> Text selection :: Formatter -> Selection -> Text
selection formatter (SelectionField x) = field formatter x selection formatter = Text.append indent . f
selection formatter (SelectionInlineFragment x) = inlineFragment formatter x where
selection formatter (SelectionFragmentSpread x) = fragmentSpread formatter x f (SelectionField x) = field incrementIndent x
f (SelectionInlineFragment x) = inlineFragment incrementIndent x
f (SelectionFragmentSpread x) = fragmentSpread incrementIndent x
incrementIndent
| Pretty n <- formatter = Pretty $ n + 1
| otherwise = Minified
indent
| Pretty n <- formatter = Text.replicate (fromIntegral $ n + 1) " "
| otherwise = mempty
field :: Formatter -> Field -> Text field :: Formatter -> Field -> Text
field formatter (Field alias name args dirs selso) field formatter (Field alias name args dirs selso)
@ -118,8 +138,8 @@ argument formatter (Argument name v)
-- * Fragments -- * Fragments
fragmentSpread :: Formatter -> FragmentSpread -> Text fragmentSpread :: Formatter -> FragmentSpread -> Text
fragmentSpread formatter (FragmentSpread name ds) = fragmentSpread formatter (FragmentSpread name ds)
"..." <> name <> optempty (directives formatter) ds = "..." <> name <> optempty (directives formatter) ds
inlineFragment :: Formatter -> InlineFragment -> Text inlineFragment :: Formatter -> InlineFragment -> Text
inlineFragment formatter (InlineFragment tc dirs sels) inlineFragment formatter (InlineFragment tc dirs sels)
@ -162,7 +182,13 @@ listValue :: Formatter -> [Value] -> Text
listValue formatter = bracketsCommas formatter $ value formatter listValue formatter = bracketsCommas formatter $ value formatter
objectValue :: Formatter -> [ObjectField] -> Text objectValue :: Formatter -> [ObjectField] -> Text
objectValue formatter = bracesCommas $ objectField formatter objectValue formatter = intercalate $ objectField formatter
where
intercalate f
= braces
. Text.intercalate (eitherFormat formatter ", " ",")
. fmap f
objectField :: Formatter -> ObjectField -> Text objectField :: Formatter -> ObjectField -> Text
objectField formatter (ObjectField name v) = name <> colon <> value formatter v objectField formatter (ObjectField name v) = name <> colon <> value formatter v
@ -227,15 +253,17 @@ bracketsCommas formatter f
. Text.intercalate (eitherFormat formatter ", " ",") . Text.intercalate (eitherFormat formatter ", " ",")
. fmap f . fmap f
bracesCommas :: forall a. (a -> Text) -> [a] -> Text bracesList :: forall a. Formatter -> (a -> Text) -> [a] -> Text
bracesCommas f = braces . Text.intercalate "," . fmap f bracesList (Pretty intendation) f xs
= Text.snoc (Text.intercalate "\n" content) '\n'
bracesNewLines :: forall a. (a -> Text) -> [a] -> Text <> (Text.snoc $ Text.replicate (fromIntegral intendation) " ") '}'
bracesNewLines f xs = Text.append (Text.intercalate "\n" $ "{" : fmap f xs) "\n}" where
content = "{" : fmap f xs
bracesList Minified f xs = braces $ Text.intercalate "," $ fmap f xs
optempty :: (Eq a, Monoid a, Monoid b) => (a -> b) -> a -> b optempty :: (Eq a, Monoid a, Monoid b) => (a -> b) -> a -> b
optempty f xs = if xs == mempty then mempty else f xs optempty f xs = if xs == mempty then mempty else f xs
eitherFormat :: forall a. Formatter -> a -> a -> a eitherFormat :: forall a. Formatter -> a -> a -> a
eitherFormat (Pretty _) pretty _ = pretty eitherFormat (Pretty _) x _ = x
eitherFormat Minified _ minified = minified eitherFormat Minified _ x = x

View File

@ -30,46 +30,46 @@ spec = describe "Kitchen Sink" $ do
either either
(expectationFailure . errorBundlePretty) (expectationFailure . errorBundlePretty)
(flip shouldBe expected . Encoder.document Encoder.Minified) (flip shouldBe expected . Encoder.document Encoder.minified)
$ parse Parser.document dataFileName actual $ parse Parser.document dataFileName actual
it "pretty prints the query" $ do it "pretty prints the query" $ do
dataFileName <- getDataFileName "tests/data/kitchen-sink.graphql" dataFileName <- getDataFileName "tests/data/kitchen-sink.graphql"
actual <- Text.IO.readFile dataFileName actual <- Text.IO.readFile dataFileName
let expected = [r|query queryName($foo: ComplexType, $site: Site = MOBILE) { let expected = [r|query queryName($foo: ComplexType, $site: Site = MOBILE) {
whoever123is: node(id: [123, 456]) { whoever123is: node(id: [123, 456]) {
id id
... on User @defer { ... on User @defer {
field2 { field2 {
id id
alias: field1(first: 10, after: $foo) @include(if: $foo) { alias: field1(first: 10, after: $foo) @include(if: $foo) {
id id
...frag ...frag
} }
} }
} }
} }
} }
mutation likeStory { mutation likeStory {
like(story: 123) @defer { like(story: 123) @defer {
story { story {
id id
} }
} }
} }
fragment frag on Friend { fragment frag on Friend {
foo(size: $size, bar: $b, obj: {key: "value"}) foo(size: $size, bar: $b, obj: {key: "value"})
} }
{ {
unnamed(truthy: true, falsey: false) unnamed(truthy: true, falsey: false)
query query
} }
|] |]
either either
(expectationFailure . errorBundlePretty) (expectationFailure . errorBundlePretty)
(flip shouldBe expected . Encoder.document (Encoder.Pretty 0)) (flip shouldBe expected . Encoder.document Encoder.pretty)
$ parse Parser.document dataFileName actual $ parse Parser.document dataFileName actual