Skip to content

Commit 1d35442

Browse files
authored
impl(docfx): skip duplicate deprecated sections (#11615)
Namespaces can have multiple `xrefsect` marking them as deprecated. This skips all but the first such section.
1 parent 2c090bb commit 1d35442

5 files changed

Lines changed: 181 additions & 9 deletions

File tree

docfx/doxygen2markdown.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,9 @@ bool AppendIfSect1(std::ostream& os, MarkdownContext const& ctx,
176176
bool AppendIfXRefSect(std::ostream& os, MarkdownContext const& ctx,
177177
pugi::xml_node node) {
178178
if (std::string_view{node.name()} != "xrefsect") return false;
179-
// Add the title in bold, then some
180-
os << "**" << node.child_value("xreftitle") << "**";
179+
if (ctx.skip_xrefsect) return true;
180+
// Add the title in bold, then the description.
181+
os << "**" << node.child_value("xreftitle") << "**\n\n";
181182
AppendDescriptionType(os, ctx, node.child("xrefdescription"));
182183
return true;
183184
}

docfx/doxygen2markdown.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct MarkdownContext {
3434
std::string paragraph_indent;
3535
std::string item_prefix;
3636
std::vector<std::string> decorators;
37+
bool skip_xrefsect = false;
3738
};
3839

3940
/// Handles a sect4 node.

docfx/doxygen2markdown_test.cc

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,73 @@ Second paragraph (4).)md";
235235
EXPECT_EQ(kExpected, os.str());
236236
}
237237

238+
TEST(Doxygen2Markdown, DetailedDescriptionNotSkipped) {
239+
auto constexpr kXml = R"xml(<?xml version="1.0" standalone="yes"?>
240+
<doxygen version="1.9.1" xml:lang="en-US">
241+
<compounddef xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="namespacegoogle_1_1cloud_1_1kms" kind="namespace" language="C++">
242+
<compoundname>google::cloud::kms</compoundname>
243+
<briefdescription>
244+
</briefdescription>
245+
<detaileddescription>
246+
<para><xrefsect id="deprecated_1_deprecated000001"><xreftitle>Deprecated</xreftitle><xrefdescription><para>This namespace exists for backwards compatibility. Use the types defined in <ref refid="namespacegoogle_1_1cloud_1_1kms__v1" kindref="compound">kms_v1</ref> instead of the aliases defined in this namespace.</para>
247+
</xrefdescription></xrefsect></para>
248+
<para><xrefsect id="deprecated_1_deprecated000014"><xreftitle>Deprecated</xreftitle><xrefdescription><para>This namespace exists for backwards compatibility. Use the types defined in <ref refid="namespacegoogle_1_1cloud_1_1kms__v1" kindref="compound">kms_v1</ref> instead of the aliases defined in this namespace.</para>
249+
</xrefdescription></xrefsect></para>
250+
</detaileddescription>
251+
</compounddef>
252+
</doxygen>)xml";
253+
254+
auto constexpr kExpected = R"md(**Deprecated**
255+
256+
This namespace exists for backwards compatibility. Use the types defined in [kms_v1](xref:namespacegoogle_1_1cloud_1_1kms__v1) instead of the aliases defined in this namespace.
257+
258+
**Deprecated**
259+
260+
261+
262+
This namespace exists for backwards compatibility. Use the types defined in [kms_v1](xref:namespacegoogle_1_1cloud_1_1kms__v1) instead of the aliases defined in this namespace.)md";
263+
pugi::xml_document doc;
264+
doc.load_string(kXml);
265+
auto selected = doc.select_node("//detaileddescription");
266+
ASSERT_TRUE(selected);
267+
std::ostringstream os;
268+
MarkdownContext ctx;
269+
ctx.paragraph_start = "";
270+
EXPECT_TRUE(AppendIfDetailedDescription(os, ctx, selected.node()));
271+
EXPECT_EQ(kExpected, os.str());
272+
}
273+
274+
TEST(Doxygen2Markdown, DetailedDescriptionSkipped) {
275+
auto constexpr kXml = R"xml(<?xml version="1.0" standalone="yes"?>
276+
<doxygen version="1.9.1" xml:lang="en-US">
277+
<compounddef xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="namespacegoogle_1_1cloud_1_1kms" kind="namespace" language="C++">
278+
<compoundname>google::cloud::kms</compoundname>
279+
<briefdescription>
280+
</briefdescription>
281+
<detaileddescription>
282+
<para><xrefsect id="deprecated_1_deprecated000001"><xreftitle>Deprecated</xreftitle><xrefdescription><para>This namespace exists for backwards compatibility. Use the types defined in <ref refid="namespacegoogle_1_1cloud_1_1kms__v1" kindref="compound">kms_v1</ref> instead of the aliases defined in this namespace.</para>
283+
</xrefdescription></xrefsect></para>
284+
<para><xrefsect id="deprecated_1_deprecated000014"><xreftitle>Deprecated</xreftitle><xrefdescription><para>This namespace exists for backwards compatibility. Use the types defined in <ref refid="namespacegoogle_1_1cloud_1_1kms__v1" kindref="compound">kms_v1</ref> instead of the aliases defined in this namespace.</para>
285+
</xrefdescription></xrefsect></para>
286+
</detaileddescription>
287+
</compounddef>
288+
</doxygen>)xml";
289+
290+
auto constexpr kExpected = R"md(
291+
292+
)md";
293+
pugi::xml_document doc;
294+
doc.load_string(kXml);
295+
auto selected = doc.select_node("//detaileddescription");
296+
ASSERT_TRUE(selected);
297+
std::ostringstream os;
298+
MarkdownContext ctx;
299+
ctx.paragraph_start = "";
300+
ctx.skip_xrefsect = true;
301+
EXPECT_TRUE(AppendIfDetailedDescription(os, ctx, selected.node()));
302+
EXPECT_EQ(kExpected, os.str());
303+
}
304+
238305
TEST(Doxygen2Markdown, BriefDescription) {
239306
auto constexpr kXml = R"xml(<?xml version="1.0" standalone="yes"?>
240307
<doxygen version="1.9.1" xml:lang="en-US">
@@ -744,7 +811,7 @@ TEST(Doxygen2Markdown, ParagraphXrefSect) {
744811
auto constexpr kXml = R"xml(<?xml version="1.0" standalone="yes"?>
745812
<doxygen version="1.9.1" xml:lang="en-US">
746813
<para id='tested-node'>
747-
<xrefsect id="deprecated_1_deprecated000007">
814+
<xrefsect id="deprecated_1_deprecated000001">
748815
<xreftitle>Deprecated</xreftitle>
749816
<xrefdescription>
750817
<para>Use <computeroutput><ref refid="classgoogle_1_1cloud_1_1Options" kindref="compound">Options</ref></computeroutput> and <computeroutput><ref refid="structgoogle_1_1cloud_1_1EndpointOption" kindref="compound">EndpointOption</ref></computeroutput>.</para>
@@ -753,17 +820,17 @@ TEST(Doxygen2Markdown, ParagraphXrefSect) {
753820
</para>
754821
</doxygen>)xml";
755822

756-
auto constexpr kExpected = R"md(
757-
758-
**Deprecated**
823+
auto constexpr kExpected = R"md(**Deprecated**
759824
760825
Use [`Options`](xref:classgoogle_1_1cloud_1_1Options) and [`EndpointOption`](xref:structgoogle_1_1cloud_1_1EndpointOption).)md";
761826
pugi::xml_document doc;
762827
doc.load_string(kXml);
763828
auto selected = doc.select_node("//*[@id='tested-node']");
764829
ASSERT_TRUE(selected);
765830
std::ostringstream os;
766-
ASSERT_TRUE(AppendIfParagraph(os, {}, selected.node()));
831+
MarkdownContext ctx;
832+
ctx.paragraph_start = "";
833+
ASSERT_TRUE(AppendIfParagraph(os, ctx, selected.node()));
767834
EXPECT_EQ(kExpected, os.str());
768835
}
769836

docfx/doxygen2yaml.cc

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "docfx/public_docs.h"
2424
#include "docfx/yaml_emit.h"
2525
#include <algorithm>
26+
#include <iostream>
2627
#include <sstream>
2728
#include <string_view>
2829

@@ -96,10 +97,11 @@ std::string Summary(pugi::xml_node node) {
9697
return std::move(os).str();
9798
}
9899

99-
std::string Conceptual(pugi::xml_node node) {
100+
std::string Conceptual(pugi::xml_node node, bool skip_xrefsect = false) {
100101
std::ostringstream os;
101102
MarkdownContext ctx;
102103
ctx.paragraph_start = "";
104+
ctx.skip_xrefsect = skip_xrefsect;
103105
auto description = node.child("description");
104106
if (!description.empty()) {
105107
AppendDescriptionType(os, ctx, description);
@@ -341,7 +343,44 @@ bool AppendIfNamespace(YAML::Emitter& yaml, YamlContext const& ctx,
341343
<< YAML::Key << "type" << YAML::Value << "namespace" //
342344
<< YAML::Key << "langs" << YAML::BeginSeq << "cpp" << YAML::EndSeq; //
343345
AppendNamespaceSyntax(yaml, ctx, node);
344-
AppendDescription(yaml, node);
346+
// Deprecated namespaces need special treatment
347+
auto const summary = Summary(node);
348+
if (!summary.empty()) {
349+
yaml << YAML::Key << "summary" << YAML::Value << YAML::Literal << summary;
350+
}
351+
auto conceptual = Conceptual(node, true);
352+
// Discover all the `xrefsect` descendants that document this is a deprecated
353+
// namespace and list the alternatives.
354+
std::map<std::string, std::string> deprecated;
355+
for (auto const i :
356+
node.child("detaileddescription").select_nodes(".//xrefsect")) {
357+
auto const title = std::string_view{i.node().child_value("xreftitle")};
358+
if (title != "Deprecated") continue;
359+
auto xrefdescription = i.node().child("xrefdescription");
360+
for (auto const j : xrefdescription.select_nodes(".//ref")) {
361+
auto ref = j.node();
362+
deprecated.emplace(ref.child_value(), ref.attribute("refid").as_string());
363+
}
364+
}
365+
if (!deprecated.empty()) {
366+
std::ostringstream os;
367+
os << R"""(
368+
369+
<aside class="deprecated">
370+
<b>Deprecated:</b> This namespace is deprecated, prefer the types defined in)""";
371+
auto sep = std::string_view{" "};
372+
for (auto const& [name, uid] : deprecated) {
373+
os << sep << "[`" << name << "`](xref:" << uid << ")";
374+
sep = ", or ";
375+
}
376+
os << ".\n</aside>";
377+
conceptual.append(std::move(os.str()));
378+
}
379+
if (!conceptual.empty()) {
380+
yaml << YAML::Key << "conceptual" << YAML::Value << YAML::Literal
381+
<< conceptual;
382+
}
383+
345384
auto const children = Children(ctx, node);
346385
if (!children.empty()) {
347386
yaml << YAML::Key << "children" << YAML::Value << children;

docfx/doxygen2yaml_test.cc

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,70 @@ TEST(Doxygen2Yaml, Namespace) {
922922
EXPECT_EQ(actual, kExpected);
923923
}
924924

925+
TEST(Doxygen2Yaml, NamespaceDeprecated) {
926+
auto constexpr kXml = R"xml(<?xml version="1.0" standalone="yes"?>
927+
<doxygen version="1.9.1" xml:lang="en-US">
928+
<compounddef xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="namespacegoogle_1_1cloud_1_1kms" kind="namespace" language="C++">
929+
<compoundname>google::cloud::kms</compoundname>
930+
<sectiondef kind="func">
931+
</sectiondef>
932+
<briefdescription>
933+
</briefdescription>
934+
<detaileddescription>
935+
<para><xrefsect id="deprecated_1_deprecated000001"><xreftitle>Deprecated</xreftitle><xrefdescription><para>This namespace exists for backwards compatibility. Use the types defined in <ref refid="namespacegoogle_1_1cloud_1_1kms__v1" kindref="compound">kms_v1</ref> instead of the aliases defined in this namespace. </para>
936+
</xrefdescription></xrefsect></para>
937+
<para><xrefsect id="deprecated_1_deprecated000014"><xreftitle>Deprecated</xreftitle><xrefdescription><para>This namespace exists for backwards compatibility. Use the types defined in <ref refid="namespacegoogle_1_1cloud_1_1kms__v1" kindref="compound">kms_v1</ref> instead of the aliases defined in this namespace. </para>
938+
</xrefdescription></xrefsect></para>
939+
</detaileddescription>
940+
<location file="ekm_client.h" line="30" column="1"/>
941+
</compounddef>
942+
</doxygen>)xml";
943+
944+
auto constexpr kExpected = R"yml(### YamlMime:UniversalReference
945+
items:
946+
- uid: namespacegoogle_1_1cloud_1_1kms
947+
name: "google::cloud::kms"
948+
id: namespacegoogle_1_1cloud_1_1kms
949+
parent: test-only-parent-id
950+
type: namespace
951+
langs:
952+
- cpp
953+
syntax:
954+
contents: |
955+
namespace google::cloud::kms { ... };
956+
source:
957+
id: google::cloud::kms
958+
path: google/cloud/kms/ekm_client.h
959+
startLine: 30
960+
remote:
961+
repo: https://github.com/googleapis/google-cloud-cpp/
962+
branch: main
963+
path: google/cloud/kms/ekm_client.h
964+
conceptual: |
965+
966+
967+
968+
969+
<aside class="deprecated">
970+
<b>Deprecated:</b> This namespace is deprecated, prefer the types defined in [`kms_v1`](xref:namespacegoogle_1_1cloud_1_1kms__v1).
971+
</aside>
972+
)yml";
973+
974+
pugi::xml_document doc;
975+
doc.load_string(kXml);
976+
auto selected = doc.select_node("//*[@id='namespacegoogle_1_1cloud_1_1kms']");
977+
ASSERT_TRUE(selected);
978+
YAML::Emitter yaml;
979+
TestPre(yaml);
980+
YamlContext ctx;
981+
ctx.parent_id = "test-only-parent-id";
982+
ctx.library_root = "google/cloud/kms/";
983+
ASSERT_TRUE(AppendIfNamespace(yaml, ctx, selected.node()));
984+
TestPost(yaml);
985+
auto const actual = EndDocFxYaml(yaml);
986+
EXPECT_EQ(actual, kExpected);
987+
}
988+
925989
TEST(Doxygen2Yaml, Class) {
926990
auto constexpr kExpected = R"yml(### YamlMime:UniversalReference
927991
items:

0 commit comments

Comments
 (0)