include_code.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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. import sys
  33. from .mdx_liquid_tags import LiquidTags
  34. SYNTAX = "{% include_code /path/to/code.py [lang:python] [lines:X-Y] [:hidefilename:] [title] %}"
  35. FORMAT = re.compile(r"""
  36. ^(?:\s+)? # Allow whitespace at beginning
  37. (?P<src>\S+) # Find the path
  38. (?:\s+)? # Whitespace
  39. (?:(?:lang:)(?P<lang>\S+))? # Optional language
  40. (?:\s+)? # Whitespace
  41. (?:(?:lines:)(?P<lines>\d+-\d+))? # Optional lines
  42. (?:\s+)? # Whitespace
  43. (?P<hidefilename>:hidefilename:)? # Hidefilename flag
  44. (?:\s+)? # Whitespace
  45. (?:(?:codec:)(?P<codec>\S+))? # Optional language
  46. (?:\s+)? # Whitespace
  47. (?P<title>.+)?$ # Optional title
  48. """, re.VERBOSE)
  49. @LiquidTags.register('include_code')
  50. def include_code(preprocessor, tag, markup):
  51. title = None
  52. lang = None
  53. src = None
  54. match = FORMAT.search(markup)
  55. if match:
  56. argdict = match.groupdict()
  57. title = argdict['title'] or ""
  58. lang = argdict['lang']
  59. codec = argdict['codec'] or "utf8"
  60. lines = argdict['lines']
  61. hide_filename = bool(argdict['hidefilename'])
  62. if lines:
  63. first_line, last_line = map(int, lines.split("-"))
  64. src = argdict['src']
  65. if not src:
  66. raise ValueError("Error processing input, "
  67. "expected syntax: {0}".format(SYNTAX))
  68. code_dir = preprocessor.configs.getConfig('CODE_DIR')
  69. code_path = os.path.join('content', code_dir, src)
  70. if not os.path.exists(code_path):
  71. raise ValueError("File {0} could not be found".format(code_path))
  72. with open(code_path) as fh:
  73. if lines:
  74. code = fh.readlines()[first_line - 1: last_line]
  75. code[-1] = code[-1].rstrip()
  76. code = "".join(code)
  77. else:
  78. code = fh.read()
  79. if not title and hide_filename:
  80. raise ValueError("Either title must be specified or filename must "
  81. "be available")
  82. if not hide_filename:
  83. title += " %s" % os.path.basename(src)
  84. if lines:
  85. title += " [Lines %s]" % lines
  86. title = title.strip()
  87. url = '/{0}/{1}'.format(code_dir, src)
  88. url = re.sub('/+', '/', url)
  89. open_tag = ("<figure class='code'>\n<figcaption><span>{title}</span> "
  90. "<a href='{url}'>download</a></figcaption>".format(title=title,
  91. url=url))
  92. close_tag = "</figure>"
  93. # store HTML tags in the stash. This prevents them from being
  94. # modified by markdown.
  95. open_tag = preprocessor.configs.htmlStash.store(open_tag, safe=True)
  96. close_tag = preprocessor.configs.htmlStash.store(close_tag, safe=True)
  97. if lang:
  98. lang_include = ':::' + lang + '\n '
  99. else:
  100. lang_include = ''
  101. if sys.version_info[0] < 3:
  102. source = (open_tag
  103. + '\n\n '
  104. + lang_include
  105. + '\n '.join(code.decode(codec).split('\n')) + '\n\n'
  106. + close_tag + '\n')
  107. else:
  108. source = (open_tag
  109. + '\n\n '
  110. + lang_include
  111. + '\n '.join(code.split('\n')) + '\n\n'
  112. + close_tag + '\n')
  113. return source
  114. #----------------------------------------------------------------------
  115. # This import allows image tag to be a Pelican plugin
  116. from liquid_tags import register