git.iko.soy/typeable/pandoc
on master help sign in
Improved template API and fixed a bug. Closes #5979.

* Text.Pandoc.Templates [API change] + Add Monad wrappers `WithDefaultPartials` and `WithPartials`. Wrapping these around an instance of `PandocMonad` gives us different instances of `TemplateMonad`, with different search behavior in retrieving partials. To compile a template and limit partial search to pandoc's data files, use `runWithDefaultPartials (compileTemplate ...)`. To compile a template and allow partials to be found locally (either on the file system or via HTTP, in the event that the main template has an absolute URL), ue `runWithPartials (compileTemplate ...)`. + Export `getTemplate`, which seeks a template locally, or via HTTP if the template has an absolute URL, falling back to the data files if not found. + Export `compileDefaultTemplate` -- does `getDefaultTemplate` and compiles the result, raising an error on failure. * Text.Pandoc.Class [API change] + Remove `TemplateMonad` instances for `PandocIO` and `PandocPure`. These were too limiting and caused a bug whereby a local partial could be used even when the default template was requested. We now rely on instances provided in the Templates module. Text.Pandoc.App.OutputSettings + Simplify template retrieval code.

commit817d2048da08f44ab4a9efae7c761aff99e3d959 authorJohn MacFarlane <jgm@berkeley.edu> dateWed 11 Dec 2019, 10:29 (7 years ago) parent4e7ac06
3 files changed+65−43
src/Text/Pandoc/App/OutputSettings.hs+7−29 src/Text/Pandoc/Class.hs−11 src/Text/Pandoc/Templates.hs+58−3
src/Text/Pandoc/App/OutputSettings.hs+7−29
@@ -25,7 +25,7 @@ import qualified Data.Text as T
2525import Text.DocTemplates (toVal, Context(..), Val(..))
2626import qualified Control.Exception as E
2727import Control.Monad
28−import Control.Monad.Except (catchError, throwError)
28+import Control.Monad.Except (throwError)
2929import Control.Monad.Trans
3030import Data.Char (toLower)
3131import Data.List (find, isPrefixOf)
@@ -171,40 +171,18 @@ optToOutputSettings opts = do
171171 setVariableM "dzslides-core" dzcore vars
172172 else return vars)
173173
174− templStr <- case optTemplate opts of
174+ templ <- case optTemplate opts of
175175 _ | not standalone -> return Nothing
176− Nothing -> Just <$> getDefaultTemplate format
176+ Nothing -> Just <$> compileDefaultTemplate format
177177 Just tp -> do
178178 -- strip off extensions
179179 let tp' = case takeExtension tp of
180180 "" -> tp <.> T.unpack format
181181 _ -> tp
182− Just . UTF8.toText <$>
183− ((do surl <- stSourceURL <$> getCommonState
184− -- we don't want to look for templates remotely
185− -- unless the full URL is specified:
186− modifyCommonState $ \st -> st{
187− stSourceURL = Nothing }
188− (bs, _) <- fetchItem $ T.pack tp'
189− modifyCommonState $ \st -> st{
190− stSourceURL = surl }
191− return bs)
192− `catchError`
193− (\e ->
194− case e of
195− PandocResourceNotFound _ ->
196− readDataFile ("templates" </> tp')
197− _ -> throwError e))
198−
199− let templatePath = fromMaybe "" $ optTemplate opts
200−
201− templ <- case templStr of
202− Nothing -> return Nothing
203− Just ts -> do
204− res <- compileTemplate templatePath ts
205− case res of
206− Left e -> throwError $ PandocTemplateError $ T.pack e
207− Right t -> return $ Just t
182+ res <- getTemplate tp' >>= runWithPartials . compileTemplate tp'
183+ case res of
184+ Left e -> throwError $ PandocTemplateError $ T.pack e
185+ Right t -> return $ Just t
208186
209187 let writerOpts = def {
210188 writerTemplate = templ
src/Text/Pandoc/Class.hs−11
@@ -91,7 +91,6 @@ import Text.Parsec (ParsecT, getPosition, sourceLine, sourceName)
9191import qualified Data.Time as IO (getCurrentTime)
9292import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType)
9393import Text.Pandoc.Definition
94−import Text.DocTemplates (TemplateMonad(..))
9594import Data.Digest.Pure.SHA (sha1, showDigest)
9695import Data.Maybe (fromMaybe)
9796import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds
@@ -315,16 +314,6 @@ readFileFromDirs (d:ds) f = catchError
315314 ((Just . T.pack . UTF8.toStringLazy) <$> readFileLazy (d </> f))
316315 (\_ -> readFileFromDirs ds f)
317316
318−instance TemplateMonad PandocIO where
319− getPartial fp = UTF8.toText <$> catchError
320− (readFileStrict fp)
321− (\_ -> readDataFile ("templates" </> fp))
322−
323−instance TemplateMonad PandocPure where
324− getPartial fp = UTF8.toText <$> catchError
325− (readFileStrict fp)
326− (\_ -> readDataFile ("templates" </> fp))
327−
328317-- | 'CommonState' represents state that is used by all
329318-- instances of 'PandocMonad'. Normally users should not
330319-- need to interact with it directly; instead, auxiliary
src/Text/Pandoc/Templates.hs+58−3
@@ -1,3 +1,4 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
12{-# LANGUAGE MultiParamTypeClasses #-}
23{-# LANGUAGE ScopedTypeVariables #-}
34{-# LANGUAGE OverloadedStrings #-}
@@ -14,18 +15,61 @@ Utility functions for working with pandoc templates.
1415-}
1516
1617module Text.Pandoc.Templates ( Template
18+ , WithDefaultPartials(..)
19+ , WithPartials(..)
1720 , compileTemplate
1821 , renderTemplate
22+ , getTemplate
1923 , getDefaultTemplate
24+ , compileDefaultTemplate
2025 ) where
2126
2227import Prelude
2328import System.FilePath ((<.>), (</>))
24−import Text.DocTemplates (Template, compileTemplate, renderTemplate)
25−import Text.Pandoc.Class (PandocMonad, readDataFile)
29+import Text.DocTemplates (Template, TemplateMonad(..), compileTemplate, renderTemplate)
30+import Text.Pandoc.Class (PandocMonad, readDataFile, fetchItem,
31+ CommonState(..), getCommonState, modifyCommonState)
2632import qualified Text.Pandoc.UTF8 as UTF8
33+import Control.Monad.Except (catchError, throwError)
2734import Data.Text (Text)
2835import qualified Data.Text as T
36+import Text.Pandoc.Error
37+
38+-- | Wrap a Monad in this if you want partials to
39+-- be taken only from the default data files.
40+newtype WithDefaultPartials m a = WithDefaultPartials { runWithDefaultPartials :: m a }
41+ deriving (Functor, Applicative, Monad)
42+
43+-- | Wrap a Monad in this if you want partials to
44+-- be looked for locally (or, when the main template
45+-- is at a URL, via HTTP), falling back to default data files.
46+newtype WithPartials m a = WithPartials { runWithPartials :: m a }
47+ deriving (Functor, Applicative, Monad)
48+
49+instance PandocMonad m => TemplateMonad (WithDefaultPartials m) where
50+ getPartial fp = WithDefaultPartials $
51+ UTF8.toText <$> readDataFile fp
52+
53+instance PandocMonad m => TemplateMonad (WithPartials m) where
54+ getPartial fp = WithPartials $ getTemplate fp
55+
56+-- | Retrieve text for a template.
57+getTemplate :: PandocMonad m => FilePath -> m Text
58+getTemplate tp = UTF8.toText <$>
59+ ((do surl <- stSourceURL <$> getCommonState
60+ -- we don't want to look for templates remotely
61+ -- unless the full URL is specified:
62+ modifyCommonState $ \st -> st{
63+ stSourceURL = Nothing }
64+ (bs, _) <- fetchItem $ T.pack tp
65+ modifyCommonState $ \st -> st{
66+ stSourceURL = surl }
67+ return bs)
68+ `catchError`
69+ (\e -> case e of
70+ PandocResourceNotFound _ ->
71+ readDataFile ("templates" </> tp)
72+ _ -> throwError e))
2973
3074-- | Get default template for the specified writer.
3175getDefaultTemplate :: PandocMonad m
@@ -55,4 +99,15 @@ getDefaultTemplate writer = do
5599 let fname = "templates" </> "default" <.> T.unpack format
56100 UTF8.toText <$> readDataFile fname
57101
58−
102+-- | Get and compile default template for the specified writer.
103+-- Raise an error on compilation failure.
104+compileDefaultTemplate :: PandocMonad m
105+ => Text
106+ -> m (Template Text)
107+compileDefaultTemplate writer = do
108+ res <- getDefaultTemplate writer >>=
109+ runWithDefaultPartials .
110+ compileTemplate ("templates/default." <> T.unpack writer)
111+ case res of
112+ Left e -> throwError $ PandocTemplateError (T.pack e)
113+ Right t -> return t