From a22fb0d45f04903b9248044fb01c9d19c211cd58 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 6 Sep 2015 22:28:55 +0200 Subject: [PATCH] C++, add namespace push/pop directives. --- CHANGES | 1 + doc/domains.rst | 47 +++++++++++++++++++++++++-- sphinx/domains/cpp.py | 74 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 9da531b88..d3785de86 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Features added * #1909: Add "doc" references to Intersphinx inventories. * C++ type alias support (e.g., `.. type:: T = int`) * C++ template support for classes, functions, type aliases, and variables (#1729, #1314). +* C++, added new scope management directives ``namespace-push`` and ``namespace-pop``. Bugs fixed ---------- diff --git a/doc/domains.rst b/doc/domains.rst index 34b86c278..1ebcd3b7d 100644 --- a/doc/domains.rst +++ b/doc/domains.rst @@ -671,10 +671,19 @@ a visibility statement (``public``, ``private`` or ``protected``). Namespacing ~~~~~~~~~~~~~~~~~ +Declarations in the C++ doamin are as default placed in global scope. +The current scope can be changed using three namespace directives. +They manage a stack declarations where ``cpp:namespace`` resets the stack and +changes a given scope. +The ``cpp:namespace-push`` directive changes the scope to a given inner scope +of the current one. +The ``cpp:namespace-pop`` directive undos the most recent ``cpp:namespace-push`` +directive. + .. rst:directive:: .. cpp:namespace:: scope specification - Declarations in the C++ doamin are as default placed in global scope. - The ``namespace`` directive changes the current scope for the subsequent objects. + Changes the current scope for the subsequent objects to the given scope, + and resets the namespace directive stack. Note that the namespace does not need to correspond to C++ namespaces, but can end in names of classes, e.g.,:: @@ -683,7 +692,7 @@ Namespacing 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``, ``0``, or ``nullptr`` as the namespace will reset it to the global namespace. + Using ``NULL``, ``0``, or ``nullptr`` as the scope will change to global scope. A namespace declaration can also be templated, e.g.,:: @@ -708,6 +717,38 @@ Namespacing std::vector +.. rst:directive:: .. cpp:namespace-push:: scope specification + + Change the scope relatively to the current scope. For example, after:: + + .. cpp:namespace:: A::B + + .. cpp:namespace-push:: C::D + + the current scope will be ``A::B::C::D``. + +.. rst:directive:: .. cpp:namespace-pop:: + + Undo the previous ``cpp:namespace-push`` directive (*not* just pop a scope). + For example, after:: + + .. cpp:namespace:: A::B + + .. cpp:namespace-push:: C::D + + .. cpp:namespace-pop:: + + the current scope will be ``A::B`` (*not* ``A::B::C``). + + If no previous ``cpp:namespace-push`` directive has been used, but only a ``cpp:namespace`` + directive, then the current scope will be reset to global scope. + That is, ``.. cpp:namespace:: A::B`` is equivalent to:: + + .. cpp:namespace:: nullptr + + .. cpp:namespace-push:: A::B + + Info field lists ~~~~~~~~~~~~~~~~~ diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index a35b732ab..3e6701f4c 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3444,7 +3444,8 @@ class CPPNamespaceObject(Directive): env = self.state.document.settings.env rootSymbol = env.domaindata['cpp']['rootSymbol'] if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): - env.ref_context['cpp:parentSymbol'] = rootSymbol + symbol = rootSymbol + stack = [] else: parser = DefinitionParser(self.arguments[0], self) try: @@ -3455,8 +3456,71 @@ class CPPNamespaceObject(Directive): line=self.lineno) name = _make_phony_error_name() ast = ASTNamespace(name, None) - s = rootSymbol.add_name(ast.nestedName, ast.templatePrefix) - env.ref_context['cpp:parentSymbol'] = s + symbol = rootSymbol.add_name(ast.nestedName, ast.templatePrefix) + stack = [symbol] + env.ref_context['cpp:parentSymbol'] = symbol + env.temp_data['cpp:namespaceStack'] = stack + return [] + + +class CPPNamespacePushObject(Directive): + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def warn(self, msg): + self.state_machine.reporter.warning(msg, lineno=self.lineno) + + def run(self): + env = self.state.document.settings.env + if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): + return + parser = DefinitionParser(self.arguments[0], self) + try: + ast = parser.parse_namespace_object() + parser.assert_end() + except DefinitionError as e: + self.state_machine.reporter.warning(e.description, + line=self.lineno) + name = _make_phony_error_name() + ast = ASTNamespace(name, None) + oldParent = env.ref_context.get('cpp:parentSymbol', None) + if not oldParent: + oldParent = env.domaindata['cpp']['rootSymbol'] + symbol = oldParent.add_name(ast.nestedName, ast.templatePrefix) + stack = env.temp_data.get('cpp:namespaceStack', []) + stack.append(symbol) + env.ref_context['cpp:parentSymbol'] = symbol + env.temp_data['cpp:namespaceStack'] = stack + return [] + + +class CPPNamespacePopObject(Directive): + has_content = False + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def warn(self, msg): + self.state_machine.reporter.warning(msg, lineno=self.lineno) + + def run(self): + env = self.state.document.settings.env + stack = env.temp_data.get('cpp:namespaceStack', None) + if not stack or len(stack) == 0: + self.warn("C++ namespace pop on empty stack. Defaulting to gobal scope.") + stack = [] + else: + stack.pop() + if len(stack) > 0: + symbol = stack[-1] + else: + symbol = env.domaindata['cpp']['rootSymbol'] + env.ref_context['cpp:parentSymbol'] = symbol + env.temp_data['cpp:namespaceStack'] = stack return [] @@ -3501,7 +3565,9 @@ class CPPDomain(Domain): 'enum-struct': CPPEnumObject, 'enum-class': CPPEnumObject, 'enumerator': CPPEnumeratorObject, - 'namespace': CPPNamespaceObject + 'namespace': CPPNamespaceObject, + 'namespace-push': CPPNamespacePushObject, + 'namespace-pop': CPPNamespacePopObject } roles = { 'any': CPPXRefRole(),