Add line numbers in `sphinx.ext.viewdoc` (#6319)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
benkrikler
2023-07-28 06:52:48 +02:00
committed by GitHub
parent 6178163cb1
commit 762ed8540a
4 changed files with 53 additions and 26 deletions

View File

@@ -24,6 +24,9 @@ Features added
in many more places.
* #5474: coverage: Print summary statistics tables.
Patch by Jorge Leitao.
* #6319: viewcode: Add :confval:`viewcode_line_numbers` to control
whether line numbers are added to rendered source code.
Patch by Ben Krikler.
Bugs fixed
----------

View File

@@ -73,6 +73,14 @@ Configuration
`epubcheck <https://github.com/IDPF/epubcheck>`_'s score
becomes worse even if the reader supports.
.. confval:: viewcode_line_numbers
Default: ``False``.
If set to ``True``, inline line numbers will be added to the highlighted code.
.. versionadded:: 7.2
.. event:: viewcode-find-source (app, modname)
.. versionadded:: 1.8

View File

@@ -253,7 +253,8 @@ def collect_pages(app: Sphinx) -> Generator[tuple[str, dict[str, Any], str], Non
lexer = env.config.highlight_language
else:
lexer = 'python'
highlighted = highlighter.highlight_block(code, lexer, linenos=False)
linenos = 'inline' * env.config.viewcode_line_numbers
highlighted = highlighter.highlight_block(code, lexer, linenos=linenos)
# split the code into lines
lines = highlighted.splitlines()
# split off wrap markup from the first line of the actual code
@@ -263,15 +264,16 @@ def collect_pages(app: Sphinx) -> Generator[tuple[str, dict[str, Any], str], Non
# now that we have code lines (starting at index 1), insert anchors for
# the collected tags (HACK: this only works if the tag boundaries are
# properly nested!)
maxindex = len(lines) - 1
max_index = len(lines) - 1
link_text = _('[docs]')
for name, docname in used.items():
type, start, end = tags[name]
backlink = urito(pagename, docname) + '#' + refname + '.' + name
lines[start] = (
'<div class="viewcode-block" id="%s"><a class="viewcode-back" '
'href="%s">%s</a>' % (name, backlink, _('[docs]')) +
lines[start])
lines[min(end, maxindex)] += '</div>'
lines[start] = (f'<div class="viewcode-block" id="{name}">\n'
f'<a class="viewcode-back" href="{backlink}">{link_text}</a>\n'
+ lines[start])
lines[min(end, max_index)] += '</div>\n'
# try to find parents (for submodules)
parents = []
parent = modname
@@ -327,6 +329,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_config_value('viewcode_import', None, False)
app.add_config_value('viewcode_enable_epub', False, False)
app.add_config_value('viewcode_follow_imported_members', True, False)
app.add_config_value('viewcode_line_numbers', False, 'env', (bool,))
app.connect('doctree-read', doctree_read)
app.connect('env-merge-info', env_merge_info)
app.connect('env-purge-doc', env_purge_doc)

View File

@@ -7,10 +7,7 @@ import pygments
import pytest
@pytest.mark.sphinx(testroot='ext-viewcode', freshenv=True)
def test_viewcode(app, status, warning):
app.builder.build_all()
def check_viewcode_output(app, warning):
warnings = re.sub(r'\\+', '/', warning.getvalue())
assert re.findall(
r"index.rst:\d+: WARNING: Object named 'func1' not found in include " +
@@ -32,23 +29,39 @@ def test_viewcode(app, status, warning):
assert result.count('this is the class attribute class_attr') == 2
result = (app.outdir / '_modules/spam/mod1.html').read_text(encoding='utf8')
result = re.sub('<span class=".*?">', '<span>', result) # filter pygments classes
result = re.sub('<span class="[^"]{,2}">', '<span>', result) # filter pygments classes
assert ('<div class="viewcode-block" id="Class1">\n'
'<a class="viewcode-back" href="../../index.html#spam.Class1">[docs]</a>\n') in result
assert '<span>@decorator</span>\n' in result
assert '<span>class</span> <span>Class1</span><span>:</span>\n' in result
if pygments.__version__ >= '2.14.0':
assert ('<div class="viewcode-block" id="Class1"><a class="viewcode-back" '
'href="../../index.html#spam.Class1">[docs]</a>'
'<span>@decorator</span>\n'
'<span>class</span> <span>Class1</span><span>:</span>\n'
'<span> </span><span>&quot;&quot;&quot;</span>\n'
'<span> this is Class1</span>\n'
'<span> &quot;&quot;&quot;</span></div>\n') in result
assert '<span> </span><span>&quot;&quot;&quot;</span>\n' in result
else:
assert ('<div class="viewcode-block" id="Class1"><a class="viewcode-back" '
'href="../../index.html#spam.Class1">[docs]</a>'
'<span>@decorator</span>\n'
'<span>class</span> <span>Class1</span><span>:</span>\n'
' <span>&quot;&quot;&quot;</span>\n'
'<span> this is Class1</span>\n'
'<span> &quot;&quot;&quot;</span></div>\n') in result
assert ' <span>&quot;&quot;&quot;</span>\n' in result
assert '<span> this is Class1</span>\n' in result
assert '<span> &quot;&quot;&quot;</span>\n' in result
return result
@pytest.mark.sphinx(testroot='ext-viewcode', freshenv=True,
confoverrides={"viewcode_line_numbers": True})
def test_viewcode_linenos(app, warning):
shutil.rmtree(app.outdir / '_modules', ignore_errors=True)
app.builder.build_all()
result = check_viewcode_output(app, warning)
assert '<span class="linenos"> 1</span>' in result
@pytest.mark.sphinx(testroot='ext-viewcode', freshenv=True,
confoverrides={"viewcode_line_numbers": False})
def test_viewcode(app, warning):
shutil.rmtree(app.outdir / '_modules', ignore_errors=True)
app.builder.build_all()
result = check_viewcode_output(app, warning)
assert 'class="linenos">' not in result
@pytest.mark.sphinx('epub', testroot='ext-viewcode')