code_include.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import os.path
  4. from docutils import io, nodes, statemachine, utils
  5. from docutils.utils.error_reporting import SafeString, ErrorString
  6. from docutils.parsers.rst import directives, Directive
  7. from pelican.rstdirectives import Pygments
  8. """
  9. Include Pygments highlighted code with reStructuredText
  10. =======================================================
  11. :author: Colin Dunklau
  12. Use this plugin to make writing coding tutorials easier! You can
  13. maintain the example source files separately from the actual article.
  14. """
  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)