comparison piecrust/formatting/hoedownformatter.py @ 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
children 0c688063890f
comparison
equal deleted inserted replaced
693:d2a87365b85b 694:b917ae071994
1 import logging
2 import hoedown
3 from piecrust.formatting.base import Formatter
4
5
6 logger = logging.getLogger(__name__)
7
8
9 class HoedownFormatter(Formatter):
10 FORMAT_NAMES = ['hoedown']
11 OUTPUT_FORMAT = 'html'
12
13 def __init__(self):
14 super(HoedownFormatter, self).__init__()
15 self._formatter = None
16
17 def render(self, format_name, txt):
18 assert format_name in self.FORMAT_NAMES
19 self._ensureInitialized()
20 return self._formatter.render(txt)
21
22 def _ensureInitialized(self):
23 if self._formatter is not None:
24 return
25
26 # Don't show warnings once for each worker when baking, so only
27 # show them for the first. If the variable is not set, we're not
28 # baking so do show them either way.
29 show_warnings = (self.app.config.get('baker/worker_id', 0) == 0)
30
31 config = self.app.config.get('hoedown')
32 if config is None:
33 config = {}
34 elif not isinstance(config, dict):
35 raise Exception("The `hoedown` configuration setting must be "
36 "a dictionary.")
37
38 extensions = config.get('extensions', [])
39 if isinstance(extensions, str):
40 extensions = [e.strip() for e in extensions.split(',')]
41 # Compatibility with PieCrust 1.x
42 if config.get('use_markdown_extra'):
43 extensions.append('extra')
44
45 render_flags = config.get('render_flags')
46 if render_flags is None:
47 render_flags = []
48
49 # Translate standard Markdown formatter extensions to Hoedown
50 # extension/render flags to make it easier to use Hoedown as a drop-in
51 # replacement.
52 exts = 0
53 rdrf = 0
54 other = 0
55 for n in extensions:
56 # Try an extension?
57 e = getattr(hoedown, 'EXT_' + n.upper(), None)
58 if e is not None:
59 exts |= e
60 continue
61
62 # Try a render flag?
63 f = getattr(hoedown, 'HTML_' + n.upper(), None)
64 if f is not None:
65 rdrf |= f
66
67 # Other flag?
68 f = getattr(hoedown, 'TABLE_' + n.upper(), None)
69 if f is not None:
70 other |= f
71
72 # Try translating from a Markdown extension name.
73 t = ext_translate.get(n)
74 if t is None:
75 if show_warnings:
76 logger.warning("Unknown Hoedown Markdown extension or flag: "
77 "%s" % n)
78 continue
79 if not isinstance(t, list):
80 t = [t]
81 for i in t:
82 if i.startswith('EXT_'):
83 exts |= getattr(hoedown, i)
84 elif i.startswith('HTML_'):
85 rdrf |= getattr(hoedown, i)
86 elif show_warnings:
87 logger.warning("Unknown Hoedown Markdown extension or flag:"
88 "%s" % n)
89 if n == 'extra' and show_warnings:
90 # Special warning for the 'extra' extension.
91 logger.warning(
92 "The 'extra' extension doesn't have a full equivalent "
93 "in Hoedown Markdown. Only 'fenced_code', 'footnotes' and "
94 "'tables' extensions will be active. "
95 "To remove this warning, replace 'extra' with those 3 "
96 "specific extensions.")
97
98 # Enable a few things by default.
99 exts |= hoedown.EXT_NO_INTRA_EMPHASIS
100
101 renderer = hoedown.HtmlRenderer(flags=rdrf)
102 self._formatter = hoedown.Markdown(
103 renderer, extensions=(exts | other))
104
105
106 ext_translate = {
107 'fenced_code': 'EXT_FENCED_CODE',
108 'footnotes': 'EXT_FOOTNOTES',
109 'tables': 'EXT_TABLES',
110 'nl2br': 'HTML_HARD_WRAP',
111 'smarty': 'HTML_SMARTYPANTS',
112 'smartypants': 'HTML_SMARTYPANTS',
113 'toc': 'HTML_TOC',
114 'extra': ['EXT_FENCED_CODE', 'EXT_FOOTNOTES', 'EXT_TABLES']
115 }
116