include_code.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. """
  2. Include Code Tag
  3. ----------------
  4. This implements a Liquid-style video tag for Pelican,
  5. based on the octopress video tag [1]_
  6. Syntax
  7. ------
  8. {% include_code path/to/code [lang:python] [Title text] [codec:utf8] %}
  9. The "path to code" is specified relative to the ``code`` subdirectory of
  10. the content directory Optionally, this subdirectory can be specified in the
  11. config file:
  12. CODE_DIR = 'code'
  13. If your input file is not ASCII/UTF-8 encoded, you need to specify the
  14. appropriate input codec by using the ``codec`` option.
  15. Example ``codec:iso-8859-1``
  16. Using this option does not affect the output encoding.
  17. For a list of valid codec identifiers, see
  18. https://docs.python.org/2/library/codecs.html#standard-encodings
  19. Example
  20. -------
  21. {% include_code myscript.py %}
  22. This will import myscript.py from content/code/myscript.py
  23. and output the contents in a syntax highlighted code block inside a figure,
  24. with a figcaption listing the file name and download link.
  25. The file link will be valid only if the 'code' directory is listed
  26. in the STATIC_PATHS setting, e.g.:
  27. STATIC_PATHS = ['images', 'code']
  28. [1] https://github.com/imathis/octopress/blob/master/plugins/include_code.rb
  29. """
  30. import re
  31. import os
  32. from .mdx_liquid_tags import LiquidTags
  33. SYNTAX = "{% include_code /path/to/code.py [lang:python] [lines:X-Y] [:hidefilename:] [title] %}"
  34. FORMAT = re.compile(r"""
  35. ^(?:\s+)? # Allow whitespace at beginning
  36. (?P<src>\S+) # Find the path
  37. (?:\s+)? # Whitespace
  38. (?:(?:lang:)(?P<lang>\S+))? # Optional language
  39. (?:\s+)? # Whitespace
  40. (?:(?:lines:)(?P<lines>\d+-\d+))? # Optional lines
  41. (?:\s+)? # Whitespace
  42. (?P<hidefilename>:hidefilename:)? # Hidefilename flag
  43. (?:\s+)? # Whitespace
  44. (?:(?:codec:)(?P<codec>\S+))? # Optional language
  45. (?:\s+)? # Whitespace
  46. (?P<title>.+)?$ # Optional title
  47. """, re.VERBOSE)
  48. @LiquidTags.register('include_code')
  49. def include_code(preprocessor, tag, markup):
  50. title = None
  51. lang = None
  52. src = None
  53. match = FORMAT.search(markup)
  54. if match:
  55. argdict = match.groupdict()
  56. title = argdict['title'] or ""
  57. lang = argdict['lang']
  58. codec = argdict['codec'] or "utf8"
  59. lines = argdict['lines']
  60. hide_filename = bool(argdict['hidefilename'])
  61. if lines:
  62. first_line, last_line = map(int, lines.split("-"))
  63. src = argdict['src']
  64. if not src:
  65. raise ValueError("Error processing input, "
  66. "expected syntax: {0}".format(SYNTAX))
  67. code_dir = preprocessor.configs.getConfig('CODE_DIR')
  68. code_path = os.path.join('content', code_dir, src)
  69. if not os.path.exists(code_path):
  70. raise ValueError("File {0} could not be found".format(code_path))
  71. with open(code_path) as fh:
  72. if lines:
  73. code = fh.readlines()[first_line - 1: last_line]
  74. code[-1] = code[-1].rstrip()
  75. code = "".join(code)
  76. else:
  77. code = fh.read()
  78. if not title and hide_filename:
  79. raise ValueError("Either title must be specified or filename must "
  80. "be available")
  81. if not hide_filename:
  82. title += " %s" % os.path.basename(src)
  83. if lines:
  84. title += " [Lines %s]" % lines
  85. title = title.strip()
  86. url = '/{0}/{1}'.format(code_dir, src)
  87. url = re.sub('/+', '/', url)
  88. open_tag = ("<figure class='code'>\n<figcaption><span>{title}</span> "
  89. "<a href='{url}'>download</a></figcaption>".format(title=title,
  90. url=url))
  91. close_tag = "</figure>"
  92. # store HTML tags in the stash. This prevents them from being
  93. # modified by markdown.
  94. open_tag = preprocessor.configs.htmlStash.store(open_tag, safe=True)
  95. close_tag = preprocessor.configs.htmlStash.store(close_tag, safe=True)
  96. if lang:
  97. lang_include = ':::' + lang + '\n '
  98. else:
  99. lang_include = ''
  100. source = (open_tag
  101. + '\n\n '
  102. + lang_include
  103. + '\n '.join(code.decode(codec).split('\n')) + '\n\n'
  104. + close_tag + '\n')
  105. return source
  106. #----------------------------------------------------------------------
  107. # This import allows image tag to be a Pelican plugin
  108. from liquid_tags import register