Copyright | Copyright (C) 2009-2016 John MacFarlane |
---|---|
License | BSD3 |
Maintainer | John MacFarlane <jgm@berkeley.edu> |
Stability | alpha |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This is the templating system used by pandoc. It was formerly be a module in pandoc. It has been split off to make it easier to use independently.
Example of use
import Data.Text (Text) import qualified Data.Text.IO as T import Data.Aeson import Text.DocTemplates data Employee = Employee { firstName :: String , lastName :: String , salary :: Maybe Int } instance ToJSON Employee where toJSON e = object [ "name" .= object [ "first" .= firstName e , "last" .= lastName e ] , "salary" .= salary e ] template :: Text template = "$for(employee)$Hi, $employee.name.first$. $if(employee.salary)$You make $employee.salary$.$else$No salary data.$endif$$sep$\n$endfor$" main :: IO () main = do res <- compileTemplate "mytemplate.txt" template case res of Left e -> error e Right t -> T.putStrLn $ renderTemplate t $ object ["employee" .= [ Employee "John" "Doe" Nothing , Employee "Omar" "Smith" (Just 30000) , Employee "Sara" "Chen" (Just 60000) ] ]
Delimiters
To mark variables and control structures in the template, either $
…$
or ${
…}
may be used as delimiters. The styles may also be mixed in
the same template, but the opening and closing delimiter must match in
each case. The opening delimiter may be followed by one or more spaces
or tabs, which will be ignored. The closing delimiter may be followed by
one or more spaces or tabs, which will be ignored.
To include a literal $
in the document, use $$
.
Comments
Anything between the sequence $--
and the end of the line will be
treated as a comment and omitted from the output.
Interpolated variables
A slot for an interpolated variable is a variable name surrounded by
matched delimiters. Variable names must begin with a letter and can
contain letters, numbers, _
, -
, and .
. The keywords it
, if
,
else
, endif
, for
, sep
, and endfor
may not be used as variable
names. Examples:
$foo$ $foo.bar.baz$ $foo_bar.baz-bim$ $ foo $ ${foo} ${foo.bar.baz} ${foo_bar.baz-bim} ${ foo }
The values of variables are determined by a JSON object that is passed
as a parameter to renderTemplate
. So, for example, title
will return
the value of the title
field, and employee.salary
will return the
value of the salary
field of the object that is the value of the
employee
field.
- If the value of the variable is a JSON string, the string will be rendered verbatim. (Note that no escaping is done on the string; the assumption is that the calling program will escape the strings appropriately for the output format.)
- If the value is a JSON array, the values will be concatenated.
- If the value is a JSON object, the string
true
will be rendered. - If the value is a JSON number, it will be rendered as an integer if possible, otherwise as a floating-point number.
- If the value is a JSON boolean, it will be rendered as
true
if true, and as the empty string if false. - Every other value will be rendered as the empty string.
The value of a variable will be indented to the same level as the opening delimiter of the variable.
Conditionals
A conditional begins with if(variable)
(enclosed in matched
delimiters) and ends with endif
(enclosed in matched delimiters). It
may optionally contain an else
(enclosed in matched delimiters). The
if
section is used if variable
has a non-empty value, otherwise the
else
section is used (if present). (Note that even the string false
counts as a true value.) Examples:
$if(foo)$bar$endif$ $if(foo)$ $foo$ $endif$ $if(foo)$ part one $else$ part two $endif$ ${if(foo)}bar${endif} ${if(foo)} ${foo} ${endif} ${if(foo)} ${ foo.bar } ${else} no foo! ${endif}
Conditional keywords should not be indented, or unexpected spacing problems may occur.
For loops
A for loop begins with for(variable)
(enclosed in matched delimiters)
and ends with endfor
(enclosed in matched delimiters. If variable
is
an array, the material inside the loop will be evaluated repeatedly,
with variable
being set to each value of the array in turn. If the
value of the associated variable is not an array, a single iteration
will be performed on its value.
Examples:
$for(foo)$$foo$$sep$, $endfor$ $for(foo)$ - $foo.last$, $foo.first$ $endfor$ ${ for(foo.bar) } - ${ foo.bar.last }, ${ foo.bar.first } ${ endfor }
You may optionally specify a separator between consecutive values using
sep
(enclosed in matched delimiters). The material between sep
and
the endfor
is the separator.
${ for(foo) }${ foo }${ sep }, ${ endfor }
Instead of using variable
inside the loop, the special anaphoric
keyword it
may be used.
${ for(foo.bar) } - ${ it.last }, ${ it.first } ${ endfor }
Partials
Partials (subtemplates stored in different files) may be included using the syntax
${ boilerplate() }
The partials are obtained using getPartial
from the TemplateMonad
class. This may be implemented differently in different monads. The path
passed to getPartial
is computed on the basis of the original template
path (a parameter to compileTemplate
) and the partial’s name. The
partial’s name is substituted for the base name of the original
template path (leaving the original template’s extension), unless the
partial has an explicit extension, in which case this is kept. So, with
the TemplateMonad
instance for IO, partials will be sought in the
directory containing the main template, and will be assumed to have the
extension of the main template.
Partials may optionally be applied to variables using a colon:
${ date:fancy() } ${ articles:bibentry() }
If articles
is an array, this will iterate over its values, applying
the partial bibentry()
to each one. So the second example above is
equivalent to
${ for(articles) } ${ it:bibentry() } ${ endfor }
Final newlines are omitted from included partials.
Partials may include other partials. If you exceed a nesting level of
50, though, in resolving partials, the literal (loop)
will be
returned, to avoid infinite loops.
A separator between values of an array may be specified in square brackets, immediately after the variable name or partial:
${months[, ]}$ ${articles:bibentry()[; ]$
The separator in this case is literal and (unlike with sep
in an
explicit for
loop) cannot contain interpolated variables or other
template directives.
Documentation
compileTemplate :: TemplateMonad m => FilePath -> Text -> m (Either String Template) Source #
applyTemplate :: (TemplateMonad m, ToJSON a) => FilePath -> Text -> a -> m (Either String Text) Source #
class Monad m => TemplateMonad m where Source #
getPartial :: FilePath -> Parser m Text Source #
Instances
TemplateMonad IO Source # | |
Defined in Text.DocTemplates | |
TemplateMonad Identity Source # | |
Defined in Text.DocTemplates |
Instances
Eq Template Source # | |
Data Template Source # | |
Defined in Text.DocTemplates gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Template -> c Template # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c Template # toConstr :: Template -> Constr # dataTypeOf :: Template -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c Template) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Template) # gmapT :: (forall b. Data b => b -> b) -> Template -> Template # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Template -> r # gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Template -> r # gmapQ :: (forall d. Data d => d -> u) -> Template -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> Template -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> Template -> m Template # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Template -> m Template # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Template -> m Template # | |
Ord Template Source # | |
Defined in Text.DocTemplates | |
Read Template Source # | |
Show Template Source # | |
Generic Template Source # | |
Semigroup Template Source # | |
Monoid Template Source # | |
type Rep Template Source # | |
Defined in Text.DocTemplates type Rep Template = D1 (MetaData "Template" "Text.DocTemplates" "doctemplates-0.3-IjVmbfZZHSF7YCrMvGx8KO" True) (C1 (MetaCons "Template" PrefixI True) (S1 (MetaSel (Just "unTemplate") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 [TemplatePart]))) |
data TemplatePart Source #
Interpolate Variable | |
Conditional Variable Template Template | |
Iterate Variable Template Template | |
Partial Template | |
Literal Text |
Instances
Variable | |
|
Instances
Eq Variable Source # | |
Data Variable Source # | |
Defined in Text.DocTemplates gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Variable -> c Variable # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c Variable # toConstr :: Variable -> Constr # dataTypeOf :: Variable -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c Variable) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Variable) # gmapT :: (forall b. Data b => b -> b) -> Variable -> Variable # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Variable -> r # gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Variable -> r # gmapQ :: (forall d. Data d => d -> u) -> Variable -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> Variable -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> Variable -> m Variable # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Variable -> m Variable # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Variable -> m Variable # | |
Ord Variable Source # | |
Defined in Text.DocTemplates | |
Read Variable Source # | |
Show Variable Source # | |
Generic Variable Source # | |
Semigroup Variable Source # | |
Monoid Variable Source # | |
type Rep Variable Source # | |
Defined in Text.DocTemplates |