on master help sign in
* added Greg Weber's Shakespearean Elm templates
e0aa5c5Vincent Ambo
elm-yesod/Language/Elm/Yesod.hs+2−1
@@ -25,7 +25,8 @@ import Language.Elm
25-- |toWidget takes some Elm code in String format and produces a widget. Usage example:
25-- |toWidget takes some Elm code in String format and produces a widget. Usage example:
26--
26--
27-- > toWidget [elmFile|elm-source/somePage.elm|]
27-- > toWidget [elmFile|elm-source/somePage.elm|]
28+elmWidget :: ElmSource a
28−elmWidget :: String -- ^ The Elm source code
29+ => a -- ^ The Elm source code
29 -> GWidget sub master ()
30 -> GWidget sub master ()
30elmWidget source =
31elmWidget source =
31 let (html, css, js) = toParts source in
32 let (html, css, js) = toParts source in
elm/Elm.cabal+1−2
@@ -63,9 +63,8 @@ Library
63 blaze-markup == 0.5.1.*,
63 blaze-markup == 0.5.1.*,
64 deepseq,
64 deepseq,
65 text,
65 text,
66− attoparsec >= 0.10,
67 template-haskell,
66 template-haskell,
68− haskell-src-meta
67+ shakespeare
69
68
70Executable elm
69Executable elm
71 Main-is: Compiler.hs
70 Main-is: Compiler.hs
elm/src/Language/Elm.hs+33−25
@@ -1,3 +1,4 @@
1+{-# LANGUAGE TypeSynonymInstances #-}
1{- | This module exports the functions necessary for compiling Elm code into the
2{- | This module exports the functions necessary for compiling Elm code into the
2 respective HTML, JS and CSS code.
3 respective HTML, JS and CSS code.
3
4
@@ -7,10 +8,8 @@
7 The documentation for the Elm language is available at <http://elm-lang.org/Documentation.elm>
8 The documentation for the Elm language is available at <http://elm-lang.org/Documentation.elm>
8-}
9-}
9module Language.Elm (
10module Language.Elm (
10− toHtml,
11+ ElmSource (..)
11− toParts,
12− elm,
13− elmFile) where
12+ ) where
14
13
15import CompileToJS
14import CompileToJS
16import ExtractNoscript
15import ExtractNoscript
@@ -19,25 +18,34 @@ import Initialize
19import Text.Blaze.Html (Html)
18import Text.Blaze.Html (Html)
20import Language.Elm.Quasi
19import Language.Elm.Quasi
21
20
21+import qualified Data.Text.Lazy as TL
22
22
23−-- |This function compiles Elm code into a full HTML page.
23+class ElmSource a where
24−--
25−-- Usage example:
26−--
27−-- > toHtml "/elm-min.js" "Some title" [elmFile|elm-source/somePage.elm|]
28−toHtml :: String -- ^ Location of elm-min.js as expected by the browser
29− -> String -- ^ The page title
30− -> String -- ^ The elm source code.
31− -> Html
32−toHtml = generateHtml
33−
34−-- |This function compiles Elm code to three separate parts: HTML, CSS,
24+ -- |This function compiles Elm code to three separate parts: HTML, CSS,
35−-- and JavaScript. The HTML is only the contents of the body, so the three
25+ -- and JavaScript. The HTML is only the contents of the body, so the three
36−-- parts must be combined in a basic HTML skeleton.
26+ -- parts must be combined in a basic HTML skeleton.
37−toParts :: String -- ^ The Elm source code.
27+ toParts :: a -> (Html, Html, String)
38− -> (Html, Html, String) -- ^ HTML, CSS, and JavaScript in that order.
28+ -- |This function compiles Elm code into a full HTML page.
29+ toHtml :: String -- ^ Location of elm-min.js as expected by the browser
30+ -> String -- ^ The page title
31+ -> a -- ^ The elm source code
32+ -> Html
33+
34+instance ElmSource String where
35+ toParts = toPartsHelper
36+ toHtml = generateHtml
37+
38+instance ElmSource Elm where
39+ toParts = toPartsHelper . TL.unpack . renderElm
40+ toHtml elmL title = generateHtml elmL title . TL.unpack . renderElm
41+
42+instance ElmSource TL.Text where
43+ toParts = toPartsHelper . TL.unpack
44+ toHtml elmL title = generateHtml elmL title . TL.unpack
45+
46+-- build helper to avoid boilerplate repetition
47+toPartsHelper :: String -> (Html, Html, String)
39−toParts source = (html, css, js)
48+toPartsHelper source = (html, css, js)
40− where expr = initialize source
49+ where expr = initialize source
41− js = compileToJS expr
50+ js = compileToJS expr
42− html = body $ either id extract expr
51+ html = body $ either id extract expr
43−
elm/src/Language/Elm/Quasi.hs+92−89
@@ -1,93 +1,96 @@
1−{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
1+{-# LANGUAGE TemplateHaskell #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE CPP #-}
2{-# OPTIONS_GHC -fno-warn-missing-fields #-}
5{-# OPTIONS_GHC -fno-warn-missing-fields #-}
3
6
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.
10+--
11+-- Further reading on Shakespearean templates: <http://www.yesodweb.com/book/templates>
12+--
13+-- Further reading on Elm: <http://elm-lang.org>
4−module Language.Elm.Quasi (elm, elmFile) where
14+module Language.Elm.Quasi
5−
15+ ( -- * Functions
16+ -- ** Template-Reading Functions
17+ -- | These QuasiQuoter and Template Haskell methods return values of
18+ -- type @'ElmUrl' url@. See the Yesod book for details.
19+ elm
20+ , elmFile
21+ , elmFileReload
22+
23+ -- * Datatypes
24+ , ElmUrl
25+ , Elm (..)
26+
27+ -- * Typeclass for interpolated variables
28+ , ToElm (..)
29+
30+ -- ** Rendering Functions
31+ , renderElm
32+ , renderElmUrl
33+
34+ ) where
35+
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
18− elmPage = [elm|
47+
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:
21− @
50+--
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
28−
57+
29− Example:
58+-- | Newtype wrapper of 'Builder'.
30−
59+newtype Elm = Elm { unElm :: Builder }
31− > let google = "http://google.com"
60+ deriving Monoid
32− > in [elm|
61+
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
35−
64+
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
42−elm = QuasiQuoter
71+
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
78+ , wrap = wrapExp
79+ , unwrap = unWrapExp
49 }
80 }
50
81
51−{- | elmFile is a quoteFile wrapper around the 'elm' QuasiQuoter and allows for
52− external elm source files to be embedded.
53− Usage:
54−
55− @
56− [elmFile|elm-source/page.elm|]
57− @
58−
59− Please note that no spaces should be added before and after the filename.
60−-}
61−elmFile :: QuasiQuoter
82+elm :: QuasiQuoter
62−elmFile = quoteFile elm
83+elm = QuasiQuoter { quoteExp = \s -> do
63−
84+ rs <- elmSettings
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
68−
89+elmFile fp = do
69−
90+ rs <- elmSettings
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
76−
77− go s = do
78− txt <- takeTill (== '^')
79− evt <- choice [expression, fmap (S . T.unpack) takeText]
80− end <- atEnd
81− if end
82− then return $ filter (not . blank) $ reverse (evt:(S . T.unpack) txt:s)
83− else go (evt:(S . T.unpack) txt:s)
84−
85− blank (S "") = True
86− blank (E "") = True
87− blank _ = False
88−
89− expression = do
90− string "^{"
91− expr <- takeTill (== '}')
92− char '}'
93− return $ (E . T.unpack) expr