summaryrefslogtreecommitdiffstats
path: root/sphinx/ext/linkcode.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/ext/linkcode.py')
-rw-r--r--sphinx/ext/linkcode.py71
1 files changed, 71 insertions, 0 deletions
diff --git a/sphinx/ext/linkcode.py b/sphinx/ext/linkcode.py
new file mode 100644
index 0000000..ad7abd3
--- /dev/null
+++ b/sphinx/ext/linkcode.py
@@ -0,0 +1,71 @@
+"""Add external links to module code in Python object descriptions."""
+
+from typing import Any, Dict, Set
+
+from docutils import nodes
+from docutils.nodes import Node
+
+import sphinx
+from sphinx import addnodes
+from sphinx.application import Sphinx
+from sphinx.errors import SphinxError
+from sphinx.locale import _
+
+
+class LinkcodeError(SphinxError):
+ category = "linkcode error"
+
+
+def doctree_read(app: Sphinx, doctree: Node) -> None:
+ env = app.builder.env
+
+ resolve_target = getattr(env.config, 'linkcode_resolve', None)
+ if not callable(env.config.linkcode_resolve):
+ raise LinkcodeError(
+ "Function `linkcode_resolve` is not given in conf.py")
+
+ domain_keys = {
+ 'py': ['module', 'fullname'],
+ 'c': ['names'],
+ 'cpp': ['names'],
+ 'js': ['object', 'fullname'],
+ }
+
+ for objnode in list(doctree.findall(addnodes.desc)):
+ domain = objnode.get('domain')
+ uris: Set[str] = set()
+ for signode in objnode:
+ if not isinstance(signode, addnodes.desc_signature):
+ continue
+
+ # Convert signode to a specified format
+ info = {}
+ for key in domain_keys.get(domain, []):
+ value = signode.get(key)
+ if not value:
+ value = ''
+ info[key] = value
+ if not info:
+ continue
+
+ # Call user code to resolve the link
+ uri = resolve_target(domain, info)
+ if not uri:
+ # no source
+ continue
+
+ if uri in uris or not uri:
+ # only one link per name, please
+ continue
+ uris.add(uri)
+
+ inline = nodes.inline('', _('[source]'), classes=['viewcode-link'])
+ onlynode = addnodes.only(expr='html')
+ onlynode += nodes.reference('', '', inline, internal=False, refuri=uri)
+ signode += onlynode
+
+
+def setup(app: Sphinx) -> Dict[str, Any]:
+ app.connect('doctree-read', doctree_read)
+ app.add_config_value('linkcode_resolve', None, '')
+ return {'version': sphinx.__display_version__, 'parallel_read_safe': True}