diff options
author | Jeppe Pihl <jpihl08@gmail.com> | 2014-10-06 20:31:30 +0200 |
---|---|---|
committer | Jeppe Pihl <jpihl08@gmail.com> | 2014-10-06 20:31:30 +0200 |
commit | f877c6532664cab5da69d0c8fedf08452522ad77 (patch) | |
tree | a6d1daae49b0a9a3c29cff1482eb63a4a7ed5175 | |
parent | 72d21037e99936a76d51aab4b6b7b88ba3144f20 (diff) | |
parent | 18ad1c1415db956e7a224bc34c665cb175324c50 (diff) | |
download | sphinx-f877c6532664cab5da69d0c8fedf08452522ad77.tar.gz |
merge
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | doc/markup/code.rst | 9 | ||||
-rw-r--r-- | sphinx/directives/code.py | 79 | ||||
-rw-r--r-- | tests/root/includes.txt | 16 | ||||
-rw-r--r-- | tests/roots/test-directive-code/lineno_match.rst | 17 | ||||
-rw-r--r-- | tests/roots/test-directive-code/lineno_start.rst | 6 | ||||
-rw-r--r-- | tests/roots/test-directive-code/linenos.rst | 6 | ||||
-rw-r--r-- | tests/test_directive_code.py | 74 |
9 files changed, 187 insertions, 23 deletions
@@ -41,6 +41,7 @@ Other contributors, listed alphabetically, are: * Christopher Perkins -- autosummary integration * Benjamin Peterson -- unittests * T. Powers -- HTML output improvements +* Jeppe Pihl -- literalinclude improvements * Rob Ruana -- napoleon extension * Stefan Seefeld -- toctree improvements * Shibukawa Yoshiki -- pluggable search API and Japanese search @@ -91,6 +91,8 @@ Features added Thanks to Takeshi Komiya. * #1344: add :confval:`gettext_enables` to enable extracting 'index' to gettext catalog output / applying translation catalog to generated documentation. +* #1583: Allow the line numbering of the directive `literalinclude` to match + that of the included file, using a new ``lineno-match`` option. * PR#299: add various options to sphinx-quickstart. Quiet mode option ``--quiet`` will skips wizard mode. Thanks to WAKAYAMA shirou. diff --git a/doc/markup/code.rst b/doc/markup/code.rst index b948dc38..9a503519 100644 --- a/doc/markup/code.rst +++ b/doc/markup/code.rst @@ -184,6 +184,10 @@ Includes string option, only lines that precede the first lines containing that string are included. + When specifying particular parts of a file to display, it can be useful to + display exactly which lines are being presented. + This can be done using the ``lineno-match`` option. + You can prepend and/or append a line to the included code, using the ``prepend`` and ``append`` option, respectively. This is useful e.g. for highlighting PHP code that doesn't include the ``<?php``/``?>`` markers. @@ -195,8 +199,8 @@ Includes .. literalinclude:: example.py :diff: example.py.orig - This shows the diff between example.py and example.py.orig with unified diff format. - + This shows the diff between example.py and example.py.orig with unified diff + format. .. versionadded:: 0.4.3 The ``encoding`` option. @@ -207,6 +211,7 @@ Includes The ``prepend`` and ``append`` options, as well as ``tab-width``. .. versionadded:: 1.3 The ``diff`` option. + The ``lineno-match`` option. Showing a file name diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index d63d710c..aee32fe3 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -146,6 +146,7 @@ class LiteralInclude(Directive): 'dedent': int, 'linenos': directives.flag, 'lineno-start': int, + 'lineno-match': directives.flag, 'tab-width': int, 'language': directives.unchanged_required, 'encoding': directives.encoding, @@ -163,8 +164,8 @@ class LiteralInclude(Directive): 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') + 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 @@ -194,6 +195,17 @@ class LiteralInclude(Directive): 'Cannot use both "pyobject" and "lines" options', line=self.lineno)] + if 'lineno-match' in self.options and 'lineno-start' in self.options: + return [document.reporter.warning( + 'Cannot use both "lineno-match" and "lineno-start"', + line=self.lineno)] + + if 'lineno-match' in self.options and \ + (set(['append', 'prepend']) & set(self.options.keys())): + return [document.reporter.warning( + 'Cannot use "lineno-match" and "append" or "prepend"', + line=self.lineno)] + encoding = self.options.get('encoding', env.config.source_encoding) codec_info = codecs.lookup(encoding) @@ -207,7 +219,7 @@ class LiteralInclude(Directive): tmp, fulldiffsource = env.relfn2path(diffsource) difflines = self.read_with_encoding(fulldiffsource, document, - codec_info, encoding) + codec_info, encoding) if not isinstance(difflines[0], string_types): return difflines diff = unified_diff( @@ -217,6 +229,7 @@ class LiteralInclude(Directive): self.arguments[0]) lines = list(diff) + linenostart = self.options.get('lineno-start', 1) objectname = self.options.get('pyobject') if objectname is not None: from sphinx.pycode import ModuleAnalyzer @@ -227,17 +240,30 @@ class LiteralInclude(Directive): 'Object named %r not found in include file %r' % (objectname, filename), line=self.lineno)] else: - lines = lines[tags[objectname][1]-1 : tags[objectname][2]-1] + lines = lines[tags[objectname][1]-1: tags[objectname][2]-1] + if 'lineno-match' in self.options: + linenostart = tags[objectname][1] linespec = self.options.get('lines') - if linespec is not None: + if linespec: try: linelist = parselinenos(linespec, len(lines)) except ValueError as err: return [document.reporter.warning(str(err), line=self.lineno)] - # just ignore nonexisting lines - nlines = len(lines) - lines = [lines[i] for i in linelist if i < nlines] + + if 'lineno-match' in self.options: + # make sure the line list is not "disjoint". + previous = linelist[0] + for line_number in linelist[1:]: + if line_number == previous + 1: + previous = line_number + continue + return [document.reporter.warning( + 'Cannot use "lineno-match" with a disjoint set of ' + '"lines"', line=self.lineno)] + linenostart = linelist[0] + 1 + # just ignore non-existing lines + lines = [lines[i] for i in linelist if i < len(lines)] if not lines: return [document.reporter.warning( 'Line spec %r: no lines pulled from include file %r' % @@ -253,43 +279,54 @@ class LiteralInclude(Directive): hl_lines = None startafter = self.options.get('start-after') - endbefore = self.options.get('end-before') - prepend = self.options.get('prepend') - append = self.options.get('append') + endbefore = self.options.get('end-before') if startafter is not None or endbefore is not None: use = not startafter res = [] - for line in lines: + for line_number, line in enumerate(lines): if not use and startafter and startafter in line: + if 'lineno-match' in self.options: + linenostart += line_number + 1 use = True elif use and endbefore and endbefore in line: - use = False break elif use: res.append(line) lines = res + if 'lineno-match' in self.options: + # handle that docutils remove preceding lines which only contains + # line separation. + for line in lines: + # check if line contains anything else than line separation. + if line and line.splitlines()[0]: + break + linenostart += 1 + + prepend = self.options.get('prepend') if prepend: - lines.insert(0, prepend + '\n') + lines.insert(0, prepend + '\n') + + append = self.options.get('append') if append: - lines.append(append + '\n') + lines.append(append + '\n') text = ''.join(lines) if self.options.get('tab-width'): 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 + if diffsource: # if diff is set, set udiff retnode['language'] = 'udiff' - if self.options.get('language', ''): + if 'language' in self.options: retnode['language'] = self.options['language'] retnode['linenos'] = 'linenos' in self.options or \ - 'lineno-start' in self.options + 'lineno-start' in self.options or \ + 'lineno-match' in self.options extra_args = retnode['highlight_args'] = {} if hl_lines is not None: extra_args['hl_lines'] = hl_lines - if 'lineno-start' in self.options: - extra_args['linenostart'] = self.options['lineno-start'] + extra_args['linenostart'] = linenostart env.note_dependency(rel_filename) caption = self.options.get('caption') @@ -303,7 +340,7 @@ class LiteralInclude(Directive): directives.register_directive('highlight', Highlight) -directives.register_directive('highlightlang', Highlight) # old +directives.register_directive('highlightlang', Highlight) # old directives.register_directive('code-block', CodeBlock) directives.register_directive('sourcecode', CodeBlock) directives.register_directive('literalinclude', LiteralInclude) diff --git a/tests/root/includes.txt b/tests/root/includes.txt index e84cec06..907b81e9 100644 --- a/tests/root/includes.txt +++ b/tests/root/includes.txt @@ -71,6 +71,22 @@ Literalinclude options :tab-width: 8 :language: python +.. cssclass:: inc-pyobj-lines-match +.. literalinclude:: literal.inc + :pyobject: Foo + :lineno-match: + +.. cssclass:: inc-lines-match +.. literalinclude:: literal.inc + :lines: 6-7,8 + :lineno-match: + +.. cssclass:: inc-startend-match +.. literalinclude:: literal.inc + :start-after: coding: utf-8 + :end-before: class Foo + :lineno-match: + Test if dedenting before parsing works. .. highlight:: python diff --git a/tests/roots/test-directive-code/lineno_match.rst b/tests/roots/test-directive-code/lineno_match.rst new file mode 100644 index 00000000..4e3b3835 --- /dev/null +++ b/tests/roots/test-directive-code/lineno_match.rst @@ -0,0 +1,17 @@ +Literal Includes with Line Numbers Matching +=========================================== + +.. literalinclude:: literal.inc + :language: python + :pyobject: Bar + :lineno-match: + +.. literalinclude:: literal.inc + :language: python + :lines: 5-6,7,8-9 + :lineno-match: + +.. literalinclude:: literal.inc + :language: python + :start-after: pass + :lineno-match: diff --git a/tests/roots/test-directive-code/lineno_start.rst b/tests/roots/test-directive-code/lineno_start.rst new file mode 100644 index 00000000..1beaabbf --- /dev/null +++ b/tests/roots/test-directive-code/lineno_start.rst @@ -0,0 +1,6 @@ +Literal Includes with Line Numbers Starting from 200 +==================================================== + +.. literalinclude:: literal.inc + :language: python + :lineno-start: 200 diff --git a/tests/roots/test-directive-code/linenos.rst b/tests/roots/test-directive-code/linenos.rst new file mode 100644 index 00000000..2f64498d --- /dev/null +++ b/tests/roots/test-directive-code/linenos.rst @@ -0,0 +1,6 @@ +Literal Includes with Line Numbers +================================== + +.. literalinclude:: literal.inc + :language: python + :linenos: diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py index e72c476f..651527c7 100644 --- a/tests/test_directive_code.py +++ b/tests/test_directive_code.py @@ -99,6 +99,80 @@ def test_literal_include_dedent(app, status, warning): @with_app('html', testroot='directive-code') +def test_literal_include_linenos(app, status, warning): + app.builder.build(['linenos']) + html = (app.outdir / 'linenos.html').text() + linenos = ( + '<td class="linenos"><div class="linenodiv"><pre>' + ' 1\n' + ' 2\n' + ' 3\n' + ' 4\n' + ' 5\n' + ' 6\n' + ' 7\n' + ' 8\n' + ' 9\n' + '10\n' + '11\n' + '12\n' + '13</pre></div></td>') + assert linenos in html + + +@with_app('html', testroot='directive-code') +def test_literal_include_lineno_start(app, status, warning): + app.builder.build(['lineno_start']) + html = (app.outdir / 'lineno_start.html').text() + linenos = ( + '<td class="linenos"><div class="linenodiv"><pre>' + '200\n' + '201\n' + '202\n' + '203\n' + '204\n' + '205\n' + '206\n' + '207\n' + '208\n' + '209\n' + '210\n' + '211\n' + '212</pre></div></td>') + assert linenos in html + + +@with_app('html', testroot='directive-code') +def test_literal_include_lineno_match(app, status, warning): + app.builder.build(['lineno_match']) + html = (app.outdir / 'lineno_match.html').text() + pyobject = ( + '<td class="linenos"><div class="linenodiv"><pre>' + ' 9\n' + '10\n' + '11</pre></div></td>') + + assert pyobject in html + + lines = ( + '<td class="linenos"><div class="linenodiv"><pre>' + '6\n' + '7\n' + '8\n' + '9</pre></div></td>') + assert lines in html + + start_after = ( + '<td class="linenos"><div class="linenodiv"><pre>' + ' 9\n' + '10\n' + '11\n' + '12\n' + '13</pre></div></td>') + assert start_after in html + + +@with_app('html', testroot='directive-code') def test_literalinclude_caption_html(app, status, warning): app.builder.build('index') html = (app.outdir / 'caption.html').text(encoding='utf-8') |