mdx_liquid_tags.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. """
  2. Markdown Extension for Liquid-style Tags
  3. ----------------------------------------
  4. A markdown extension to allow user-defined tags of the form::
  5. {% tag arg1 arg2 ... argn %}
  6. Where "tag" is associated with some user-defined extension.
  7. These result in a preprocess step within markdown that produces
  8. either markdown or html.
  9. """
  10. import warnings
  11. import markdown
  12. import itertools
  13. import re
  14. import os
  15. from functools import wraps
  16. # Define some regular expressions
  17. LIQUID_TAG = re.compile(r'\{%.*?%\}', re.MULTILINE | re.DOTALL)
  18. EXTRACT_TAG = re.compile(r'(?:\s*)(\S+)(?:\s*)')
  19. LT_CONFIG = { 'CODE_DIR': 'code',
  20. 'NOTEBOOK_DIR': 'notebooks',
  21. 'FLICKR_API_KEY': 'flickr'
  22. }
  23. LT_HELP = { 'CODE_DIR' : 'Code directory for include_code subplugin',
  24. 'NOTEBOOK_DIR' : 'Notebook directory for notebook subplugin',
  25. 'FLICKR_API_KEY': 'Flickr key for accessing the API'
  26. }
  27. class _LiquidTagsPreprocessor(markdown.preprocessors.Preprocessor):
  28. _tags = {}
  29. def __init__(self, configs):
  30. self.configs = configs
  31. def run(self, lines):
  32. page = '\n'.join(lines)
  33. liquid_tags = LIQUID_TAG.findall(page)
  34. for i, markup in enumerate(liquid_tags):
  35. # remove {% %}
  36. markup = markup[2:-2]
  37. tag = EXTRACT_TAG.match(markup).groups()[0]
  38. markup = EXTRACT_TAG.sub('', markup, 1)
  39. if tag in self._tags:
  40. liquid_tags[i] = self._tags[tag](self, tag, markup.strip())
  41. # add an empty string to liquid_tags so that chaining works
  42. liquid_tags.append('')
  43. # reconstruct string
  44. page = ''.join(itertools.chain(*zip(LIQUID_TAG.split(page),
  45. liquid_tags)))
  46. # resplit the lines
  47. return page.split("\n")
  48. class LiquidTags(markdown.Extension):
  49. """Wrapper for MDPreprocessor"""
  50. def __init__(self, config):
  51. try:
  52. # Needed for markdown versions >= 2.5
  53. for key,value in LT_CONFIG.items():
  54. self.config[key] = [value,LT_HELP[key]]
  55. super(LiquidTags,self).__init__(**config)
  56. except AttributeError:
  57. # Markdown versions < 2.5
  58. for key,value in LT_CONFIG.items():
  59. config[key] = [config[key],LT_HELP[key]]
  60. super(LiquidTags,self).__init__(config)
  61. @classmethod
  62. def register(cls, tag):
  63. """Decorator to register a new include tag"""
  64. def dec(func):
  65. if tag in _LiquidTagsPreprocessor._tags:
  66. warnings.warn("Enhanced Markdown: overriding tag '%s'" % tag)
  67. _LiquidTagsPreprocessor._tags[tag] = func
  68. return func
  69. return dec
  70. def extendMarkdown(self, md, md_globals):
  71. self.htmlStash = md.htmlStash
  72. md.registerExtension(self)
  73. # for the include_code preprocessor, we need to re-run the
  74. # fenced code block preprocessor after substituting the code.
  75. # Because the fenced code processor is run before, {% %} tags
  76. # within equations will not be parsed as an include.
  77. md.preprocessors.add('mdincludes',
  78. _LiquidTagsPreprocessor(self), ">html_block")
  79. def makeExtension(configs=None):
  80. """Wrapper for a MarkDown extension"""
  81. return LiquidTags(configs=configs)