mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #7468 from jakobandersen/c_namespace
C, add scoping directives
This commit is contained in:
commit
09f66c690b
2
CHANGES
2
CHANGES
@ -19,6 +19,8 @@ Features added
|
|||||||
* LaTeX: Make the ``toplevel_sectioning`` setting optional in LaTeX theme
|
* LaTeX: Make the ``toplevel_sectioning`` setting optional in LaTeX theme
|
||||||
* #7410: Allow to suppress "circular toctree references detected" warnings using
|
* #7410: Allow to suppress "circular toctree references detected" warnings using
|
||||||
:confval:`suppress_warnings`
|
:confval:`suppress_warnings`
|
||||||
|
* C, added scope control directives, :rst:dir:`c:namespace`,
|
||||||
|
:rst:dir:`c:namespace-push`, and :rst:dir:`c:namespace-pop`.
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
@ -706,6 +706,72 @@ Inline Expressions and Types
|
|||||||
.. versionadded:: 3.0
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
|
||||||
|
Namespacing
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
The C language it self does not support namespacing, but it can sometimes be
|
||||||
|
useful to emulate it in documentation, e.g., to show alternate declarations.
|
||||||
|
The feature may also be used to document members of structs/unions/enums
|
||||||
|
separate from their parent declaration.
|
||||||
|
|
||||||
|
The current scope can be changed using three namespace directives. They manage
|
||||||
|
a stack declarations where ``c:namespace`` resets the stack and changes a given
|
||||||
|
scope.
|
||||||
|
|
||||||
|
The ``c:namespace-push`` directive changes the scope to a given inner scope
|
||||||
|
of the current one.
|
||||||
|
|
||||||
|
The ``c:namespace-pop`` directive undoes the most recent
|
||||||
|
``c:namespace-push`` directive.
|
||||||
|
|
||||||
|
.. rst:directive:: .. c:namespace:: scope specification
|
||||||
|
|
||||||
|
Changes the current scope for the subsequent objects to the given scope, and
|
||||||
|
resets the namespace directive stack. Note that nested scopes can be
|
||||||
|
specified by separating with a dot, e.g.::
|
||||||
|
|
||||||
|
.. c:namespace:: Namespace1.Namespace2.SomeStruct.AnInnerStruct
|
||||||
|
|
||||||
|
All subsequent objects will be defined as if their name were declared with
|
||||||
|
the scope prepended. The subsequent cross-references will be searched for
|
||||||
|
starting in the current scope.
|
||||||
|
|
||||||
|
Using ``NULL`` or ``0`` as the scope will change to global scope.
|
||||||
|
|
||||||
|
.. rst:directive:: .. c:namespace-push:: scope specification
|
||||||
|
|
||||||
|
Change the scope relatively to the current scope. For example, after::
|
||||||
|
|
||||||
|
.. c:namespace:: A.B
|
||||||
|
|
||||||
|
.. c:namespace-push:: C.D
|
||||||
|
|
||||||
|
the current scope will be ``A.B.C.D``.
|
||||||
|
|
||||||
|
.. rst:directive:: .. c:namespace-pop::
|
||||||
|
|
||||||
|
Undo the previous ``c:namespace-push`` directive (*not* just pop a scope).
|
||||||
|
For example, after::
|
||||||
|
|
||||||
|
.. c:namespace:: A.B
|
||||||
|
|
||||||
|
.. c:namespace-push:: C.D
|
||||||
|
|
||||||
|
.. c:namespace-pop::
|
||||||
|
|
||||||
|
the current scope will be ``A.B`` (*not* ``A.B.C``).
|
||||||
|
|
||||||
|
If no previous ``c:namespace-push`` directive has been used, but only a
|
||||||
|
``c:namespace`` directive, then the current scope will be reset to global
|
||||||
|
scope. That is, ``.. c:namespace:: A.B`` is equivalent to::
|
||||||
|
|
||||||
|
.. c:namespace:: NULL
|
||||||
|
|
||||||
|
.. c:namespace-push:: A.B
|
||||||
|
|
||||||
|
|
||||||
.. _cpp-domain:
|
.. _cpp-domain:
|
||||||
|
|
||||||
The C++ Domain
|
The C++ Domain
|
||||||
|
@ -35,6 +35,7 @@ from sphinx.util.cfamily import (
|
|||||||
char_literal_re
|
char_literal_re
|
||||||
)
|
)
|
||||||
from sphinx.util.docfields import Field, TypedField
|
from sphinx.util.docfields import Field, TypedField
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
from sphinx.util.nodes import make_refnode
|
from sphinx.util.nodes import make_refnode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -2928,6 +2929,9 @@ class DefinitionParser(BaseParser):
|
|||||||
assert False
|
assert False
|
||||||
return ASTDeclaration(objectType, directiveType, declaration)
|
return ASTDeclaration(objectType, directiveType, declaration)
|
||||||
|
|
||||||
|
def parse_namespace_object(self) -> ASTNestedName:
|
||||||
|
return self._parse_nested_name()
|
||||||
|
|
||||||
def parse_xref_object(self) -> ASTNestedName:
|
def parse_xref_object(self) -> ASTNestedName:
|
||||||
name = self._parse_nested_name()
|
name = self._parse_nested_name()
|
||||||
# if there are '()' left, just skip them
|
# if there are '()' left, just skip them
|
||||||
@ -3178,6 +3182,95 @@ class CTypeObject(CObject):
|
|||||||
object_type = 'type'
|
object_type = 'type'
|
||||||
|
|
||||||
|
|
||||||
|
class CNamespaceObject(SphinxDirective):
|
||||||
|
"""
|
||||||
|
This directive is just to tell Sphinx that we're documenting stuff in
|
||||||
|
namespace foo.
|
||||||
|
"""
|
||||||
|
|
||||||
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {} # type: Dict
|
||||||
|
|
||||||
|
def run(self) -> List[Node]:
|
||||||
|
rootSymbol = self.env.domaindata['c']['root_symbol']
|
||||||
|
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
|
||||||
|
symbol = rootSymbol
|
||||||
|
stack = [] # type: List[Symbol]
|
||||||
|
else:
|
||||||
|
parser = DefinitionParser(self.arguments[0],
|
||||||
|
location=self.get_source_info())
|
||||||
|
try:
|
||||||
|
name = parser.parse_namespace_object()
|
||||||
|
parser.assert_end()
|
||||||
|
except DefinitionError as e:
|
||||||
|
logger.warning(e, location=self.get_source_info())
|
||||||
|
name = _make_phony_error_name()
|
||||||
|
symbol = rootSymbol.add_name(name)
|
||||||
|
stack = [symbol]
|
||||||
|
self.env.temp_data['c:parent_symbol'] = symbol
|
||||||
|
self.env.temp_data['c:namespace_stack'] = stack
|
||||||
|
self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class CNamespacePushObject(SphinxDirective):
|
||||||
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {} # type: Dict
|
||||||
|
|
||||||
|
def run(self) -> List[Node]:
|
||||||
|
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
|
||||||
|
return []
|
||||||
|
parser = DefinitionParser(self.arguments[0],
|
||||||
|
location=self.get_source_info())
|
||||||
|
try:
|
||||||
|
name = parser.parse_namespace_object()
|
||||||
|
parser.assert_end()
|
||||||
|
except DefinitionError as e:
|
||||||
|
logger.warning(e, location=self.get_source_info())
|
||||||
|
name = _make_phony_error_name()
|
||||||
|
oldParent = self.env.temp_data.get('c:parent_symbol', None)
|
||||||
|
if not oldParent:
|
||||||
|
oldParent = self.env.domaindata['c']['root_symbol']
|
||||||
|
symbol = oldParent.add_name(name)
|
||||||
|
stack = self.env.temp_data.get('c:namespace_stack', [])
|
||||||
|
stack.append(symbol)
|
||||||
|
self.env.temp_data['c:parent_symbol'] = symbol
|
||||||
|
self.env.temp_data['c:namespace_stack'] = stack
|
||||||
|
self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class CNamespacePopObject(SphinxDirective):
|
||||||
|
has_content = False
|
||||||
|
required_arguments = 0
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {} # type: Dict
|
||||||
|
|
||||||
|
def run(self) -> List[Node]:
|
||||||
|
stack = self.env.temp_data.get('c:namespace_stack', None)
|
||||||
|
if not stack or len(stack) == 0:
|
||||||
|
logger.warning("C namespace pop on empty stack. Defaulting to gobal scope.",
|
||||||
|
location=self.get_source_info())
|
||||||
|
stack = []
|
||||||
|
else:
|
||||||
|
stack.pop()
|
||||||
|
if len(stack) > 0:
|
||||||
|
symbol = stack[-1]
|
||||||
|
else:
|
||||||
|
symbol = self.env.domaindata['c']['root_symbol']
|
||||||
|
self.env.temp_data['c:parent_symbol'] = symbol
|
||||||
|
self.env.temp_data['c:namespace_stack'] = stack
|
||||||
|
self.env.ref_context['cp:parent_key'] = symbol.get_lookup_key()
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class CXRefRole(XRefRole):
|
class CXRefRole(XRefRole):
|
||||||
def process_link(self, env: BuildEnvironment, refnode: Element,
|
def process_link(self, env: BuildEnvironment, refnode: Element,
|
||||||
has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
|
has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
|
||||||
@ -3256,6 +3349,10 @@ class CDomain(Domain):
|
|||||||
'enum': CEnumObject,
|
'enum': CEnumObject,
|
||||||
'enumerator': CEnumeratorObject,
|
'enumerator': CEnumeratorObject,
|
||||||
'type': CTypeObject,
|
'type': CTypeObject,
|
||||||
|
# scope control
|
||||||
|
'namespace': CNamespaceObject,
|
||||||
|
'namespace-push': CNamespacePushObject,
|
||||||
|
'namespace-pop': CNamespacePopObject,
|
||||||
}
|
}
|
||||||
roles = {
|
roles = {
|
||||||
'member': CXRefRole(),
|
'member': CXRefRole(),
|
||||||
|
21
tests/roots/test-domain-c/namespace.rst
Normal file
21
tests/roots/test-domain-c/namespace.rst
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.. c:namespace:: NS
|
||||||
|
|
||||||
|
.. c:var:: int NSVar
|
||||||
|
|
||||||
|
.. c:namespace:: NULL
|
||||||
|
|
||||||
|
.. c:var:: int NULLVar
|
||||||
|
|
||||||
|
.. c:namespace:: NSDummy
|
||||||
|
|
||||||
|
.. c:namespace:: 0
|
||||||
|
|
||||||
|
.. c:var:: int ZeroVar
|
||||||
|
|
||||||
|
.. c:namespace-push:: NS2.NS3
|
||||||
|
|
||||||
|
.. c:var:: int NS2NS3Var
|
||||||
|
|
||||||
|
.. c:namespace-pop::
|
||||||
|
|
||||||
|
.. c:var:: int PopVar
|
@ -473,6 +473,14 @@ def test_build_domain_c(app, status, warning):
|
|||||||
ws = filter_warnings(warning, "index")
|
ws = filter_warnings(warning, "index")
|
||||||
assert len(ws) == 0
|
assert len(ws) == 0
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
|
||||||
|
def test_build_domain_c(app, status, warning):
|
||||||
|
app.builder.build_all()
|
||||||
|
ws = filter_warnings(warning, "namespace")
|
||||||
|
assert len(ws) == 0
|
||||||
|
t = (app.outdir / "namespace.html").read_text()
|
||||||
|
for id_ in ('NS.NSVar', 'NULLVar', 'ZeroVar', 'NS2.NS3.NS2NS3Var', 'PopVar'):
|
||||||
|
assert 'id="c.{}"'.format(id_) in t
|
||||||
|
|
||||||
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
|
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
|
||||||
def test_build_domain_c_anon_dup_decl(app, status, warning):
|
def test_build_domain_c_anon_dup_decl(app, status, warning):
|
||||||
|
Loading…
Reference in New Issue
Block a user