-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
Copy pathlinkcode.py
97 lines (73 loc) · 2.87 KB
/
linkcode.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""Add external links to module code in Python object descriptions."""
from __future__ import annotations
from typing import TYPE_CHECKING
from docutils import nodes
import sphinx
from sphinx import addnodes
from sphinx.errors import SphinxError
from sphinx.locale import _
if TYPE_CHECKING:
from docutils.nodes import Node
from sphinx.application import Sphinx
from sphinx.util.typing import ExtensionMetadata
_DOMAIN_KEYS = {
'py': ['module', 'fullname'],
'c': ['names'],
'cpp': ['names'],
'js': ['object', 'fullname'],
}
def add_linkcode_domain(domain: str, keys: list[str], override: bool = False) -> None:
"""Register a new list of keys to use for a domain.
.. versionadded:: 8.2
"""
if override or domain not in _DOMAIN_KEYS:
_DOMAIN_KEYS[domain] = list(keys)
class LinkcodeError(SphinxError):
category = 'linkcode error'
def doctree_read(app: Sphinx, doctree: Node) -> None:
env = app.env
resolve_target = getattr(env.config, 'linkcode_resolve', None)
if not callable(env.config.linkcode_resolve):
msg = 'Function `linkcode_resolve` is not given in conf.py'
raise LinkcodeError(msg)
assert resolve_target is not None # for mypy
# By default, the linkcode extension will only inject references
# for an ``html`` builder. If a builder wishes to support managing
# references generated by linkcode as well, it can define the
# ``supported_linkcode`` attribute.
node_only_expr = getattr(app.builder, 'supported_linkcode', 'html')
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=node_only_expr)
onlynode += nodes.reference('', '', inline, internal=False, refuri=uri)
signode += onlynode
def setup(app: Sphinx) -> ExtensionMetadata:
app.connect('doctree-read', doctree_read)
app.add_config_value('linkcode_resolve', None, '')
return {
'version': sphinx.__display_version__,
'parallel_read_safe': True,
}