Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add line numbers in sphinx.ext.viewdoc #6319

Merged
merged 15 commits into from
Jul 28, 2023
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------
Expand Down
8 changes: 8 additions & 0 deletions doc/usage/extensions/viewcode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 10 additions & 7 deletions sphinx/ext/viewcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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" '
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This <div> tag is used for highlighting the source code when user came from description:
スクリーンショット 2019-04-26 1 15 02

I think this removal disables the highlights. This is not a bug fix.

'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
Expand Down Expand Up @@ -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)
Expand Down
51 changes: 32 additions & 19 deletions tests/test_ext_viewcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
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 " +
Expand All @@ -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')
Expand Down
Loading