mdx_liquid_tags.py 2.5 KB

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