mdx_liquid_tags.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  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 markdown
  11. import itertools
  12. import re
  13. import os
  14. from functools import wraps
  15. # Define some regular expressions
  16. LIQUID_TAG = re.compile(r'\{%.*?%\}')
  17. EXTRACT_TAG = re.compile(r'(?:\s*)(\S+)(?:\s*)')
  18. class _LiquidTagsPreprocessor(markdown.preprocessors.Preprocessor):
  19. _tags = {}
  20. def __init__(self, configs):
  21. self.configs = configs
  22. def run(self, lines):
  23. page = '\n'.join(lines)
  24. liquid_tags = LIQUID_TAG.findall(page)
  25. for i, markup in enumerate(liquid_tags):
  26. # remove {% %}
  27. markup = markup[2:-2]
  28. tag = EXTRACT_TAG.match(markup).groups()[0]
  29. markup = EXTRACT_TAG.sub('', markup, 1)
  30. if tag in self._tags:
  31. liquid_tags[i] = self._tags[tag](self, tag, markup)
  32. # add an empty string to liquid_tags so that chaining works
  33. liquid_tags.append('')
  34. # reconstruct string
  35. page = ''.join(itertools.chain(*zip(LIQUID_TAG.split(page),
  36. liquid_tags)))
  37. # resplit the lines
  38. return page.split("\n")
  39. class LiquidTags(markdown.Extension):
  40. """Wrapper for MDPreprocessor"""
  41. @classmethod
  42. def register(cls, tag):
  43. """Decorator to register a new include tag"""
  44. def dec(func):
  45. if tag in _LiquidTagsPreprocessor._tags:
  46. warnings.warn("Enhanced Markdown: overriding tag '%s'" % tag)
  47. _LiquidTagsPreprocessor._tags[tag] = func
  48. return func
  49. return dec
  50. def extendMarkdown(self, md, md_globals):
  51. self.htmlStash = md.htmlStash
  52. md.registerExtension(self)
  53. # for the include_code preprocessor, we need to re-run the
  54. # fenced code block preprocessor after substituting the code.
  55. # Because the fenced code processor is run before, {% %} tags
  56. # within equations will not be parsed as an include.
  57. md.preprocessors.add('mdincludes',
  58. _LiquidTagsPreprocessor(self), ">html_block")
  59. def makeExtension(configs=None):
  60. """Wrapper for a MarkDown extension"""
  61. return LiquidTags(configs=configs)