code_include.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. """
  4. Include Pygments highlighted code with reStructuredText
  5. =======================================================
  6. :author: Colin Dunklau
  7. Use this plugin to make writing coding tutorials easier! You can
  8. maintain the example source files separately from the actual article.
  9. """
  10. import os.path
  11. from docutils import io, nodes, statemachine, utils
  12. from docutils.utils.error_reporting import SafeString, ErrorString
  13. from docutils.parsers.rst import directives, Directive
  14. from pelican.rstdirectives import Pygments
  15. class CodeInclude(Directive):
  16. """
  17. Include content read from a separate source file, and highlight
  18. it with the given lexer (using pelican.rstdirectives.CodeBlock)
  19. The encoding of the included file can be specified. Only a part
  20. of the given file argument may be included by specifying start
  21. and end line. Hard tabs will be replaced with ``tab-width``
  22. spaces.
  23. """
  24. required_arguments = 1
  25. optional_arguments = 0
  26. final_argument_whitespace = True
  27. option_spec = {'lexer': directives.unchanged,
  28. 'encoding': directives.encoding,
  29. 'tab-width': int,
  30. 'start-line': int,
  31. 'end-line': int}
  32. def run(self):
  33. """Include a file as part of the content of this reST file."""
  34. if not self.state.document.settings.file_insertion_enabled:
  35. raise self.warning('"%s" directive disabled.' % self.name)
  36. source = self.state_machine.input_lines.source(
  37. self.lineno - self.state_machine.input_offset - 1)
  38. source_dir = os.path.dirname(os.path.abspath(source))
  39. path = directives.path(self.arguments[0])
  40. path = os.path.normpath(os.path.join(source_dir, path))
  41. path = utils.relative_path(None, path)
  42. path = nodes.reprunicode(path)
  43. encoding = self.options.get(
  44. 'encoding', self.state.document.settings.input_encoding)
  45. e_handler = self.state.document.settings.input_encoding_error_handler
  46. tab_width = self.options.get(
  47. 'tab-width', self.state.document.settings.tab_width)
  48. try:
  49. self.state.document.settings.record_dependencies.add(path)
  50. include_file = io.FileInput(source_path=path,
  51. encoding=encoding,
  52. error_handler=e_handler)
  53. except UnicodeEncodeError as error:
  54. raise self.severe('Problems with "%s" directive path:\n'
  55. 'Cannot encode input file path "%s" '
  56. '(wrong locale?).' %
  57. (self.name, SafeString(path)))
  58. except IOError as error:
  59. raise self.severe('Problems with "%s" directive path:\n%s.' %
  60. (self.name, ErrorString(error)))
  61. startline = self.options.get('start-line', None)
  62. endline = self.options.get('end-line', None)
  63. try:
  64. if startline or (endline is not None):
  65. lines = include_file.readlines()
  66. rawtext = ''.join(lines[startline:endline])
  67. else:
  68. rawtext = include_file.read()
  69. except UnicodeError as error:
  70. raise self.severe('Problem with "%s" directive:\n%s' %
  71. (self.name, ErrorString(error)))
  72. include_lines = statemachine.string2lines(rawtext, tab_width,
  73. convert_whitespace=True)
  74. # default lexer to 'text'
  75. lexer = self.options.get('lexer', 'text')
  76. self.options['source'] = path
  77. codeblock = Pygments(self.name,
  78. [lexer], # arguments
  79. {}, # no options for this directive
  80. include_lines, # content
  81. self.lineno,
  82. self.content_offset,
  83. self.block_text,
  84. self.state,
  85. self.state_machine)
  86. return codeblock.run()
  87. def register():
  88. directives.register_directive('code-include', CodeInclude)