mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #8707 from tk0miya/8704_viewcode_not_working
Fix #8704: viewcode: anchors are generated in incremental build
This commit is contained in:
commit
d5bc970dd9
2
CHANGES
2
CHANGES
@ -10,6 +10,7 @@ Incompatible changes
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* pending_xref node for viewcode extension
|
||||
* ``sphinx.builders.linkcheck.CheckExternalLinksBuilder.broken``
|
||||
* ``sphinx.builders.linkcheck.CheckExternalLinksBuilder.good``
|
||||
* ``sphinx.builders.linkcheck.CheckExternalLinksBuilder.redirected``
|
||||
@ -69,6 +70,7 @@ Bugs fixed
|
||||
* #8094: texinfo: image files on the different directory with document are not
|
||||
copied
|
||||
* #8720: viewcode: module pages are generated for epub on incremental build
|
||||
* #8704: viewcode: anchors are generated in incremental build after singlehtml
|
||||
* #8671: :confval:`highlight_options` is not working
|
||||
* #8341: C, fix intersphinx lookup types for names in declarations.
|
||||
* C, C++: in general fix intersphinx and role lookup types.
|
||||
|
@ -26,6 +26,11 @@ The following is a list of deprecated interfaces.
|
||||
- (will be) Removed
|
||||
- Alternatives
|
||||
|
||||
* - pending_xref node for viewcode extension
|
||||
- 3.5
|
||||
- 5.0
|
||||
- ``sphinx.ext.viewcode.viewcode_anchor``
|
||||
|
||||
* - ``sphinx.builders.linkcheck.CheckExternalLinksBuilder.broken``
|
||||
- 3.5
|
||||
- 5.0
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
import posixpath
|
||||
import traceback
|
||||
import warnings
|
||||
from os import path
|
||||
from typing import Any, Dict, Generator, Iterable, Optional, Set, Tuple, cast
|
||||
|
||||
@ -19,10 +20,13 @@ from docutils.nodes import Element, Node
|
||||
import sphinx
|
||||
from sphinx import addnodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.pycode import ModuleAnalyzer
|
||||
from sphinx.transforms.post_transforms import SphinxPostTransform
|
||||
from sphinx.util import get_full_modname, logging, status_iterator
|
||||
from sphinx.util.nodes import make_refnode
|
||||
|
||||
@ -32,6 +36,15 @@ logger = logging.getLogger(__name__)
|
||||
OUTPUT_DIRNAME = '_modules'
|
||||
|
||||
|
||||
class viewcode_anchor(Element):
|
||||
"""Node for viewcode anchors.
|
||||
|
||||
This node will be processed in the resolving phase.
|
||||
For viewcode supported builders, they will be all converted to the anchors.
|
||||
For not supported builders, they will be removed.
|
||||
"""
|
||||
|
||||
|
||||
def _get_full_modname(app: Sphinx, modname: str, attribute: str) -> Optional[str]:
|
||||
try:
|
||||
return get_full_modname(modname, attribute)
|
||||
@ -50,14 +63,21 @@ def _get_full_modname(app: Sphinx, modname: str, attribute: str) -> Optional[str
|
||||
return None
|
||||
|
||||
|
||||
def is_supported_builder(builder: Builder) -> bool:
|
||||
if builder.format != 'html':
|
||||
return False
|
||||
elif builder.name == 'singlehtml':
|
||||
return False
|
||||
elif builder.name.startswith('epub') and not builder.config.viewcode_enable_epub:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def doctree_read(app: Sphinx, doctree: Node) -> None:
|
||||
env = app.builder.env
|
||||
if not hasattr(env, '_viewcode_modules'):
|
||||
env._viewcode_modules = {} # type: ignore
|
||||
if app.builder.name == "singlehtml":
|
||||
return
|
||||
if app.builder.name.startswith("epub") and not env.config.viewcode_enable_epub:
|
||||
return
|
||||
|
||||
def has_tag(modname: str, fullname: str, docname: str, refname: str) -> bool:
|
||||
entry = env._viewcode_modules.get(modname, None) # type: ignore
|
||||
@ -115,12 +135,7 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
|
||||
continue
|
||||
names.add(fullname)
|
||||
pagename = posixpath.join(OUTPUT_DIRNAME, modname.replace('.', '/'))
|
||||
inline = nodes.inline('', _('[source]'), classes=['viewcode-link'])
|
||||
onlynode = addnodes.only(expr='html')
|
||||
onlynode += addnodes.pending_xref('', inline, reftype='viewcode', refdomain='std',
|
||||
refexplicit=False, reftarget=pagename,
|
||||
refid=fullname, refdoc=env.docname)
|
||||
signode += onlynode
|
||||
signode += viewcode_anchor(reftarget=pagename, refid=fullname, refdoc=env.docname)
|
||||
|
||||
|
||||
def env_merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
||||
@ -134,10 +149,34 @@ def env_merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
||||
env._viewcode_modules.update(other._viewcode_modules) # type: ignore
|
||||
|
||||
|
||||
class ViewcodeAnchorTransform(SphinxPostTransform):
|
||||
"""Convert or remove viewcode_anchor nodes depends on builder."""
|
||||
default_priority = 100
|
||||
|
||||
def run(self, **kwargs: Any) -> None:
|
||||
if is_supported_builder(self.app.builder):
|
||||
self.convert_viewcode_anchors()
|
||||
else:
|
||||
self.remove_viewcode_anchors()
|
||||
|
||||
def convert_viewcode_anchors(self) -> None:
|
||||
for node in self.document.traverse(viewcode_anchor):
|
||||
anchor = nodes.inline('', _('[source]'), classes=['viewcode-link'])
|
||||
refnode = make_refnode(self.app.builder, node['refdoc'], node['reftarget'],
|
||||
node['refid'], anchor)
|
||||
node.replace_self(refnode)
|
||||
|
||||
def remove_viewcode_anchors(self) -> None:
|
||||
for node in self.document.traverse(viewcode_anchor):
|
||||
node.parent.remove(node)
|
||||
|
||||
|
||||
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: Node
|
||||
) -> Optional[Node]:
|
||||
# resolve our "viewcode" reference nodes -- they need special treatment
|
||||
if node['reftype'] == 'viewcode':
|
||||
warnings.warn('viewcode extension is no longer use pending_xref node. '
|
||||
'Please update your extension.', RemovedInSphinx50Warning)
|
||||
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
||||
node['refid'], contnode)
|
||||
|
||||
@ -182,9 +221,7 @@ def collect_pages(app: Sphinx) -> Generator[Tuple[str, Dict[str, Any], str], Non
|
||||
env = app.builder.env
|
||||
if not hasattr(env, '_viewcode_modules'):
|
||||
return
|
||||
if app.builder.name == "singlehtml":
|
||||
return
|
||||
if app.builder.name.startswith("epub") and not env.config.viewcode_enable_epub:
|
||||
if not is_supported_builder(app.builder):
|
||||
return
|
||||
highlighter = app.builder.highlighter # type: ignore
|
||||
urito = app.builder.get_relative_uri
|
||||
@ -292,6 +329,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
# app.add_config_value('viewcode_exclude_modules', [], 'env')
|
||||
app.add_event('viewcode-find-source')
|
||||
app.add_event('viewcode-follow-imported')
|
||||
app.add_post_transform(ViewcodeAnchorTransform)
|
||||
return {
|
||||
'version': sphinx.__display_version__,
|
||||
'env_version': 1,
|
||||
|
@ -55,6 +55,9 @@ def test_viewcode_epub_default(app, status, warning):
|
||||
|
||||
assert not (app.outdir / '_modules/spam/mod1.xhtml').exists()
|
||||
|
||||
result = (app.outdir / 'index.xhtml').read_text()
|
||||
assert result.count('href="_modules/spam/mod1.xhtml#func1"') == 0
|
||||
|
||||
|
||||
@pytest.mark.sphinx('epub', testroot='ext-viewcode',
|
||||
confoverrides={'viewcode_enable_epub': True})
|
||||
@ -63,6 +66,9 @@ def test_viewcode_epub_enabled(app, status, warning):
|
||||
|
||||
assert (app.outdir / '_modules/spam/mod1.xhtml').exists()
|
||||
|
||||
result = (app.outdir / 'index.xhtml').read_text()
|
||||
assert result.count('href="_modules/spam/mod1.xhtml#func1"') == 2
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-viewcode', tags=['test_linkcode'])
|
||||
def test_linkcode(app, status, warning):
|
||||
|
Loading…
Reference in New Issue
Block a user