summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeppe Pihl <jpihl08@gmail.com>2014-10-06 10:35:33 +0200
committerJeppe Pihl <jpihl08@gmail.com>2014-10-06 10:35:33 +0200
commitbd0b5f19426148f0ead0c70e564d1b301da7fc53 (patch)
tree2ce09422e261f3e9b2e466cd0df97b298bca3a34
parenta05d4b0f6f9928c5760130dfdf52b3e59cc50e76 (diff)
parent376f62970ff7b9276c77ef74e915bced88f5a212 (diff)
downloadsphinx-bd0b5f19426148f0ead0c70e564d1b301da7fc53.tar.gz
merge
-rw-r--r--AUTHORS1
-rw-r--r--CHANGES2
-rw-r--r--doc/markup/code.rst9
-rw-r--r--sphinx/directives/code.py77
-rw-r--r--tests/root/includes.txt16
-rw-r--r--tests/roots/test-directive-code/lineno_match.rst17
-rw-r--r--tests/roots/test-directive-code/lineno_start.rst6
-rw-r--r--tests/roots/test-directive-code/linenos.rst6
-rw-r--r--tests/test_directive_code.py78
9 files changed, 188 insertions, 24 deletions
diff --git a/AUTHORS b/AUTHORS
index ce4f4bf0..85fbbe74 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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
diff --git a/CHANGES b/CHANGES
index 0d768936..8bf02b3c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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..90be1557 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,52 @@ 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 + 2
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 preceding, empty lines ('\n') are removed.
+ for line in lines[linenostart-1:]:
+ if line != '\n':
+ 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 +338,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..f1e9cf15
--- /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: 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 3f0a6d3f..c4fb829d 100644
--- a/tests/test_directive_code.py
+++ b/tests/test_directive_code.py
@@ -96,10 +96,86 @@ 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()
- caption = '<div class="code-block-caption">caption <strong>test</strong> py</div>'
+ caption = ('<div class="code-block-caption">'
+ 'caption <strong>test</strong> py'
+ '</div>')
assert caption in html