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
|
||||
* #7410: Allow to suppress "circular toctree references detected" warnings using
|
||||
: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
|
||||
----------
|
||||
|
@ -706,6 +706,72 @@ Inline Expressions and Types
|
||||
.. 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:
|
||||
|
||||
The C++ Domain
|
||||
|
@ -35,6 +35,7 @@ from sphinx.util.cfamily import (
|
||||
char_literal_re
|
||||
)
|
||||
from sphinx.util.docfields import Field, TypedField
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.nodes import make_refnode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -2928,6 +2929,9 @@ class DefinitionParser(BaseParser):
|
||||
assert False
|
||||
return ASTDeclaration(objectType, directiveType, declaration)
|
||||
|
||||
def parse_namespace_object(self) -> ASTNestedName:
|
||||
return self._parse_nested_name()
|
||||
|
||||
def parse_xref_object(self) -> ASTNestedName:
|
||||
name = self._parse_nested_name()
|
||||
# if there are '()' left, just skip them
|
||||
@ -3178,6 +3182,95 @@ class CTypeObject(CObject):
|
||||
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):
|
||||
def process_link(self, env: BuildEnvironment, refnode: Element,
|
||||
has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
|
||||
@ -3256,6 +3349,10 @@ class CDomain(Domain):
|
||||
'enum': CEnumObject,
|
||||
'enumerator': CEnumeratorObject,
|
||||
'type': CTypeObject,
|
||||
# scope control
|
||||
'namespace': CNamespaceObject,
|
||||
'namespace-push': CNamespacePushObject,
|
||||
'namespace-pop': CNamespacePopObject,
|
||||
}
|
||||
roles = {
|
||||
'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")
|
||||
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})
|
||||
def test_build_domain_c_anon_dup_decl(app, status, warning):
|
||||
|
Loading…
Reference in New Issue
Block a user