{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}

{- |
Module      :  Text.HTML.QQ

Copyright   :  Dennis Gosnell 2017
License     :  BSD3

Maintainer  :  Dennis Gosnell (cdep.illabout@gmail.com)
Stability   :  experimental
Portability :  unknown

This module provides a quasi-quoter for HTML 'Document's.  See the 'html'
function for some examples.

See "Text.XML.QQ" for an explanation of the difference between "Text.HTML.QQ"
and "Text.XML.QQ".
-}

module Text.HTML.QQ
  ( html
  , htmlRaw
    -- * Types
  , Document
  ) where

import Data.Text.Lazy (pack)
import Language.Haskell.TH (appE)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Haskell.TH.Syntax (lift)
import Text.Blaze.Renderer.Text (renderMarkup)
import Text.Heterocephalus (compileFromString, textSetting)
import Text.HTML.DOM (parseLT)
import Text.XML (Document)

import Text.XMLHTML.Internal (createExpQuasiQuoter)

-- $setup
-- >>> :set -XQuasiQuotes
-- >>> :set -XTemplateHaskell


-- | This 'QuasiQuoter' produces HTML 'Document's.
--
-- This 'QuasiQuoter' produces expressions of type 'Document'.
--
-- Here's a simple example of using it:
--
-- >>> [html|<html></html>|] :: Document
-- Document {documentPrologue = Prologue {prologueBefore = [], prologueDoctype = Nothing, prologueAfter = []}, documentRoot = Element {elementName = Name {nameLocalName = "html", nameNamespace = Nothing, namePrefix = Nothing}, elementAttributes = fromList [], elementNodes = []}, documentEpilogue = []}
--
-- Internally, this function is using the
-- <https://hackage.haskell.org/package/heterocephalus heterocephalus> package.
-- This means you can use variable interpolation, as well as @forall@, @if@,
-- and @case@ control statements.  Checkout the
-- <https://github.com/arowM/heterocephalus#syntax heterocephalus README> for
-- more info.
--
-- >>> let a = "hello world"
-- >>> [html|<html>#{a}</html>|]
-- Document ...
--
-- Even invalid HTML will still parse.
--
-- >>> [html|<html </html>|]
-- Document ...
--
-- Here's an example of a template that can be parsed as an HTML 'Document', but
-- not as an XML 'Document':
--
-- >>> [html|<html><br></html>|]
-- Document ...
html :: QuasiQuoter
html :: QuasiQuoter
html =
  (String -> Q Exp) -> QuasiQuoter
createExpQuasiQuoter ((String -> Q Exp) -> QuasiQuoter)
-> (String -> Q Exp) -> QuasiQuoter
forall a b. (a -> b) -> a -> b
$ \String
string ->
    Q Exp -> Q Exp -> Q Exp
forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
appE [|parseLT . renderMarkup|] (Q Exp -> Q Exp) -> Q Exp -> Q Exp
forall a b. (a -> b) -> a -> b
$ HeterocephalusSetting -> String -> Q Exp
compileFromString HeterocephalusSetting
textSetting String
string

-- | This function is the same as 'html', but doesn't allow variable
-- interpolation or control statements.  It also produces expressions of type
-- 'Document'.
--
-- Here's a simple example of using it:
--
-- >>> [htmlRaw|<html></html>|] :: Document
-- Document ...
htmlRaw :: QuasiQuoter
htmlRaw :: QuasiQuoter
htmlRaw = (String -> Q Exp) -> QuasiQuoter
createExpQuasiQuoter ((String -> Q Exp) -> QuasiQuoter)
-> (String -> Q Exp) -> QuasiQuoter
forall a b. (a -> b) -> a -> b
$ Document -> Q Exp
forall t (m :: * -> *). (Lift t, Quote m) => t -> m Exp
lift (Document -> Q Exp) -> (String -> Document) -> String -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Document
parseLT (Text -> Document) -> (String -> Text) -> String -> Document
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
pack