diff --git a/doc/markup/code.rst b/doc/markup/code.rst index 936080d6e..c1d9c25f4 100644 --- a/doc/markup/code.rst +++ b/doc/markup/code.rst @@ -180,6 +180,16 @@ Includes ``prepend`` and ``append`` option, respectively. This is useful e.g. for highlighting PHP code that doesn't include the ```` markers. + + If you want to show the diff of the code, you can specify the old + file by giving a ``diff`` option:: + + .. literalinclude:: example.py + :diff: example.py.orig + + This shows the diff between example.py and example.py.orig with unified diff format. + + .. versionadded:: 0.4.3 The ``encoding`` option. .. versionadded:: 0.6 @@ -187,6 +197,8 @@ Includes as well as support for absolute filenames. .. versionadded:: 1.0 The ``prepend`` and ``append`` options, as well as ``tab-width``. + .. versionadded:: 1.3 + The ``diff`` option. Showing a file name diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 7f08b8142..da7dc0caa 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -9,10 +9,13 @@ import sys import codecs +from difflib import unified_diff from docutils import nodes from docutils.parsers.rst import Directive, directives +from six import string_types + from sphinx import addnodes from sphinx.util import parselinenos from sphinx.util.nodes import set_source_info @@ -138,8 +141,30 @@ class LiteralInclude(Directive): 'append': directives.unchanged_required, 'emphasize-lines': directives.unchanged_required, 'filename': directives.unchanged, + 'diff': directives.unchanged_required, } + def read_with_encoding(self, filename, document, codec_info, encoding): + f = None + try: + f = codecs.StreamReaderWriter(open(filename, 'rb'), + codec_info[2], codec_info[3], 'strict') + lines = f.readlines() + lines = dedent_lines(lines, self.options.get('dedent')) + return lines + except (IOError, OSError): + return [document.reporter.warning( + 'Include file %r not found or reading it failed' % filename, + line=self.lineno)] + except UnicodeError: + return [document.reporter.warning( + 'Encoding %r used for reading included file %r seems to ' + 'be wrong, try giving an :encoding: option' % + (encoding, filename))] + finally: + if f is not None: + f.close() + def run(self): document = self.state.document if not document.settings.file_insertion_enabled: @@ -155,24 +180,26 @@ class LiteralInclude(Directive): encoding = self.options.get('encoding', env.config.source_encoding) codec_info = codecs.lookup(encoding) - f = None - try: - f = codecs.StreamReaderWriter(open(filename, 'rb'), - codec_info[2], codec_info[3], 'strict') - lines = f.readlines() - lines = dedent_lines(lines, self.options.get('dedent')) - except (IOError, OSError): - return [document.reporter.warning( - 'Include file %r not found or reading it failed' % filename, - line=self.lineno)] - except UnicodeError: - return [document.reporter.warning( - 'Encoding %r used for reading included file %r seems to ' - 'be wrong, try giving an :encoding: option' % - (encoding, filename))] - finally: - if f is not None: - f.close() + + lines = self.read_with_encoding(filename, document, + codec_info, encoding) + if not isinstance(lines[0], string_types): + return lines + + diffsource = self.options.get('diff') + if diffsource is not None: + tmp, fulldiffsource = env.relfn2path(diffsource) + + difflines = self.read_with_encoding(fulldiffsource, document, + codec_info, encoding) + if not isinstance(difflines[0], string_types): + return difflines + diff = unified_diff( + difflines, + lines, + diffsource, + self.arguments[0]) + lines = list(diff) objectname = self.options.get('pyobject') if objectname is not None: @@ -236,6 +263,8 @@ class LiteralInclude(Directive): text = text.expandtabs(self.options['tab-width']) retnode = nodes.literal_block(text, text, source=filename) set_source_info(self, retnode) + if diffsource is not None: # if diff is set, set udiff + retnode['language'] = 'udiff' if self.options.get('language', ''): retnode['language'] = self.options['language'] retnode['linenos'] = 'linenos' in self.options or \ diff --git a/tests/root/includes.txt b/tests/root/includes.txt index 089178119..2a0d8b709 100644 --- a/tests/root/includes.txt +++ b/tests/root/includes.txt @@ -58,6 +58,9 @@ Literalinclude options .. literalinclude:: literal.inc :end-before: class Foo +.. literalinclude:: literal.inc + :diff: literal.inc.orig + .. cssclass:: inc-tab3 .. literalinclude:: tabs.inc :tab-width: 3 diff --git a/tests/root/literal.inc.orig b/tests/root/literal.inc.orig new file mode 100644 index 000000000..14fd214c4 --- /dev/null +++ b/tests/root/literal.inc.orig @@ -0,0 +1,13 @@ +# Literally included file using Python highlighting +# -*- coding: utf-8 -*- + +foo = "Including Unicode characters: üöä" # This will be changed + +class FooOrig: + pass + +class BarOrig: + def baz(): + pass + +def bar(): pass