mdx_liquid_tags.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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. 'GIPHY_API_KEY': 'giphy'
  23. }
  24. LT_HELP = { 'CODE_DIR' : 'Code directory for include_code subplugin',
  25. 'NOTEBOOK_DIR' : 'Notebook directory for notebook subplugin',
  26. 'FLICKR_API_KEY': 'Flickr key for accessing the API',
  27. 'GIPHY_API_KEY': 'Giphy key for accessing the API'
  28. }
  29. class _LiquidTagsPreprocessor(markdown.preprocessors.Preprocessor):
  30. _tags = {}
  31. def __init__(self, configs):
  32. self.configs = configs
  33. def run(self, lines):
  34. page = '\n'.join(lines)
  35. liquid_tags = LIQUID_TAG.findall(page)
  36. for i, markup in enumerate(liquid_tags):
  37. # remove {% %}
  38. markup = markup[2:-2]
  39. tag = EXTRACT_TAG.match(markup).groups()[0]
  40. markup = EXTRACT_TAG.sub('', markup, 1)
  41. if tag in self._tags:
  42. liquid_tags[i] = self._tags[tag](self, tag, markup.strip())
  43. # add an empty string to liquid_tags so that chaining works
  44. liquid_tags.append('')
  45. # reconstruct string
  46. page = ''.join(itertools.chain(*zip(LIQUID_TAG.split(page),
  47. liquid_tags)))
  48. # resplit the lines
  49. return page.split("\n")
  50. class LiquidTags(markdown.Extension):
  51. """Wrapper for MDPreprocessor"""
  52. def __init__(self, config):
  53. try:
  54. # Needed for markdown versions >= 2.5
  55. for key,value in LT_CONFIG.items():
  56. self.config[key] = [value,LT_HELP[key]]
  57. super(LiquidTags,self).__init__(**config)
  58. except AttributeError:
  59. # Markdown versions < 2.5
  60. for key,value in LT_CONFIG.items():
  61. config[key] = [config[key],LT_HELP[key]]
  62. super(LiquidTags,self).__init__(config)
  63. @classmethod
  64. def register(cls, tag):
  65. """Decorator to register a new include tag"""
  66. def dec(func):
  67. if tag in _LiquidTagsPreprocessor._tags:
  68. warnings.warn("Enhanced Markdown: overriding tag '%s'" % tag)
  69. _LiquidTagsPreprocessor._tags[tag] = func
  70. return func
  71. return dec
  72. def extendMarkdown(self, md, md_globals):
  73. self.htmlStash = md.htmlStash
  74. md.registerExtension(self)
  75. # for the include_code preprocessor, we need to re-run the
  76. # fenced code block preprocessor after substituting the code.
  77. # Because the fenced code processor is run before, {% %} tags
  78. # within equations will not be parsed as an include.
  79. md.preprocessors.add('mdincludes',
  80. _LiquidTagsPreprocessor(self), ">html_block")
  81. def makeExtension(configs=None):
  82. """Wrapper for a MarkDown extension"""
  83. return LiquidTags(configs=configs)