mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merged in jpihl/sphinx-fork (pull request #301)
#1583: Allow the line numbering of the directive `literalinclude` to match that of the included file, using a new ``lineno-match`` option.
This commit is contained in:
commit
5d12c20d2a
1
AUTHORS
1
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
|
||||
|
2
CHANGES
2
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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
17
tests/roots/test-directive-code/lineno_match.rst
Normal file
17
tests/roots/test-directive-code/lineno_match.rst
Normal file
@ -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:
|
6
tests/roots/test-directive-code/lineno_start.rst
Normal file
6
tests/roots/test-directive-code/lineno_start.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Literal Includes with Line Numbers Starting from 200
|
||||
====================================================
|
||||
|
||||
.. literalinclude:: literal.inc
|
||||
:language: python
|
||||
:lineno-start: 200
|
6
tests/roots/test-directive-code/linenos.rst
Normal file
6
tests/roots/test-directive-code/linenos.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Literal Includes with Line Numbers
|
||||
==================================
|
||||
|
||||
.. literalinclude:: literal.inc
|
||||
:language: python
|
||||
:linenos:
|
@ -98,6 +98,80 @@ def test_literal_include_dedent(app, status, warning):
|
||||
assert blocks[5].text == '\n\n' # dedent: 1000
|
||||
|
||||
|
||||
@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')
|
||||
|
Loading…
Reference in New Issue
Block a user