@@ -1,93 +1,96 @@
1−{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
1+{-# LANGUAGE TemplateHaskell #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE FlexibleInstances #-}
2{-# OPTIONS_GHC -fno-warn-missing-fields #-}
5{-# OPTIONS_GHC -fno-warn-missing-fields #-}
7+-- | This module contains Shakespearean (see "Text.Shakespeare") templates for Elm.
8+-- It introduces type-safe compile-time variable and URL interpolation. A typeclass
9+-- 'ToElm' is provided for interpolated variables.
11+-- Further reading on Shakespearean templates: <http://www.yesodweb.com/book/templates>
13+-- Further reading on Elm: <http://elm-lang.org>
4−module Language.Elm.Quasi (elm, elmFile) where
14+module Language.Elm.Quasi
16+ -- ** Template-Reading Functions
17+ -- | These QuasiQuoter and Template Haskell methods return values of
18+ -- type @'ElmUrl' url@. See the Yesod book for details.
27+ -- * Typeclass for interpolated variables
30+ -- ** Rendering Functions
6−import Language.Haskell.TH.Quote
36+import Language.Haskell.TH.Quote (QuasiQuoter (..))
7−import Language.Haskell.TH
8−import Language.Haskell.Meta (parseExp)
37+import Language.Haskell.TH.Syntax
9−
38+import Data.Text.Lazy.Builder (Builder, fromText, toLazyText, fromLazyText)
10−import Data.Attoparsec.Text
39+import Data.Monoid
11−import Data.Text as T (Text, pack, unpack, strip)
40+import qualified Data.Text as TS
12−
41+import qualified Data.Text.Lazy as TL
13−{- | This QuasiQuoter allows for literal embedding of elm source code within
42+import Text.Shakespeare
14− Haskell files. It is not responsible for the actual compilation Process.
43+
15− Usage:
44+-- | Render Elm to lazy Text without route interpolation.
16−
45+renderElm :: Elm -> TL.Text
17− @
46+renderElm (Elm b) = toLazyText b
19− main = image \"someImage.jpg\"
48+-- | render with route interpolation. If using this module standalone, apart
20− |]
49+-- from type-safe routes, a dummy renderer can be used:
22−
51+-- > renderElmUrl (\_ _ -> undefined) elmUrl
23− It also allows for simple variable interpolation using the ^{...} syntax.
52+--
24− This is intended for e.g. embedding pre-rendered type-safe URLs.
53+-- When using Yesod, a renderer is generated for you, which can be accessed
25− All interpolated variables must be a member of the Show typeclass. This
54+-- within the GHandler monad: 'Yesod.Handler.getUrlRenderParams'.
26− is not tested with more complicated expressions, so it is recommended to
55+renderElmUrl :: (url -> [(TS.Text, TS.Text)] -> TS.Text) -> ElmUrl url -> TL.Text
27− restrict usage of interpolation to pre-calculated variables.
56+renderElmUrl r s = renderElm $ s r
29− Example:
58+-- | Newtype wrapper of 'Builder'.
30−
59+newtype Elm = Elm { unElm :: Builder }
31− > let google = "http://google.com"
60+ deriving Monoid
33− > main = text $ link ^{google} (toText "Google")
62+-- | Return type of template-reading functions.
34− > |]
63+type ElmUrl url = (url -> [(TS.Text, TS.Text)] -> TS.Text) -> Elm
36−
65+-- | A typeclass for types that can be interpolated in CoffeeScript templates.
37− How the elm code is compiled depends on how you intend to use it.
66+class ToElm a where
38− Example uses are the included 'generateHtml' function as well as the
67+ toElm :: a -> Builder
39− "Language.Elm.Yesod" module.
68+instance ToElm [Char] where toElm = fromLazyText . TL.pack
40−-}
69+instance ToElm TS.Text where toElm = fromText
41−elm :: QuasiQuoter
70+instance ToElm TL.Text where toElm = fromLazyText
43− { quoteExp = \s ->
72+elmSettings :: Q ShakespeareSettings
44− let chunks = flip map (getQChunks s) $ \c ->
73+elmSettings = do
45− case c of
74+ toJExp <- [|toElm|]
46− S t -> [| t |]
75+ wrapExp <- [|Elm|]
47− E t -> let Right e = parseExp t in appE [| show |] (return e)
76+ unWrapExp <- [|unElm|]
48− in appE [| concat |] (listE chunks)
77+ return $ defaultShakespeareSettings { toBuilder = toJExp
51−{- | elmFile is a quoteFile wrapper around the 'elm' QuasiQuoter and allows for
52− external elm source files to be embedded.
56− [elmFile|elm-source/page.elm|]
59− Please note that no spaces should be added before and after the filename.
61−elmFile :: QuasiQuoter
82+elm :: QuasiQuoter
62−elmFile = quoteFile elm
83+elm = QuasiQuoter { quoteExp = \s -> do
64−data QChunk =
85+ quoteExp (shakespeare rs) s
65− S String -- ^ A literal string
86+ }
66− | E String -- ^ An expression (must be of the Show typeclass)
87+
67− deriving (Show, Eq)
88+elmFile :: FilePath -> Q Exp
70−{- thanks to mikeplus64 for providing an example QuasiQuoter that I could look at while writing this :) -}
91+ shakespeareFile rs fp
71−getQChunks :: String -> [QChunk]
92+
72−getQChunks si = let i = T.pack si
93+elmFileReload :: FilePath -> Q Exp
73− Right m = parseOnly parser (strip i) in m
94+elmFileReload fp = do
74− where
95+ rs <- elmSettings
75− parser = go []
96+ shakespeareFileReload rs fp
78− txt <- takeTill (== '^')
79− evt <- choice [expression, fmap (S . T.unpack) takeText]
82− then return $ filter (not . blank) $ reverse (evt:(S . T.unpack) txt:s)
83− else go (evt:(S . T.unpack) txt:s)
91− expr <- takeTill (== '}')
93− return $ (E . T.unpack) expr