diff --git a/CHANGES.rst b/CHANGES.rst index 15bea2772..2a8b46872 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -21,6 +21,9 @@ Features added * Add a new ``duplicate_declaration`` warning type, with ``duplicate_declaration.c`` and ``duplicate_declaration.cpp`` subtypes. Patch by Julien Lecomte and Adam Turner. +* #11824: linkcode: Allow extensions to add support for a domain by defining + the keys that should be present. + Patch by Nicolas Peugnet. Bugs fixed ---------- diff --git a/doc/usage/extensions/linkcode.rst b/doc/usage/extensions/linkcode.rst index e65a0b780..a08076f5a 100644 --- a/doc/usage/extensions/linkcode.rst +++ b/doc/usage/extensions/linkcode.rst @@ -49,3 +49,21 @@ Configuration return None filename = info['module'].replace('.', '/') return "https://somesite/sourcerepo/%s.py" % filename + + +Third-party domains +------------------- + +Support for other domains can be added by extensions with +:py:func:`.add_linkcode_domain()`. +For example, a Sphinx extension that provides a ``php`` domain +could use the following code to support :mod:`~sphinx.ext.linkcode`: + +.. code-block:: python + + from sphinx.ext.linkcode import add_linkcode_domain + + def setup(app): + add_linkcode_domain('php', ['namespace', 'class', 'fullname']) + +.. autofunction:: add_linkcode_domain diff --git a/sphinx/ext/linkcode.py b/sphinx/ext/linkcode.py index ed46bc278..6643ea7f8 100644 --- a/sphinx/ext/linkcode.py +++ b/sphinx/ext/linkcode.py @@ -18,6 +18,23 @@ if TYPE_CHECKING: 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' @@ -37,13 +54,6 @@ def doctree_read(app: Sphinx, doctree: Node) -> None: # ``supported_linkcode`` attribute. node_only_expr = getattr(app.builder, 'supported_linkcode', 'html') - 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() @@ -53,7 +63,7 @@ def doctree_read(app: Sphinx, doctree: Node) -> None: # Convert signode to a specified format info = {} - for key in domain_keys.get(domain, []): + for key in _DOMAIN_KEYS.get(domain, []): value = signode.get(key) if not value: value = '' diff --git a/tests/roots/test-ext-viewcode/conf.py b/tests/roots/test-ext-viewcode/conf.py index 5388dd156..a15e2eab5 100644 --- a/tests/roots/test-ext-viewcode/conf.py +++ b/tests/roots/test-ext-viewcode/conf.py @@ -1,6 +1,8 @@ import sys from pathlib import Path +from sphinx.ext.linkcode import add_linkcode_domain + sys.path.insert(0, str(Path.cwd().resolve())) extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] @@ -19,5 +21,10 @@ if 'test_linkcode' in tags: # NoQA: F821 (tags is injected into conf.py) return 'https://foobar/js/' + info['fullname'] elif domain in {'c', 'cpp'}: return f'https://foobar/{domain}/{"".join(info["names"])}' + elif domain == 'rst': + return 'http://foobar/rst/{fullname}'.format(**info) else: raise AssertionError + + def setup(app): + add_linkcode_domain('rst', ['fullname']) diff --git a/tests/roots/test-ext-viewcode/objects.rst b/tests/roots/test-ext-viewcode/objects.rst index 114adbf2e..bf492e263 100644 --- a/tests/roots/test-ext-viewcode/objects.rst +++ b/tests/roots/test-ext-viewcode/objects.rst @@ -167,3 +167,10 @@ CPP domain .. cpp:function:: T& operator[]( unsigned j ) const T& operator[]( unsigned j ) const + +rST domain +========== + +.. rst:role:: foo + + Foo description. diff --git a/tests/test_extensions/test_ext_viewcode.py b/tests/test_extensions/test_ext_viewcode.py index eeef391c1..89c52b72b 100644 --- a/tests/test_extensions/test_ext_viewcode.py +++ b/tests/test_extensions/test_ext_viewcode.py @@ -116,6 +116,7 @@ def test_linkcode(app): assert 'https://foobar/js/' in stuff assert 'https://foobar/c/' in stuff assert 'https://foobar/cpp/' in stuff + assert 'http://foobar/rst/' in stuff @pytest.mark.sphinx('html', testroot='ext-viewcode-find', freshenv=True)