ede 0.3.3.0 → 0.3.4.0
raw patch · 20 files changed
+306/−47 lines, 20 filesdep +attoparsec-aesondep ~aesondep ~optparse-applicativePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: attoparsec-aeson
Dependency ranges changed: aeson, optparse-applicative
API changes (from Hackage documentation)
- Text.EDE.Internal.Quoting: instance Text.EDE.Internal.Quoting.Quote GHC.Integer.Type.Integer
- Text.EDE.Internal.Quoting: instance Text.EDE.Internal.Quoting.Unquote GHC.Integer.Type.Integer
+ Text.EDE.Internal.AST: eOverrideBlock :: Id -> Exp a -> Exp a -> Exp a
+ Text.EDE.Internal.AST: eblock :: Id -> Exp a -> Exp a
+ Text.EDE.Internal.AST: eset :: Id -> Exp a -> Exp a -> Exp a
+ Text.EDE.Internal.Eval: [_blocks] :: Env -> HashMap Id (Exp Delta)
+ Text.EDE.Internal.Eval: [_extends] :: Env -> Bool
+ Text.EDE.Internal.Eval: bindBlock :: (HashMap Id (Exp Delta) -> HashMap Id (Exp Delta)) -> Context a -> Context a
+ Text.EDE.Internal.Eval: setExtended :: Bool -> Context a -> Context a
+ Text.EDE.Internal.Parser: [_blocks] :: Env -> HashMap Text (Exp Delta)
+ Text.EDE.Internal.Parser: [_extends] :: Env -> HashMap Text (NonEmpty Delta)
+ Text.EDE.Internal.Parser: block' :: Parser m => m (Exp Delta)
+ Text.EDE.Internal.Parser: blocks :: Lens' Env (HashMap Text (Exp Delta))
+ Text.EDE.Internal.Parser: extends :: Lens' Env (HashMap Text (NonEmpty Delta))
+ Text.EDE.Internal.Parser: extends' :: Parser m => m (Exp Delta)
+ Text.EDE.Internal.Parser: set :: Parser m => m (Exp Delta)
+ Text.EDE.Internal.Quoting: instance Text.EDE.Internal.Quoting.Quote GHC.Num.Integer.Integer
+ Text.EDE.Internal.Quoting: instance Text.EDE.Internal.Quoting.Unquote GHC.Num.Integer.Integer
+ Text.EDE.Internal.Types: EBlock :: !Id -> !a -> ExpF a
+ Text.EDE.Internal.Types: EExt :: !Text -> ExpF a
+ Text.EDE.Internal.Types: EOverrideBlock :: !Id -> !a -> !a -> ExpF a
+ Text.EDE.Internal.Types: [_tmplBlocks] :: Template -> HashMap Id (Exp Delta)
+ Text.EDE.Internal.Types: [_tmplExtends] :: Template -> HashMap Id (Exp Delta)
- Text.EDE: (.=) :: (KeyValue kv, ToJSON v) => Key -> v -> kv
+ Text.EDE: (.=) :: (KeyValue e kv, ToJSON v) => Key -> v -> kv
- Text.EDE: data Delta
+ Text.EDE: data () => Delta
- Text.EDE: delimBlock :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE: delimBlock :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE: delimComment :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE: delimComment :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE: delimInline :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE: delimInline :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE: delimPragma :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE: delimPragma :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE.Internal.Eval: Env :: HashMap Id (Exp Delta) -> HashMap Id Term -> HashMap Id Value -> Env
+ Text.EDE.Internal.Eval: Env :: Bool -> HashMap Id (Exp Delta) -> HashMap Id Term -> HashMap Id Value -> HashMap Id (Exp Delta) -> Env
- Text.EDE.Internal.Parser: Env :: !Syntax -> HashMap Text (NonEmpty Delta) -> Env
+ Text.EDE.Internal.Parser: Env :: !Syntax -> HashMap Text (NonEmpty Delta) -> HashMap Text (NonEmpty Delta) -> HashMap Text (Exp Delta) -> Env
- Text.EDE.Internal.Parser: runParser :: Syntax -> Text -> ByteString -> Result (Exp Delta, HashMap Text (NonEmpty Delta))
+ Text.EDE.Internal.Parser: runParser :: Syntax -> Text -> ByteString -> Result (Exp Delta, HashMap Text (NonEmpty Delta), HashMap Text (NonEmpty Delta), HashMap Text (Exp Delta))
- Text.EDE.Internal.Types: Template :: !Text -> !Exp Delta -> HashMap Id (Exp Delta) -> Template
+ Text.EDE.Internal.Types: Template :: !Text -> !Exp Delta -> HashMap Id (Exp Delta) -> HashMap Id (Exp Delta) -> HashMap Id (Exp Delta) -> Template
- Text.EDE.Internal.Types: _Failure :: forall a_abTC. Prism' (Result a_abTC) AnsiDoc
+ Text.EDE.Internal.Types: _Failure :: forall a_ackG. Prism' (Result a_ackG) AnsiDoc
- Text.EDE.Internal.Types: _Success :: forall a_adyU a_abTC. Prism (Result a_adyU) (Result a_abTC) a_adyU a_abTC
+ Text.EDE.Internal.Types: _Success :: forall a_adTF a_ackG. Prism (Result a_adTF) (Result a_ackG) a_adTF a_ackG
- Text.EDE.Internal.Types: class HasSyntax c_adP9
+ Text.EDE.Internal.Types: class HasSyntax c_aeaW
- Text.EDE.Internal.Types: delimBlock :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE.Internal.Types: delimBlock :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE.Internal.Types: delimComment :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE.Internal.Types: delimComment :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE.Internal.Types: delimInline :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE.Internal.Types: delimInline :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE.Internal.Types: delimPragma :: HasSyntax c_adP9 => Lens' c_adP9 Delim
+ Text.EDE.Internal.Types: delimPragma :: HasSyntax c_aeaW => Lens' c_aeaW Delim
- Text.EDE.Internal.Types: syntax :: HasSyntax c_adP9 => Lens' c_adP9 Syntax
+ Text.EDE.Internal.Types: syntax :: HasSyntax c_aeaW => Lens' c_aeaW Syntax
Files
- app/Main.hs +21/−11
- ede.cabal +15/−5
- lib/Text/EDE.hs +75/−10
- lib/Text/EDE/Filters.hs +1/−1
- lib/Text/EDE/Internal/AST.hs +16/−1
- lib/Text/EDE/Internal/Eval.hs +50/−5
- lib/Text/EDE/Internal/Filters.hs +1/−1
- lib/Text/EDE/Internal/Parser.hs +43/−6
- lib/Text/EDE/Internal/Quoting.hs +1/−1
- lib/Text/EDE/Internal/Syntax.hs +6/−2
- lib/Text/EDE/Internal/Types.hs +10/−2
- test/Main.hs +1/−1
- test/resources/ext-base.ede +7/−0
- test/resources/ext-base.golden +2/−0
- test/resources/ext-child.ede +16/−0
- test/resources/ext-child.golden +13/−0
- test/resources/polymorphic-show.golden +1/−1
- test/resources/set.ede +17/−0
- test/resources/set.golden +8/−0
- test/resources/whitespace.golden +2/−0
app/Main.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} @@ -5,6 +6,9 @@ import qualified Control.Monad.Fail as Fail import qualified Data.Aeson as Aeson+#ifdef VERSION_attoparsec_aeson+import qualified Data.Aeson.Parser as Aeson+#endif import qualified Data.Attoparsec.ByteString as Parsec import qualified Data.ByteString as ByteString import qualified Data.ByteString.Lazy as ByteString.Lazy@@ -41,8 +45,8 @@ ( Options.long "context-file" <> Options.metavar "PATH" <> Options.help- "Path of a file containing a JSON object which is used \- \as the template context"+ ("Path of a file containing a JSON object which is used " <>+ "as the template context") ) ) <*> Options.optional@@ -63,26 +67,32 @@ where usage = Pretty.vcat- [ Pretty.empty,+ [ Pretty.emptyDoc, "The --context-file and --context-json options are processed as follows:", Pretty.indent 2 "1.)" Pretty.<+> ( Pretty.align- ( "Both are provided"- Pretty.<$$> "Both objects are merged into one with the keys of \- \--context-json taking precedence over those in the \- \file provided by --context-file."+ ( Pretty.vcat+ [ "Both are provided",+ "Both objects are merged into one with the keys of " <>+ "--context-json taking precedence over those in the " <>+ "file provided by --context-file."+ ] ) ), Pretty.indent 2 "2.)" Pretty.<+> ( Pretty.align- ( "None of them are provided"- Pretty.<$$> "The JSON object is read from STDIN."+ ( Pretty.vcat+ [ "None of them are provided",+ "The JSON object is read from STDIN."+ ] ) ), Pretty.indent 2 "3.)" Pretty.<+> ( Pretty.align- ( "One of them is provided"- Pretty.<$$> "The JSON object is read from the supplied option."+ ( Pretty.vcat+ [ "One of them is provided",+ "The JSON object is read from the supplied option."+ ] ) ) ]
ede.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: ede-version: 0.3.3.0+version: 0.3.4.0 synopsis: Templating language with similar syntax and features to Liquid or Jinja2. @@ -9,7 +9,7 @@ license-file: LICENSE author: Brendan Hay maintainer: Brendan Hay <brendan.g.hay@gmail.com>-copyright: Copyright (c) 2013-2020 Brendan Hay+copyright: Copyright (c) 2013-2022 Brendan Hay stability: Experimental category: Text, Template, Web build-type: Simple@@ -45,6 +45,9 @@ type: git location: git://github.com/brendanhay/ede.git +flag AttoparsecAeson+ description: Build attoparsec-aeson.+ common base default-language: Haskell2010 ghc-options:@@ -95,15 +98,22 @@ main-is: Main.hs ghc-options: -threaded -rtsopts -with-rtsopts=-A128m build-depends:- , aeson >=1.4 , attoparsec >=0.13 , bytestring >=0.10.4 , ede- , optparse-applicative >=0.11- , prettyprinter >= 1.7+ , optparse-applicative >=0.18+ , prettyprinter >=1.7 , prettyprinter-ansi-terminal >=1.1 , text >=1.2 , unordered-containers >=0.2.3++ if flag(AttoparsecAeson)+ build-depends:+ , aeson >=2.2+ , attoparsec-aeson >=2.2+ else+ build-depends:+ , aeson >=1.4 test-suite tests import: base
lib/Text/EDE.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Text.EDE--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or@@ -109,6 +109,9 @@ -- ** Let Expressions -- $let++ -- ** Set+ -- $set ) where @@ -211,14 +214,15 @@ -- | Strict 'ByteString' template definition. ByteString -> m (Result Template)-parseWith config f name =- result failure resolve- . Parser.runParser config name+parseWith config f name input =+ case Parser.runParser config name input of+ Success (u, is', es', bs) -> do+ is <- Foldable.foldrM include (Success (HashMap.singleton name u)) (HashMap.toList is')+ es <- Foldable.foldrM extends (Success mempty) (HashMap.toList es')+ pure (Template name u <$> is <*> es <*> pure bs)+ Failure err ->+ failure err where- resolve (u, is) =- Foldable.foldrM include (Success (HashMap.singleton name u)) (HashMap.toList is)- >>= result failure (success . Template name u)- -- Presuming self is always in self's includes, see singleton above. -- FIXME: utilise the list of deltas for failures include (_, _) (Failure err) = failure err@@ -226,6 +230,11 @@ f config key delta >>= result failure (success . mappend ss . _tmplIncl) + extends (_, _) (Failure err) = failure err+ extends (key, delta :| _) (Success ss) =+ f config key delta+ >>= result failure (success . mappend ss . _tmplIncl)+ -- | 'HashMap' resolver for @include@ expressions. -- -- The 'identifier' component of the @include@ expression is treated as a lookup@@ -288,8 +297,12 @@ -- | Bindings to make available in the environment. HashMap Text Value -> Result Text.Lazy.Text-renderWith fs (Template _ u ts) =- fmap Text.Builder.toLazyText . Eval.render ts fs u+renderWith fs t =+ fmap Text.Builder.toLazyText .+ Eval.render+ (_tmplIncl t <> _tmplExtends t)+ fs+ (_tmplExp t) -- | /See:/ 'parse' eitherParse :: ByteString -> Either String Template@@ -696,3 +709,55 @@ -- > {{ var }} -- > ... -- > {% endlet %}++-- $set+--+-- You can also bind an identifier to whole templates which will be available within+-- the following expression scope. The identifier will be available in subsequent template.+--+-- For example:+--+-- > {% set var %}+-- > ...+-- > {% endset %}+-- > ...+-- > {{ var }}++-- $block+--+-- Blocks are used for inheritance and act as both placeholders and replacements at the same time: The most powerful+-- part of @ED-E@ is template inheritance. Template inheritance allows you to build a base "skeleton" template that+-- contains all the common elements of your site and defines blocks that child templates can override.+--+-- Base template:+--+-- > <!doctype html>+-- > {% block head %}+-- > <title>{% block title %}{% endblock %}</title>+-- > {% endblock %}+-- > {% block body %}{% endblock %}+--+-- Child template:+--+-- > {% extends "base.html" %}+-- > {% block title %}Index{% endblock %}+-- > {% block head %}+-- > {{ super }}+-- > <style type="text/css">+-- > .important { color: #336699; }+-- > </style>+-- > {% endblock %}+-- > {% block body %}+-- > <h1>Index</h1>+-- > <p class="important">+-- > Welcome to my awesome homepage.+-- > </p>+-- > {% endblock %}+--+-- The @{% extends %}@ tag is the key here. It tells the template engine that this template "extends" another template.+-- When the template system evaluates this template, it first locates the parent. The extends tag should be the first tag in the template.+--+-- As you can see it's also possible to render the contents of the parent block by calling super(). You can’t define multiple {% block %}+-- tags with the same name in the same template. This limitation exists because a block tag works in “both” directions. That is, a block+-- tag doesn’t just provide a placeholder to fill - it also defines the content that fills the placeholder in the parent. If there were two+-- similarly-named {% block %} tags in a template, that template's parent wouldn’t know which one of the blocks’ content to use.
lib/Text/EDE/Filters.hs view
@@ -1,6 +1,6 @@ -- | -- Module : Text.EDE.Filters--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or
lib/Text/EDE/Internal/AST.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Text.EDE.Internal.AST--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or@@ -56,6 +56,21 @@ elet :: Maybe (Id, Exp a) -> Exp a -> Exp a elet m e = maybe e (\(i, b) -> Comonad.extract b :< ELet i b e) m {-# INLINEABLE elet #-}++eset :: Id -> Exp a -> Exp a -> Exp a+eset i e k =+ elet (Just (i, e)) k+{-# INLINEABLE eset #-}++eblock :: Id -> Exp a -> Exp a+eblock i e =+ Comonad.extract e :< EBlock i e+{-# INLINEABLE eblock #-}++eOverrideBlock :: Id -> Exp a -> Exp a -> Exp a+eOverrideBlock i b e =+ Comonad.extract b :< EOverrideBlock i b e+{-# INLINEABLE eOverrideBlock #-} ecase :: Exp a ->
lib/Text/EDE/Internal/Eval.hs view
@@ -5,7 +5,7 @@ -- | -- Module : Text.EDE.Internal.Eval--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or@@ -49,9 +49,11 @@ import qualified Text.Trifecta.Delta as Trifecta.Delta data Env = Env- { _templates :: HashMap Id (Exp Delta),+ { _extends :: Bool,+ _templates :: HashMap Id (Exp Delta), _quoted :: HashMap Id Term,- _values :: HashMap Id Value+ _values :: HashMap Id Value,+ _blocks :: HashMap Id (Exp Delta) } type Context = ReaderT Env Result@@ -63,7 +65,7 @@ HashMap Id Value -> Result Builder render ts fs e o =- Reader.runReaderT (eval e >>= nf) (Env ts (stdlib <> fs) o)+ Reader.runReaderT (eval e >>= nf) (Env False ts (stdlib <> fs) o mempty) where nf (TVal v) = build (Trifecta.Delta.delta e) v nf _ =@@ -85,6 +87,26 @@ x <- eval a y <- eval b binding d x y+eval (d :< EOverrideBlock i b e) =+ setExtended True $ do+ q <- HashMap.lookup i <$> Reader.asks _blocks+ case q of+ Nothing ->+ bindBlock (HashMap.insert i b) (eval e)+ Just eb ->+ bindBlock (HashMap.insert i (d :< ELet "super" b eb)) (eval e)+eval (_ :< EBlock i b) = do+ extends <- Reader.asks _extends+ q <- HashMap.lookup i <$> Reader.asks _blocks+ if extends then+ pure (qprim (String mempty))+ else do+ x <- eval b+ v <- lift (unquote i 0 x)+ maybe+ (pure x)+ (bind (HashMap.insert "super" v) . eval)+ q eval (_ :< ELet k rhs bdy) = do q <- eval rhs v <- lift (unquote k 0 q)@@ -164,18 +186,41 @@ eval (d :< EIncl i) = do ts <- Reader.asks _templates case HashMap.lookup i ts of- Just e -> eval e+ Just e ->+ -- Don't inherit any blocks declared so far.+ setExtended False $+ bindBlock (\_ -> mempty) (eval e) Nothing -> throwError d $ "template" <+> bold (pp i) <+> "is not in scope:" <+> PP.brackets (pp (Text.intercalate "," $ HashMap.keys ts))+eval (d :< EExt i) = do+ ts <- Reader.asks _templates+ case HashMap.lookup i ts of+ Just e ->+ setExtended False (eval e)+ Nothing ->+ throwError d $+ "template"+ <+> bold (pp i)+ <+> "is not in scope:"+ <+> PP.brackets (pp (Text.intercalate "," $ HashMap.keys ts)) {-# INLINEABLE eval #-} bind :: (HashMap Text Value -> HashMap Text Value) -> Context a -> Context a bind f = Reader.withReaderT (\x -> x {_values = f (_values x)}) {-# INLINEABLE bind #-}++bindBlock :: (HashMap Id (Exp Delta) -> HashMap Id (Exp Delta)) -> Context a -> Context a+bindBlock f = Reader.withReaderT (\x -> x {_blocks = f (_blocks x)})+{-# INLINEABLE bindBlock #-}++setExtended :: Bool -> Context a -> Context a+setExtended extends =+ Reader.withReaderT (\x -> x { _extends = extends })+{-# INLINEABLE setExtended #-} variable :: Delta -> Var -> Context Value variable d (Var is) =
lib/Text/EDE/Internal/Filters.hs view
@@ -8,7 +8,7 @@ -- | -- Module : Text.EDE.Internal.Filters--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or
lib/Text/EDE/Internal/Parser.hs view
@@ -11,7 +11,7 @@ -- | -- Module : Text.EDE.Internal.Parser--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or@@ -35,7 +35,6 @@ import qualified Control.Monad.State.Strict as State import Control.Monad.Trans (lift) import Data.Aeson.Types (Value (..))-import qualified Data.Bifunctor as Bifunctor import Data.ByteString (ByteString) import qualified Data.Char as Char import Data.HashMap.Strict (HashMap)@@ -64,7 +63,9 @@ data Env = Env { _settings :: !Syntax,- _includes :: HashMap Text (NonEmpty Delta)+ _includes :: HashMap Text (NonEmpty Delta),+ _extends :: HashMap Text (NonEmpty Delta),+ _blocks :: HashMap Text (Exp Delta) } $(Lens.makeLenses ''Env)@@ -120,15 +121,25 @@ Syntax -> Text -> ByteString ->- Result (Exp Delta, HashMap Text (NonEmpty Delta))+ Result (Exp Delta, HashMap Text (NonEmpty Delta), HashMap Text (NonEmpty Delta), HashMap Text (Exp Delta)) runParser o n = res . Trifecta.parseByteString (runEDE run) pos where- run = State.runStateT (pragma *> document <* Trifecta.eof) (Env o mempty)+ run = State.runStateT (pragma *> document <* Trifecta.eof) (Env o mempty mempty mempty) pos = Trifecta.Delta.Directed (Text.Encoding.encodeUtf8 n) 0 0 0 0 res = \case- Trifecta.Success x -> Success (Bifunctor.second _includes x)+ Trifecta.Success (x, env) ->+ if null (_extends env) then+ Success (x, _includes env, _extends env, _blocks env)+ else+ -- In case the template extends another template we arrange the contained+ -- blocks in a way that make it easy to evaluate the "inheritance" mechanism.+ let+ x' =+ HashMap.foldrWithKey eOverrideBlock x (_blocks env)+ in+ Success (x', _includes env, _extends env, _blocks env) Trifecta.Failure e -> Failure (Trifecta._errDoc e) pragma :: Parser m => m ()@@ -184,6 +195,9 @@ cases, loop, include,+ set,+ block',+ extends', binding, raw, comment@@ -257,6 +271,13 @@ (,) <$> (keyword "with" *> identifier) <*> (Trifecta.symbol "=" *> term) +extends' :: Parser m => m (Exp Delta)+extends' = block "extends" $ do+ d <- Trifecta.position+ k <- Trifecta.stringLiteral+ extends %= HashMap.insertWith (<>) k (d :| [])+ pure (d :< EExt k)+ binding :: Parser m => m (Exp Delta) binding = elet . Just@@ -267,6 +288,22 @@ ) <*> document <* exit "endlet"++set :: Parser m => m (Exp Delta)+set =+ eset+ <$> block "set" identifier+ <*> document+ <* exit "endset"+ <*> document++block' :: Parser m => m (Exp Delta)+block' = do+ k <- block "block" identifier+ e <- document+ _ <- exit "endblock"+ blocks %= HashMap.insert k e+ pure (eblock k e) raw :: Parser m => m (Exp Delta) raw = ann (ELit <$> body)
lib/Text/EDE/Internal/Quoting.hs view
@@ -8,7 +8,7 @@ -- | -- Module : Text.EDE.Internal.Quoting--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or
lib/Text/EDE/Internal/Syntax.hs view
@@ -2,7 +2,7 @@ -- | -- Module : Text.EDE.Internal.Syntax--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or@@ -104,7 +104,11 @@ "_", ".", "true",- "false"+ "false",+ "set",+ "endset",+ "block",+ "endblock" ] pragmaStyle :: TokenParsing m => IdentifierStyle m
lib/Text/EDE/Internal/Types.hs view
@@ -11,7 +11,7 @@ -- | -- Module : Text.EDE.Internal.Types--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or@@ -166,7 +166,9 @@ data Template = Template { _tmplName :: !Text, _tmplExp :: !(Exp Delta),- _tmplIncl :: HashMap Id (Exp Delta)+ _tmplIncl :: HashMap Id (Exp Delta),+ _tmplExtends :: HashMap Id (Exp Delta),+ _tmplBlocks :: HashMap Id (Exp Delta) } deriving (Eq) @@ -203,9 +205,12 @@ | EFun !Id | EApp !a !a | ELet !Id !a !a+ | EBlock !Id !a+ | EOverrideBlock !Id !a !a | ECase !a [Alt a] | ELoop !Id !a !a | EIncl !Text+ | EExt !Text deriving (Eq, Show, Functor) instance Functor.Classes.Eq1 ExpF where@@ -214,11 +219,14 @@ liftEq _ (EFun a) (EFun b) = a == b liftEq c (EApp a1 a2) (EApp b1 b2) = a1 `c` b1 && a2 `c` b2 liftEq c (ELet a0 a1 a2) (ELet b0 b1 b2) = a0 == b0 && a1 `c` b1 && a2 `c` b2+ liftEq c (EBlock a0 a1) (EBlock b0 b1) = a0 == b0 && a1 `c` b1+ liftEq c (EOverrideBlock a0 a1 a2) (EOverrideBlock b0 b1 b2) = a0 == b0 && a1 `c` b1 && a2 `c` b2 liftEq c (ECase a as) (ECase b bs) = a `c` b && (List.all (uncurry altEq) $ zip as bs) where altEq (pA, a') (pB, b') = pA == pB && a' `c` b' liftEq c (ELoop a0 a1 a2) (ELoop b0 b1 b2) = a0 == b0 && a1 `c` b1 && a2 `c` b2 liftEq _ (EIncl a) (EIncl b) = a == b+ liftEq _ (EExt a) (EExt b) = a == b liftEq _ _ _ = False type Exp = Cofree ExpF
test/Main.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} -- Module : Main--- Copyright : (c) 2013-2020 Brendan Hay <brendan.g.hay@gmail.com>+-- Copyright : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com> -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or
+ test/resources/ext-base.ede view
@@ -0,0 +1,7 @@+{}+---+<!doctype html>+{% block head %}+<title>{% block title %}{% endblock %}</title>+{% endblock %}+{% block body %}{% endblock %}
+ test/resources/ext-base.golden view
@@ -0,0 +1,2 @@+<!doctype html>+<title></title>
+ test/resources/ext-child.ede view
@@ -0,0 +1,16 @@+{}+---+{% extends "ext-base.ede" %}+{% block title %}Index{% endblock %}+{% block head %}+ {{ super }}+ <style type="text/css">+ .important { color: #336699; }+ </style>+{% endblock %}+{% block body %}+ <h1>Index</h1>+ <p class="important">+ Welcome to my awesome homepage.+ </p>+{% endblock %}
+ test/resources/ext-child.golden view
@@ -0,0 +1,13 @@+{}+---+<!doctype html>+ <title>Index</title>++ <style type="text/css">+ .important { color: #336699; }+ </style>+ <h1>Index</h1>+ <p class="important">+ Welcome to my awesome homepage.+ </p>+
test/resources/polymorphic-show.golden view
@@ -4,4 +4,4 @@ 1.0 ["a","b","c","d","e"] [1,2,3,4,5]-{"foo":"x","bar":"y"}+{"bar":"y","foo":"x"}
+ test/resources/set.ede view
@@ -0,0 +1,17 @@+{+ "var": true+}+---+{% set x %}++ {% set y %}+ This is a nested block {{var}}+ {% endset %}++ This is a big expression! {{var}}++ {{ y }}++{% endset %}++{{x}}
+ test/resources/set.golden view
@@ -0,0 +1,8 @@++++ This is a big expression! true++ This is a nested block true++
test/resources/whitespace.golden view
@@ -6,6 +6,8 @@ + test +