changeset 694:b917ae071994

formatting: Add a `hoedown` formatter. The `hoedown` library is not yet in the dependency list however since I have to figure out its availability on most platforms.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 23 Mar 2016 08:35:51 -0700
parents d2a87365b85b
children 0c688063890f
files piecrust/formatting/hoedownformatter.py piecrust/plugins/builtin.py
diffstat 2 files changed, 118 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/formatting/hoedownformatter.py	Wed Mar 23 08:35:51 2016 -0700
@@ -0,0 +1,116 @@
+import logging
+import hoedown
+from piecrust.formatting.base import Formatter
+
+
+logger = logging.getLogger(__name__)
+
+
+class HoedownFormatter(Formatter):
+    FORMAT_NAMES = ['hoedown']
+    OUTPUT_FORMAT = 'html'
+
+    def __init__(self):
+        super(HoedownFormatter, self).__init__()
+        self._formatter = None
+
+    def render(self, format_name, txt):
+        assert format_name in self.FORMAT_NAMES
+        self._ensureInitialized()
+        return self._formatter.render(txt)
+
+    def _ensureInitialized(self):
+        if self._formatter is not None:
+            return
+
+        # Don't show warnings once for each worker when baking, so only
+        # show them for the first. If the variable is not set, we're not
+        # baking so do show them either way.
+        show_warnings = (self.app.config.get('baker/worker_id', 0) == 0)
+
+        config = self.app.config.get('hoedown')
+        if config is None:
+            config = {}
+        elif not isinstance(config, dict):
+            raise Exception("The `hoedown` configuration setting must be "
+                            "a dictionary.")
+
+        extensions = config.get('extensions', [])
+        if isinstance(extensions, str):
+            extensions = [e.strip() for e in extensions.split(',')]
+        # Compatibility with PieCrust 1.x
+        if config.get('use_markdown_extra'):
+            extensions.append('extra')
+
+        render_flags = config.get('render_flags')
+        if render_flags is None:
+            render_flags = []
+
+        # Translate standard Markdown formatter extensions to Hoedown
+        # extension/render flags to make it easier to use Hoedown as a drop-in
+        # replacement.
+        exts = 0
+        rdrf = 0
+        other = 0
+        for n in extensions:
+            # Try an extension?
+            e = getattr(hoedown, 'EXT_' + n.upper(), None)
+            if e is not None:
+                exts |= e
+                continue
+
+            # Try a render flag?
+            f = getattr(hoedown, 'HTML_' + n.upper(), None)
+            if f is not None:
+                rdrf |= f
+
+            # Other flag?
+            f = getattr(hoedown, 'TABLE_' + n.upper(), None)
+            if f is not None:
+                other |= f
+
+            # Try translating from a Markdown extension name.
+            t = ext_translate.get(n)
+            if t is None:
+                if show_warnings:
+                    logger.warning("Unknown Hoedown Markdown extension or flag: "
+                                   "%s" % n)
+                continue
+            if not isinstance(t, list):
+                t = [t]
+            for i in t:
+                if i.startswith('EXT_'):
+                    exts |= getattr(hoedown, i)
+                elif i.startswith('HTML_'):
+                    rdrf |= getattr(hoedown, i)
+                elif show_warnings:
+                    logger.warning("Unknown Hoedown Markdown extension or flag:"
+                                   "%s" % n)
+            if n == 'extra' and show_warnings:
+                # Special warning for the 'extra' extension.
+                logger.warning(
+                    "The 'extra' extension doesn't have a full equivalent "
+                    "in Hoedown Markdown. Only 'fenced_code', 'footnotes' and "
+                    "'tables' extensions will be active. "
+                    "To remove this warning, replace 'extra' with those 3 "
+                    "specific extensions.")
+
+        # Enable a few things by default.
+        exts |= hoedown.EXT_NO_INTRA_EMPHASIS
+
+        renderer = hoedown.HtmlRenderer(flags=rdrf)
+        self._formatter = hoedown.Markdown(
+                renderer, extensions=(exts | other))
+
+
+ext_translate = {
+        'fenced_code': 'EXT_FENCED_CODE',
+        'footnotes': 'EXT_FOOTNOTES',
+        'tables': 'EXT_TABLES',
+        'nl2br': 'HTML_HARD_WRAP',
+        'smarty': 'HTML_SMARTYPANTS',
+        'smartypants': 'HTML_SMARTYPANTS',
+        'toc': 'HTML_TOC',
+        'extra': ['EXT_FENCED_CODE', 'EXT_FOOTNOTES', 'EXT_TABLES']
+        }
+
--- a/piecrust/plugins/builtin.py	Wed Mar 23 01:53:57 2016 -0700
+++ b/piecrust/plugins/builtin.py	Wed Mar 23 08:35:51 2016 -0700
@@ -17,6 +17,7 @@
 from piecrust.commands.builtin.util import (
         InitCommand, PurgeCommand, ImportCommand)
 from piecrust.data.provider import (IteratorDataProvider, BlogDataProvider)
+from piecrust.formatting.hoedownformatter import HoedownFormatter
 from piecrust.formatting.markdownformatter import MarkdownFormatter
 from piecrust.formatting.textileformatter import TextileFormatter
 from piecrust.formatting.smartypantsformatter import SmartyPantsFormatter
@@ -98,6 +99,7 @@
 
     def getFormatters(self):
         return [
+                HoedownFormatter(),
                 MarkdownFormatter(),
                 SmartyPantsFormatter(),
                 TextileFormatter()]