chiark / gitweb /
documentation/doxygen: undo the crap Doxygen does to inline namespaces.
authorVladimír Vondruš <mosra@centrum.cz>
Sat, 14 Sep 2024 20:03:12 +0000 (22:03 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Sat, 14 Sep 2024 20:17:55 +0000 (22:17 +0200)
Like, COME ON, WAS THAT "CONVENIENCE FEATURE" REALLY WORTH THE EXTREME
ADDED PAIN FOR EVERYBODY ELSE??? Fuck me.

documentation/doxygen.py
documentation/test_doxygen/compound_inline_namespace/File.h
documentation/test_doxygen/compound_inline_namespace/File_8h.html [new file with mode: 0644]
documentation/test_doxygen/test_compound.py

index 8813b5d6297836b72ff77720b1016abce9b62c6b..87f0191540c24e8c34890589e931d8a2cd242514 100755 (executable)
@@ -2481,7 +2481,18 @@ def extract_metadata(state: State, xml):
         for i in compounddef.findall('innerclass'):
             compound.children += [i.attrib['refid']]
         for i in compounddef.findall('innernamespace'):
-            compound.children += [i.attrib['refid']]
+            # Children of inline namespaces get listed also in their parents as
+            # of 1.9.0 and https://github.com/doxygen/doxygen/commit/4372054e0b7af9c0cd1c1390859d8fef3581d8bb
+            # So e.g. if Bar is inline, Foo::Bar::Baz gets shortened to
+            # Foo::Baz, and listed as <innernamespace> of both Foo and
+            # Foo::Bar. Compare their IDs (because they don't get shortened)
+            # and add the namespace only if it's a direct descendant. Another
+            # patching gets done in postprocess_state() below, then the same
+            # child filtering is done in parse_xml(), and finally the
+            # duplicates have to be removed in parse_index_xml() as well. See
+            # test_compound.InlineNamespace for a matching test case.
+            if i.attrib['refid'].rpartition('_1_1')[0] == compound.id:
+                compound.children += [i.attrib['refid']]
     elif compounddef.attrib['kind'] in ['dir', 'file']:
         for i in compounddef.findall('innerdir'):
             compound.children += [i.attrib['refid']]
@@ -2509,8 +2520,15 @@ def postprocess_state(state: State):
             compound.leaf_name = compound.name
             continue
 
+        # Extract leaf namespace name from symbol name, take just everything
+        # after the last ::, as the parent names may have intermediate inline
+        # namespaces removed since 1.9.0 and https://github.com/doxygen/doxygen/commit/4372054e0b7af9c0cd1c1390859d8fef3581d8bb
+        # The full name is reconstructed in a second step below.
+        if compound.kind == 'namespace':
+            compound.leaf_name = compound.name.rpartition('::')[2]
+
         # Strip parent namespace/class from symbol name
-        if compound.kind in ['namespace', 'struct', 'class', 'union']:
+        elif compound.kind in ['struct', 'class', 'union']:
             prefix = state.compounds[compound.parent].name + '::'
             assert compound.name.startswith(prefix)
             compound.leaf_name = compound.name[len(prefix):]
@@ -2527,6 +2545,18 @@ def postprocess_state(state: State):
         # Other compounds are not in any index pages or breadcrumb, so leaf
         # name not needed
 
+    # Now that we have leaf names for all namespaces made above, reconstruct
+    # full names from them. FFS, so much extra code just to undo this crap.
+    for _, compound in state.compounds.items():
+        if not compound.kind == 'namespace':
+            continue
+        compound.name = compound.leaf_name
+        parent = compound.parent
+        while parent:
+            compound_parent = state.compounds[parent]
+            compound.name = compound_parent.leaf_name + '::' + compound.name
+            parent = compound_parent.parent
+
     # Build reverse header name to ID mapping for #include information, unless
     # it's explicitly disabled. Doxygen doesn't provide full path for files so
     # we need to combine that ourselves. Ugh. (Yes, I know SHOW_INCLUDE_FILES
@@ -2917,7 +2947,21 @@ def parse_xml(state: State, xml: str):
                     namespace.deprecated = symbol.deprecated
                     namespace.since = symbol.since
                     namespace.is_inline = compounddef_child.attrib.get('inline') == 'yes'
-                    compound.namespaces += [namespace]
+
+                    # Children of inline namespaces get listed also in their
+                    # parents as of 1.9.0 and https://github.com/doxygen/doxygen/commit/4372054e0b7af9c0cd1c1390859d8fef3581d8bb
+                    # So e.g. if Bar is inline, Foo::Bar::Baz gets shortened to
+                    # Foo::Baz, and listed as <innernamespace> of both Foo and
+                    # Foo::Bar. Compare their IDs (because they don't get
+                    # shortened) and add the namespace only if it's a direct
+                    # descendant. Same is done in extract_metadata() above. See
+                    # test_compound.InlineNamespace for a matching test case.
+                    #
+                    # Note that file / group compounds can also contain
+                    # <innernamespace>. Those should list all namespaces
+                    # always.
+                    if compound.kind != 'namespace' or compounddef_child.attrib['refid'].rpartition('_1_1')[0] == compound.id:
+                        compound.namespaces += [namespace]
 
                 else:
                     assert compounddef_child.tag == 'innerclass'
@@ -3546,6 +3590,17 @@ def parse_index_xml(state: State, xml):
         entry.has_nestable_children = False
         if compound.kind == 'namespace':
             entry.is_inline = compound.is_inline
+
+            # As of 1.9.0 and https://github.com/doxygen/doxygen/commit/4372054e0b7af9c0cd1c1390859d8fef3581d8bb
+            # children of inline namespaces are listed twice in index.xml, once
+            # with their full name, and once with the intermediate inline
+            # namespaces removed. Keep just the ones with full names, i.e.
+            # where the name matches what was painstakingly reconstructed
+            # in postprocess_state() before. I hate this fucking tool of a
+            # tool.
+            if compound.name != i.findtext('name'):
+                continue
+
         if compound.kind in ['class', 'struct', 'union']:
             entry.is_final = compound.is_final
 
index 258dbcece2b8a117bf8804f0bda2bfc0043fe61f..caf99b2bae9f69c2d2a9fb29dc73356425e40f20 100644 (file)
@@ -1,3 +1,7 @@
+/** @file
+ * @brief A file
+ */
+
 /** @brief A namespace */
 namespace Foo {
 
diff --git a/documentation/test_doxygen/compound_inline_namespace/File_8h.html b/documentation/test_doxygen/compound_inline_namespace/File_8h.html
new file mode 100644 (file)
index 0000000..551cb52
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>File.h file | My Project</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+  <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+    </div>
+  </div>
+</nav></header>
+<main><article>
+  <div class="m-container m-container-inflatable">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <h1>
+          File.h <span class="m-thin">file</span>
+        </h1>
+        <p>A file.</p>
+        <nav class="m-block m-default">
+          <h3>Contents</h3>
+          <ul>
+            <li>
+              Reference
+              <ul>
+                <li><a href="#namespaces">Namespaces</a></li>
+                <li><a href="#nested-classes">Classes</a></li>
+              </ul>
+            </li>
+          </ul>
+        </nav>
+        <section id="namespaces">
+          <h2><a href="#namespaces">Namespaces</a></h2>
+          <dl class="m-doc">
+            <dt>namespace <a href="namespaceFoo.html" class="m-doc">Foo</a></dt>
+            <dd>A namespace.</dd>
+            <dt>namespace <a href="namespaceFoo_1_1Bar.html" class="m-doc">Foo::Bar</a> <span class="m-label m-flat m-info">inline</span></dt>
+            <dd>An inline nested namespace.</dd>
+            <dt>namespace <a href="namespaceFoo_1_1Bar_1_1Baz.html" class="m-doc">Foo::Bar::Baz</a> <span class="m-label m-flat m-info">inline</span></dt>
+            <dd>Another inline namespace.</dd>
+          </dl>
+        </section>
+        <section id="nested-classes">
+          <h2><a href="#nested-classes">Classes</a></h2>
+          <dl class="m-doc">
+            <dt>
+              struct <a href="structFoo_1_1Thing.html" class="m-doc">Foo::Thing</a>
+            </dt>
+            <dd>A thing.</dd>
+          </dl>
+        </section>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
index 5da1544799d40134a99768818b236fdad07bdc34..17cf3f947f2d80ec4f80c3c778185688cbf2b25c 100644 (file)
@@ -391,5 +391,6 @@ class InlineNamespace(IntegrationTestCase):
                 self.skipTest("Doxygen doesn't support inline namespaces here")
 
         self.assertEqual(*self.actual_expected_contents('namespaceFoo_1_1Bar.html'))
+        self.assertEqual(*self.actual_expected_contents('File_8h.html'))
         self.assertEqual(*self.actual_expected_contents('annotated.html'))
         self.assertEqual(*self.actual_expected_contents('namespaces.html'))