mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-10-25 14:40:22 -05:00
Merge pull request #7269 from tk0miya/6903_cdomain
c domain: Generate node_id for objects in the right way
This commit is contained in:
4 files changed
+124
-36
No files matched your search
@@ -23,7 +23,7 @@ Incompatible changes
|
||||
* Due to the scoping changes for :rst:dir:`productionlist` some uses of
|
||||
:rst:role:`token` must be modified to include the scope which was previously
|
||||
ignored.
|
||||
* #6903: Internal data structure of Python, reST and standard domains have
|
||||
* #6903: Internal data structure of C, Python, reST and standard domains have
|
||||
changed. The node_id is added to the index of objects and modules. Now they
|
||||
contains a pair of docname and node_id for cross reference.
|
||||
* #7210: js domain: Non intended behavior is removed such as ``parseInt_`` links
|
||||
|
||||
+38
-30
@@ -27,7 +27,7 @@ from sphinx.locale import _, __
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docfields import Field, TypedField
|
||||
from sphinx.util.nodes import make_refnode
|
||||
from sphinx.util.nodes import make_id, make_refnode
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -197,21 +197,23 @@ class CObject(ObjectDescription):
|
||||
return ''
|
||||
|
||||
def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
|
||||
# for C API items we add a prefix since names are usually not qualified
|
||||
# by a module name and so easily clash with e.g. section titles
|
||||
targetname = 'c.' + name
|
||||
if targetname not in self.state.document.ids:
|
||||
signode['names'].append(targetname)
|
||||
signode['ids'].append(targetname)
|
||||
self.state.document.note_explicit_target(signode)
|
||||
node_id = make_id(self.env, self.state.document, 'c', name)
|
||||
signode['ids'].append(node_id)
|
||||
|
||||
domain = cast(CDomain, self.env.get_domain('c'))
|
||||
domain.note_object(name, self.objtype)
|
||||
# Assign old styled node_id not to break old hyperlinks (if possible)
|
||||
# Note: Will be removed in Sphinx-5.0 (RemovedInSphinx50Warning)
|
||||
old_node_id = self.make_old_id(name)
|
||||
if old_node_id not in self.state.document.ids and old_node_id not in signode['ids']:
|
||||
signode['ids'].append(old_node_id)
|
||||
|
||||
self.state.document.note_explicit_target(signode)
|
||||
|
||||
domain = cast(CDomain, self.env.get_domain('c'))
|
||||
domain.note_object(name, self.objtype, node_id)
|
||||
|
||||
indextext = self.get_index_text(name)
|
||||
if indextext:
|
||||
self.indexnode['entries'].append(('single', indextext,
|
||||
targetname, '', None))
|
||||
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
|
||||
|
||||
def before_content(self) -> None:
|
||||
self.typename_set = False
|
||||
@@ -224,6 +226,14 @@ class CObject(ObjectDescription):
|
||||
if self.typename_set:
|
||||
self.env.ref_context.pop('c:type', None)
|
||||
|
||||
def make_old_id(self, name: str) -> str:
|
||||
"""Generate old styled node_id for C objects.
|
||||
|
||||
.. note:: Old Styled node_id was used until Sphinx-3.0.
|
||||
This will be removed in Sphinx-5.0.
|
||||
"""
|
||||
return 'c.' + name
|
||||
|
||||
|
||||
class CXRefRole(XRefRole):
|
||||
def process_link(self, env: BuildEnvironment, refnode: Element,
|
||||
@@ -267,31 +277,31 @@ class CDomain(Domain):
|
||||
'type': CXRefRole(),
|
||||
}
|
||||
initial_data = {
|
||||
'objects': {}, # fullname -> docname, objtype
|
||||
} # type: Dict[str, Dict[str, Tuple[str, Any]]]
|
||||
'objects': {}, # fullname -> docname, node_id, objtype
|
||||
} # type: Dict[str, Dict[str, Tuple[str, str, str]]]
|
||||
|
||||
@property
|
||||
def objects(self) -> Dict[str, Tuple[str, str]]:
|
||||
return self.data.setdefault('objects', {}) # fullname -> docname, objtype
|
||||
def objects(self) -> Dict[str, Tuple[str, str, str]]:
|
||||
return self.data.setdefault('objects', {}) # fullname -> docname, node_id, objtype
|
||||
|
||||
def note_object(self, name: str, objtype: str, location: Any = None) -> None:
|
||||
def note_object(self, name: str, objtype: str, node_id: str, location: Any = None) -> None:
|
||||
if name in self.objects:
|
||||
docname = self.objects[name][0]
|
||||
logger.warning(__('duplicate C object description of %s, '
|
||||
'other instance in %s, use :noindex: for one of them'),
|
||||
name, docname, location=location)
|
||||
self.objects[name] = (self.env.docname, objtype)
|
||||
self.objects[name] = (self.env.docname, node_id, objtype)
|
||||
|
||||
def clear_doc(self, docname: str) -> None:
|
||||
for fullname, (fn, _l) in list(self.objects.items()):
|
||||
for fullname, (fn, node_id, _l) in list(self.objects.items()):
|
||||
if fn == docname:
|
||||
del self.objects[fullname]
|
||||
|
||||
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
|
||||
# XXX check duplicates
|
||||
for fullname, (fn, objtype) in otherdata['objects'].items():
|
||||
for fullname, (fn, node_id, objtype) in otherdata['objects'].items():
|
||||
if fn in docnames:
|
||||
self.data['objects'][fullname] = (fn, objtype)
|
||||
self.data['objects'][fullname] = (fn, node_id, objtype)
|
||||
|
||||
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
|
||||
typ: str, target: str, node: pending_xref, contnode: Element
|
||||
@@ -303,9 +313,8 @@ class CDomain(Domain):
|
||||
return contnode
|
||||
if target not in self.objects:
|
||||
return None
|
||||
obj = self.objects[target]
|
||||
return make_refnode(builder, fromdocname, obj[0], 'c.' + target,
|
||||
contnode, target)
|
||||
docname, node_id, objtype = self.objects[target]
|
||||
return make_refnode(builder, fromdocname, docname, node_id, contnode, target)
|
||||
|
||||
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
|
||||
target: str, node: pending_xref, contnode: Element
|
||||
@@ -314,14 +323,13 @@ class CDomain(Domain):
|
||||
target = target.rstrip(' *')
|
||||
if target not in self.objects:
|
||||
return []
|
||||
obj = self.objects[target]
|
||||
return [('c:' + self.role_for_objtype(obj[1]),
|
||||
make_refnode(builder, fromdocname, obj[0], 'c.' + target,
|
||||
contnode, target))]
|
||||
docname, node_id, objtype = self.objects[target]
|
||||
return [('c:' + self.role_for_objtype(objtype),
|
||||
make_refnode(builder, fromdocname, docname, node_id, contnode, target))]
|
||||
|
||||
def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
|
||||
for refname, (docname, type) in list(self.objects.items()):
|
||||
yield (refname, refname, type, docname, 'c.' + refname, 1)
|
||||
for refname, (docname, node_id, objtype) in list(self.objects.items()):
|
||||
yield (refname, refname, objtype, docname, node_id, 1)
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
|
||||
@@ -288,11 +288,11 @@ def test_html4_output(app, status, warning):
|
||||
(".//a[@class='reference internal'][@href='#errmod-error']/strong", 'Error'),
|
||||
# C references
|
||||
(".//span[@class='pre']", 'CFunction()'),
|
||||
(".//a[@href='#c.Sphinx_DoSomething']", ''),
|
||||
(".//a[@href='#c.SphinxStruct.member']", ''),
|
||||
(".//a[@href='#c.SPHINX_USE_PYTHON']", ''),
|
||||
(".//a[@href='#c.SphinxType']", ''),
|
||||
(".//a[@href='#c.sphinx_global']", ''),
|
||||
(".//a[@href='#c-sphinx-dosomething']", ''),
|
||||
(".//a[@href='#c-sphinxstruct-member']", ''),
|
||||
(".//a[@href='#c-sphinx-use-python']", ''),
|
||||
(".//a[@href='#c-sphinxtype']", ''),
|
||||
(".//a[@href='#c-sphinx-global']", ''),
|
||||
# test global TOC created by toctree()
|
||||
(".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='#']",
|
||||
'Testing object descriptions'),
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
test_domain_c
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Tests the C Domain
|
||||
|
||||
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import (
|
||||
desc, desc_addname, desc_annotation, desc_content, desc_name, desc_optional,
|
||||
desc_parameter, desc_parameterlist, desc_returns, desc_signature, desc_type,
|
||||
pending_xref
|
||||
)
|
||||
from sphinx.testing import restructuredtext
|
||||
from sphinx.testing.util import assert_node
|
||||
|
||||
|
||||
def test_cfunction(app):
|
||||
text = (".. c:function:: PyObject* "
|
||||
"PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree,
|
||||
(addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_type, ([pending_xref, "PyObject"],
|
||||
"* ")],
|
||||
[desc_name, "PyType_GenericAlloc"],
|
||||
[desc_parameterlist, (desc_parameter,
|
||||
desc_parameter)])],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="function",
|
||||
domain="c", objtype="function", noindex=False)
|
||||
assert_node(doctree[1][0][2][0],
|
||||
[desc_parameter, ([pending_xref, "PyTypeObject"],
|
||||
[nodes.emphasis, "\xa0*type"])])
|
||||
assert_node(doctree[1][0][2][1],
|
||||
[desc_parameter, ([pending_xref, "Py_ssize_t"],
|
||||
[nodes.emphasis, "\xa0nitems"])])
|
||||
|
||||
domain = app.env.get_domain('c')
|
||||
entry = domain.objects.get('PyType_GenericAlloc')
|
||||
assert entry == ('index', 'c-pytype-genericalloc', 'function')
|
||||
|
||||
|
||||
def test_cmember(app):
|
||||
text = ".. c:member:: PyObject* PyTypeObject.tp_bases"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree,
|
||||
(addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_type, ([pending_xref, "PyObject"],
|
||||
"* ")],
|
||||
[desc_name, "PyTypeObject.tp_bases"])],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="member",
|
||||
domain="c", objtype="member", noindex=False)
|
||||
|
||||
domain = app.env.get_domain('c')
|
||||
entry = domain.objects.get('PyTypeObject.tp_bases')
|
||||
assert entry == ('index', 'c-pytypeobject-tp-bases', 'member')
|
||||
|
||||
|
||||
def test_cvar(app):
|
||||
text = ".. c:var:: PyObject* PyClass_Type"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree,
|
||||
(addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_type, ([pending_xref, "PyObject"],
|
||||
"* ")],
|
||||
[desc_name, "PyClass_Type"])],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="var",
|
||||
domain="c", objtype="var", noindex=False)
|
||||
|
||||
domain = app.env.get_domain('c')
|
||||
entry = domain.objects.get('PyClass_Type')
|
||||
assert entry == ('index', 'c-pyclass-type', 'var')
|
||||
Reference in new issue
Block a user