ginger 0.5.1.0 → 0.5.1.2
raw patch · 5 files changed
+207/−31 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +38/−0
- README.md +137/−0
- ginger.cabal +3/−2
- src/Text/Ginger/Run.hs +16/−19
- src/Text/Ginger/Run/Type.hs +13/−10
+ CHANGELOG.md view
@@ -0,0 +1,38 @@+## 0.5.1.1++- Bugfixes wrt indentation mode++## 0.5.1.0++- Expose parser error pretty-printer from the library++## 0.5.0.0++- Indentation mode: `{% indent %}` introduces an indentation context++## 0.4.0.0++- Statements can now return values+- Added `do` expressions (lift statements into expressions)++## 0.3.11.1++- Fixed a parser bug related to whitespace in script mode++## 0.3.11.0++- Fixed the way local scopes work in script mode+- Documented script mode++## 0.3.10.0++- Script mode: alternative syntax that makes it easier to use+ Ginger as a scripting language, used inside {% script %} blocks.++## 0.3.9.1++- Various dependency issues fixed++## 0.3.8.0++- Added a `{% switch %}` statement
+ README.md view
@@ -0,0 +1,137 @@+# Ginger+++[](https://travis-ci.org/tdammers/ginger)+[](https://hackage.haskell.org/package/ginger)++++A Haskell implementation of the [Jinja2](http://jinja.pocoo.org/) template+language.++> A note of warning: the git repository at https://bitbucket.org/tdammers/ginger+> has been deleted and restored with a rewritten commit tree on 2016-04-06 in+> order to clean up the messy history. This means that if you have a checkout+> from before that date, merging the bitbucket repo will most likely break+> things; please do a fresh clone to fix this. Sorry for the inconvenience.++## Introduction++Ginger provides most of the original Jinja2 template language, as much as that+makes sense in a Haskell host application.++We do, however, some of the most blatant Pythonisms, especially when we felt+the features in question were more of an accidental result of binding template+constructs to Python constructs.++On top of that, we deviate on a few points, and add some features that we think+are very useful and help make the language better and more consistent.++## Installation++Ginger is available from [Hackage](https://hackage.haskell.org/package/ginger),+and it is in Stackage, so you can use it in plain Cabal projects as well as+Stack ones.++### Installing with Cabal:++ cabal install ginger++### Installing with Stack:++ stack install ginger++### Using as part of another project:++Add the following to your `.cabal`'s `build-depends`:++ ginger >=0.3.9.1 && <0.4++## Template Syntax++Full template syntax documentation is available from the `/docs` subdirectory+in the project repository itself, or from [the User Guide section on the+Ginger website](https://ginger.tobiasdammers.nl/guide).++### Minimal example template++ <!DOCTYPE html>+ <html>+ <head>+ <title>{{ title }}</title>+ </head>+ {# This is a comment. Comments are removed from the output. #}+ <body>+ <menu id="nav-main">+ {% for item in navigation %}+ <li><a href="{{ item.url }}">{{ item.label }}</a></li>+ {% endfor %}+ </menu>+ <div class="layout-content-main">+ <h1>{{ title }}</h1>+ {{ body }}+ </div>+ </body>+ </html>++There are two kinds of delimiters. `{% ... %}` and `{{ ... }}`. The first+one is used to execute statements such as for-loops or assign values, the+latter prints the result of an expression to the template.++*Not implemented yet*: Jinja2 allows the programmer to override the default+tags from `{% %}` and `{{ }}` to different tokens, e.g. `<% %>` and `<< >>`.+Ginger does not currently support this.++## Haskell API++The Haskell API is documented fully through Haddock documentation, available+from [Hackage](https://hackage.haskell.org/package/ginger). We'll just provide+a short overview here.++### General++On the Haskell side of things, executing a template is a two-step process.+First, template source code is parsed into a 'Template' data structure,+which is then fed to 'runGinger' or 'runGingerT'.++### Parsing++Because Ginger templates can include other templates, the parser needs a way of+resolving template names. Instead of hard-wiring the parser into 'IO' though,+Ginger will work on any Monad type, but requires the caller to provide a+suitable template resolver function. For 'IO', the resolver would typically+load a file from a template directory, but other monads might have access to+some sort of cache, or expose template compiled into a program, or simply+return 'Nothing' unconditionally to disable any and all imports. A suitable+example implementation for 'IO' would look like this:++ loadFile fn = openFile fn ReadMode >>= hGetContents++ loadFileMay fn =+ tryIOError (loadFile fn) >>= \e ->+ case e of+ Right contents ->+ return (Just contents)+ Left err -> do+ print err -- remove this line if you want to fail silently+ return Nothing++(Taken from `cli/GingerCLI.hs`). This interprets the template name as a+filename relative to the CWD, and returns the file contents on success or+'Nothing' if there is any error.++If you don't need a monadic context for resolving includes (e.g. because you+have pre-loaded all template sources), you can use the pure 'parseGinger'+flavor, which does not rely on a host monad.++### Running++The core function for running a template is 'runGinger' (or its monadic+flavor 'runGingerT'); in order to pass an initial context to the template+engine, pass a suitable 'GingerContext', which you can create using the+'makeContext' / 'makeContextM' functions.++An example call (for running a template in 'IO') would look something like+this:++ runGingerT (makeContextM scopeLookup (putStr . Text.unpack . htmlSource)) tpl
ginger.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: ginger-version: 0.5.1.0+version: 0.5.1.2 synopsis: An implementation of the Jinja2 template language in Haskell description: Ginger is Jinja, minus the most blatant pythonisms. Wants to be feature complete, but isn't quite there yet.@@ -14,7 +14,8 @@ -- copyright: category: Text build-type: Simple--- extra-source-files:+extra-source-files: README.md+ , CHANGELOG.md data-files: test/fixtures/*.json cabal-version: >=1.10 bug-reports: https://github.com/tdammers/ginger/issues
src/Text/Ginger/Run.hs view
@@ -445,26 +445,22 @@ => GVal (Run m h) -> Run m h () echo src = do e <- asks contextEncode- newlinesMay <- asks contextNewlines- indentationMay <- gets rsIndentation p <- asks contextWrite- let indent = fromMaybe id $ do- newlines <- newlinesMay- indentation <- indentationMay- return $ applyIndentation newlines indentation-- p . indent . e $ src--applyIndentation :: (Monoid h)- => Newlines h- -> [h]- -> h- -> h-applyIndentation n levels =- let indent = mconcat . List.reverse $ levels- in joinLines n .- fmap (indent <>) .- splitLines n+ asks contextNewlines >>= \case+ Nothing ->+ p . e $ src+ Just newlines -> do+ indentation <- fromMaybe [] <$> gets rsIndentation+ let ls = splitLines newlines $ e src+ indent = mconcat . List.reverse $ indentation+ forM_ ls $ \l -> do+ atLineStart <- gets rsAtLineStart+ if atLineStart+ then p $ indent <> l+ else p l+ modify $ \state -> state {+ rsAtLineStart = endsWithNewline newlines l+ } indented :: (Monad m, Functor m, Monoid h) => h@@ -504,6 +500,7 @@ , rsCurrentTemplate = tpl , rsCurrentBlockName = Nothing , rsIndentation = Nothing+ , rsAtLineStart = True } gfnEval :: (Monad m, Monoid h, ToGVal (Run m h) h)
src/Text/Ginger/Run/Type.hs view
@@ -210,22 +210,16 @@ { splitLines :: h -> [h] , joinLines :: [h] -> h , stripIndent :: h -> h- }--stringNewlines :: Newlines String-stringNewlines =- Newlines- { splitLines = List.lines- , joinLines = List.unlines- , stripIndent = List.dropWhile isSpace+ , endsWithNewline :: h -> Bool } textNewlines :: Newlines Text textNewlines = Newlines- { splitLines = Text.lines- , joinLines = Text.unlines+ { splitLines = reNewline . Text.splitOn "\n"+ , joinLines = mconcat , stripIndent = Text.stripStart+ , endsWithNewline = ("\n" `Text.isSuffixOf`) } htmlNewlines :: Newlines Html@@ -234,8 +228,16 @@ { splitLines = fmap unsafeRawHtml . splitLines textNewlines . htmlSource , joinLines = unsafeRawHtml . joinLines textNewlines . fmap htmlSource , stripIndent = unsafeRawHtml . stripIndent textNewlines . htmlSource+ , endsWithNewline = endsWithNewline textNewlines . htmlSource } +-- | Helper; reinstates newlines after splitting a 'Text' into lines.+reNewline :: [Text] -> [Text]+reNewline [] = []+reNewline ("":[]) = []+reNewline (x:[]) = [x]+reNewline (x:"":[]) = [x <> "\n"]+reNewline (x:xs) = (x <> "\n") : reNewline xs data RunState m h = RunState@@ -244,6 +246,7 @@ , rsCurrentTemplate :: Template -- the template we are currently running , rsCurrentBlockName :: Maybe Text -- the name of the innermost block we're currently in , rsIndentation :: Maybe [h] -- current indentation level, if any+ , rsAtLineStart :: Bool -- is the next output position the first column } -- | Internal type alias for our template-runner monad stack.