Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -18,67 +18,326 @@ namespace clang { namespace doc { +namespace { + +class HTMLTag { +public: + // Any other tag can be added if required + enum TagType { + TAG_META, + TAG_TITLE, + TAG_DIV, + TAG_H1, + TAG_H2, + TAG_H3, + TAG_P, + TAG_UL, + TAG_LI, + }; + + HTMLTag() = default; + constexpr HTMLTag(TagType Value) : Value(Value) {} + + operator TagType() const { return Value; } + operator bool() = delete; + + bool IsSelfClosing() const; + + bool HasInlineChildren() const; + + llvm::SmallString<16> ToString() const; + +private: + TagType Value; +}; + +struct HTMLNode { + virtual ~HTMLNode() = default; + + virtual void Render(llvm::raw_ostream &OS, int IndentationLevel) = 0; +}; + +struct TextNode : public HTMLNode { + TextNode(llvm::StringRef Text, bool Indented) + : Text(Text), Indented(Indented) {} + + std::string Text; // Content of node + bool Indented; // Indicates if an indentation must be rendered before the text + void Render(llvm::raw_ostream &OS, int IndentationLevel) override; +}; + +struct TagNode : public HTMLNode { + TagNode(HTMLTag Tag) + : Tag(Tag), InlineChildren(Tag.HasInlineChildren()), + SelfClosing(Tag.IsSelfClosing()) {} + TagNode(HTMLTag Tag, const Twine &Text) : TagNode(Tag) { + Children.emplace_back( + llvm::make_unique(Text.str(), !InlineChildren)); + } + + HTMLTag Tag; // Name of HTML Tag (p, div, h1) + bool InlineChildren; // Indicates if children nodes are rendered in the same + // line as itself or if children must rendered in the + // next line and with additional indentation + bool SelfClosing; // Indicates if tag is self-closing + std::vector> Children; // List of child nodes + llvm::StringMap> + Attributes; // List of key-value attributes for tag + + void Render(llvm::raw_ostream &OS, int IndentationLevel) override; +}; + +constexpr const char *kDoctypeDecl = ""; + +struct HTMLFile { + std::vector> Children; // List of child nodes + void Render(llvm::raw_ostream &OS) { + OS << kDoctypeDecl << "\n"; + for (const auto &C : Children) { + C->Render(OS, 0); + OS << "\n"; + } + } +}; + +} // namespace + +bool HTMLTag::IsSelfClosing() const { + switch (Value) { + case HTMLTag::TAG_META: + return true; + case HTMLTag::TAG_TITLE: + case HTMLTag::TAG_DIV: + case HTMLTag::TAG_H1: + case HTMLTag::TAG_H2: + case HTMLTag::TAG_H3: + case HTMLTag::TAG_P: + case HTMLTag::TAG_UL: + case HTMLTag::TAG_LI: + return false; + } +} + +bool HTMLTag::HasInlineChildren() const { + switch (Value) { + case HTMLTag::TAG_META: + case HTMLTag::TAG_TITLE: + case HTMLTag::TAG_H1: + case HTMLTag::TAG_H2: + case HTMLTag::TAG_H3: + case HTMLTag::TAG_LI: + return true; + case HTMLTag::TAG_DIV: + case HTMLTag::TAG_P: + case HTMLTag::TAG_UL: + return false; + } +} + +llvm::SmallString<16> HTMLTag::ToString() const { + switch (Value) { + case HTMLTag::TAG_META: + return llvm::SmallString<16>("meta"); + case HTMLTag::TAG_TITLE: + return llvm::SmallString<16>("title"); + case HTMLTag::TAG_DIV: + return llvm::SmallString<16>("div"); + case HTMLTag::TAG_H1: + return llvm::SmallString<16>("h1"); + case HTMLTag::TAG_H2: + return llvm::SmallString<16>("h2"); + case HTMLTag::TAG_H3: + return llvm::SmallString<16>("h3"); + case HTMLTag::TAG_P: + return llvm::SmallString<16>("p"); + case HTMLTag::TAG_UL: + return llvm::SmallString<16>("ul"); + case HTMLTag::TAG_LI: + return llvm::SmallString<16>("li"); + } +} + +void TextNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { + if (Indented) + OS.indent(IndentationLevel * 2); + OS << Text; +} + +void TagNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { + OS.indent(IndentationLevel * 2); + OS << "<" << Tag.ToString(); + for (const auto &A : Attributes) + OS << " " << A.getKey() << "=\"" << A.getValue() << "\""; + if (SelfClosing) { + OS << "/>"; + return; + } + OS << ">"; + if (!InlineChildren) + OS << "\n"; + int ChildrenIndentation = InlineChildren ? 0 : IndentationLevel + 1; + for (const auto &C : Children) { + C->Render(OS, ChildrenIndentation); + if (!InlineChildren) + OS << "\n"; + } + if (!InlineChildren) + OS.indent(IndentationLevel * 2); + OS << ""; +} + // HTML generation -std::string genTag(const Twine &Text, const Twine &Tag) { - return "<" + Tag.str() + ">" + Text.str() + ""; +static std::vector> genHTML(const EnumInfo &I); +static std::vector> genHTML(const FunctionInfo &I); + +static std::vector> +genEnumsBlock(const std::vector &Enums) { + if (Enums.empty()) + return {}; + + std::vector> Out; + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, "Enums")); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_DIV)); + for (const auto &E : Enums) { + std::vector> Nodes = genHTML(E); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(Out.back()->Children)); + } + return Out; +} + +static std::unique_ptr +genEnumMembersBlock(const llvm::SmallVector, 4> &Members) { + if (Members.empty()) + return nullptr; + + auto List = llvm::make_unique(HTMLTag::TAG_UL); + for (const auto &M : Members) + List->Children.emplace_back(llvm::make_unique(HTMLTag::TAG_LI, M)); + return List; +} + +static std::vector> +genFunctionsBlock(const std::vector &Functions) { + if (Functions.empty()) + return {}; + + std::vector> Out; + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, "Functions")); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_DIV)); + for (const auto &F : Functions) { + std::vector> Nodes = genHTML(F); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(Out.back()->Children)); + } + return Out; } -static void writeLine(const Twine &Text, raw_ostream &OS) { - OS << genTag(Text, "p") << "\n"; +static std::vector> +genRecordMembersBlock(const llvm::SmallVector &Members) { + if (Members.empty()) + return {}; + + std::vector> Out; + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, "Members")); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_UL)); + for (const auto &M : Members) { + std::string Access = getAccess(M.Access); + if (Access != "") + Access = Access + " "; + Out.back()->Children.emplace_back(llvm::make_unique( + HTMLTag::TAG_LI, Access + M.Type.Name + " " + M.Name)); + } + return Out; } -static void writeHeader(const Twine &Text, const Twine &Num, raw_ostream &OS) { - OS << genTag(Text, "h" + Num) << "\n"; +static std::vector> +genReferencesBlock(const std::vector &References, + llvm::StringRef Title) { + if (References.empty()) + return {}; + + std::vector> Out; + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, Title)); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_UL)); + for (const auto &R : References) + Out.back()->Children.emplace_back( + llvm::make_unique(HTMLTag::TAG_LI, R.Name)); + return Out; } -static void writeFileDefinition(const Location &L, raw_ostream &OS) { - writeLine("Defined at line " + std::to_string(L.LineNumber) + " of " + - L.Filename, - OS); +static std::unique_ptr writeFileDefinition(const Location &L) { + return llvm::make_unique( + HTMLTag::TAG_P, + "Defined at line " + std::to_string(L.LineNumber) + " of " + L.Filename); } -static void writeDescription(const CommentInfo &I, raw_ostream &OS) { +static std::unique_ptr genHTML(const CommentInfo &I) { if (I.Kind == "FullComment") { - for (const auto &Child : I.Children) - writeDescription(*Child, OS); + auto FullComment = llvm::make_unique(HTMLTag::TAG_DIV); + for (const auto &Child : I.Children) { + std::unique_ptr Node = genHTML(*Child); + if (Node) + FullComment->Children.emplace_back(std::move(Node)); + } + return std::move(FullComment); } else if (I.Kind == "ParagraphComment") { - OS << "

"; - for (const auto &Child : I.Children) - writeDescription(*Child, OS); - OS << "

\n"; + auto ParagraphComment = llvm::make_unique(HTMLTag::TAG_P); + for (const auto &Child : I.Children) { + std::unique_ptr Node = genHTML(*Child); + if (Node) + ParagraphComment->Children.emplace_back(std::move(Node)); + } + if (ParagraphComment->Children.empty()) + return nullptr; + return std::move(ParagraphComment); } else if (I.Kind == "TextComment") { - if (I.Text != "") - OS << I.Text; + if (I.Text == "") + return nullptr; + return llvm::make_unique(I.Text, true); + } + return nullptr; +} + +static std::unique_ptr genHTML(const std::vector &C) { + auto CommentBlock = llvm::make_unique(HTMLTag::TAG_DIV); + for (const auto &Child : C) { + std::unique_ptr Node = genHTML(Child); + if (Node) + CommentBlock->Children.emplace_back(std::move(Node)); } + return CommentBlock; } -void genHTML(const EnumInfo &I, llvm::raw_ostream &OS) { +static std::vector> genHTML(const EnumInfo &I) { + std::vector> Out; std::string EnumType; if (I.Scoped) EnumType = "enum class "; else EnumType = "enum "; - writeHeader(EnumType + I.Name, "3", OS); - std::string Buffer; - llvm::raw_string_ostream Members(Buffer); - if (!I.Members.empty()) { - Members << "\n"; - for (const auto &N : I.Members) - Members << genTag(N, "li") << "\n"; - OS << genTag(Members.str(), "ul") << "\n"; - } + Out.emplace_back( + llvm::make_unique(HTMLTag::TAG_H3, EnumType + I.Name)); + + std::unique_ptr Node = genEnumMembersBlock(I.Members); + if (Node) + Out.emplace_back(std::move(Node)); if (I.DefLoc) - writeFileDefinition(I.DefLoc.getValue(), OS); + Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); + + std::string Description; + if (!I.Description.empty()) + Out.emplace_back(genHTML(I.Description)); - for (const auto &C : I.Description) - writeDescription(C, OS); + return Out; } -void genHTML(const FunctionInfo &I, llvm::raw_ostream &OS) { - writeHeader(I.Name, "3", OS); +static std::vector> genHTML(const FunctionInfo &I) { + std::vector> Out; + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H3, I.Name)); std::string Buffer; llvm::raw_string_ostream Stream(Buffer); @@ -87,101 +346,101 @@ Stream << ", "; Stream << P.Type.Name + " " + P.Name; } + std::string Access = getAccess(I.Access); if (Access != "") - writeLine(Access + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + - Stream.str() + ")", - OS); - else - writeLine(I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", - OS); + Access = Access + " "; + + Out.emplace_back(llvm::make_unique( + HTMLTag::TAG_P, Access + I.ReturnType.Type.Name + " " + I.Name + "(" + + Stream.str() + ")")); + if (I.DefLoc) - writeFileDefinition(I.DefLoc.getValue(), OS); + Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); - for (const auto &C : I.Description) - writeDescription(C, OS); + std::string Description; + if (!I.Description.empty()) + Out.emplace_back(genHTML(I.Description)); + + return Out; } -void genHTML(const NamespaceInfo &I, llvm::raw_ostream &OS) { - if (I.Name == "") - writeHeader("Global Namespace", "1", OS); +static std::vector> genHTML(const NamespaceInfo &I, + std::string &InfoTitle) { + std::vector> Out; + if (I.Name.str() == "") + InfoTitle = "Global Namespace"; else - writeHeader("namespace " + I.Name, "1", OS); + InfoTitle = ("namespace " + I.Name).str(); - if (!I.Description.empty()) { - for (const auto &C : I.Description) - writeDescription(C, OS); - } + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H1, InfoTitle)); - if (!I.ChildNamespaces.empty()) { - writeHeader("Namespaces", "2", OS); - for (const auto &R : I.ChildNamespaces) - writeLine(R.Name, OS); - } - if (!I.ChildRecords.empty()) { - writeHeader("Records", "2", OS); - for (const auto &R : I.ChildRecords) - writeLine(R.Name, OS); - } - if (!I.ChildFunctions.empty()) { - writeHeader("Functions", "2", OS); - for (const auto &F : I.ChildFunctions) - genHTML(F, OS); - } - if (!I.ChildEnums.empty()) { - writeHeader("Enums", "2", OS); - for (const auto &E : I.ChildEnums) - genHTML(E, OS); - } + std::string Description; + if (!I.Description.empty()) + Out.emplace_back(genHTML(I.Description)); + + std::vector> ChildNamespaces = + genReferencesBlock(I.ChildNamespaces, "Namespaces"); + std::move(ChildNamespaces.begin(), ChildNamespaces.end(), + std::back_inserter(Out)); + std::vector> ChildRecords = + genReferencesBlock(I.ChildRecords, "Records"); + std::move(ChildRecords.begin(), ChildRecords.end(), std::back_inserter(Out)); + + std::vector> ChildFunctions = + genFunctionsBlock(I.ChildFunctions); + std::move(ChildFunctions.begin(), ChildFunctions.end(), + std::back_inserter(Out)); + std::vector> ChildEnums = + genEnumsBlock(I.ChildEnums); + std::move(ChildEnums.begin(), ChildEnums.end(), std::back_inserter(Out)); + + return Out; } -void genHTML(const RecordInfo &I, llvm::raw_ostream &OS) { - writeHeader(getTagType(I.TagType) + " " + I.Name, "1", OS); +static std::vector> genHTML(const RecordInfo &I, + std::string &InfoTitle) { + std::vector> Out; + InfoTitle = (getTagType(I.TagType) + " " + I.Name).str(); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H1, InfoTitle)); + if (I.DefLoc) - writeFileDefinition(I.DefLoc.getValue(), OS); + Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); - if (!I.Description.empty()) { - for (const auto &C : I.Description) - writeDescription(C, OS); - } + std::string Description; + if (!I.Description.empty()) + Out.emplace_back(genHTML(I.Description)); std::string Parents = genReferenceList(I.Parents); std::string VParents = genReferenceList(I.VirtualParents); if (!Parents.empty() || !VParents.empty()) { if (Parents.empty()) - writeLine("Inherits from " + VParents, OS); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P, + "Inherits from " + VParents)); else if (VParents.empty()) - writeLine("Inherits from " + Parents, OS); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P, + "Inherits from " + Parents)); else - writeLine("Inherits from " + Parents + ", " + VParents, OS); + Out.emplace_back(llvm::make_unique( + HTMLTag::TAG_P, "Inherits from " + Parents + ", " + VParents)); } - if (!I.Members.empty()) { - writeHeader("Members", "2", OS); - for (const auto Member : I.Members) { - std::string Access = getAccess(Member.Access); - if (Access != "") - writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS); - else - writeLine(Member.Type.Name + " " + Member.Name, OS); - } - } + std::vector> Members = + genRecordMembersBlock(I.Members); + std::move(Members.begin(), Members.end(), std::back_inserter(Out)); + std::vector> ChildRecords = + genReferencesBlock(I.ChildRecords, "Records"); + std::move(ChildRecords.begin(), ChildRecords.end(), std::back_inserter(Out)); - if (!I.ChildRecords.empty()) { - writeHeader("Records", "2", OS); - for (const auto &R : I.ChildRecords) - writeLine(R.Name, OS); - } - if (!I.ChildFunctions.empty()) { - writeHeader("Functions", "2", OS); - for (const auto &F : I.ChildFunctions) - genHTML(F, OS); - } - if (!I.ChildEnums.empty()) { - writeHeader("Enums", "2", OS); - for (const auto &E : I.ChildEnums) - genHTML(E, OS); - } + std::vector> ChildFunctions = + genFunctionsBlock(I.ChildFunctions); + std::move(ChildFunctions.begin(), ChildFunctions.end(), + std::back_inserter(Out)); + std::vector> ChildEnums = + genEnumsBlock(I.ChildEnums); + std::move(ChildEnums.begin(), ChildEnums.end(), std::back_inserter(Out)); + + return Out; } /// Generator for HTML documentation. @@ -195,31 +454,64 @@ const char *HTMLGenerator::Format = "html"; llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { + HTMLFile F; + + auto MetaNode = llvm::make_unique(HTMLTag::TAG_META); + MetaNode->Attributes.try_emplace("charset", "utf-8"); + F.Children.push_back(std::move(MetaNode)); + + std::string InfoTitle; + Info CastedInfo; + auto MainContentNode = llvm::make_unique(HTMLTag::TAG_DIV); switch (I->IT) { - case InfoType::IT_namespace: - genHTML(*static_cast(I), OS); + case InfoType::IT_namespace: { + std::vector> Nodes = + genHTML(*static_cast(I), InfoTitle); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; - case InfoType::IT_record: - genHTML(*static_cast(I), OS); + } + case InfoType::IT_record: { + std::vector> Nodes = + genHTML(*static_cast(I), InfoTitle); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; - case InfoType::IT_enum: - genHTML(*static_cast(I), OS); + } + case InfoType::IT_enum: { + std::vector> Nodes = + genHTML(*static_cast(I)); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; - case InfoType::IT_function: - genHTML(*static_cast(I), OS); + } + case InfoType::IT_function: { + std::vector> Nodes = + genHTML(*static_cast(I)); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; + } case InfoType::IT_default: return llvm::make_error("Unexpected info type.\n", llvm::inconvertibleErrorCode()); } + // std::move(Nodes.begin(), Nodes.end(), std::back_inserter(MainContentNode)); + + F.Children.emplace_back( + llvm::make_unique(HTMLTag::TAG_TITLE, InfoTitle)); + F.Children.push_back(std::move(MainContentNode)); + + F.Render(OS); + return llvm::Error::success(); } static GeneratorRegistry::Add HTML(HTMLGenerator::Format, "Generator for HTML output."); -// This anchor is used to force the linker to link in the generated object file -// and thus register the generator. +// This anchor is used to force the linker to link in the generated object +// file and thus register the generator. volatile int HTMLGeneratorAnchorSource = 0; } // namespace doc Index: clang-tools-extra/clang-doc/MDGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/MDGenerator.cpp +++ clang-tools-extra/clang-doc/MDGenerator.cpp @@ -20,11 +20,11 @@ // Markdown generation -std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; } +static std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; } -std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; } +static std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; } -std::string genLink(const Twine &Text, const Twine &Link) { +static std::string genLink(const Twine &Text, const Twine &Link) { return "[" + Text.str() + "](" + Link.str() + ")"; } @@ -92,7 +92,7 @@ } } -void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) { +static void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) { if (I.Scoped) writeLine("| enum class " + I.Name + " |", OS); else @@ -112,7 +112,7 @@ writeDescription(C, OS); } -void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) { +static void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) { std::string Buffer; llvm::raw_string_ostream Stream(Buffer); bool First = true; @@ -139,7 +139,7 @@ writeDescription(C, OS); } -void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) { +static void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) { if (I.Name == "") writeHeader("Global Namespace", 1, OS); else @@ -178,7 +178,7 @@ } } -void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) { +static void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) { writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS); if (I.DefLoc) writeFileDefinition(I.DefLoc.getValue(), OS); Index: clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp +++ clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp @@ -40,16 +40,31 @@ llvm::raw_string_ostream Actual(Buffer); auto Err = G->generateDocForInfo(&I, Actual); assert(!Err); - std::string Expected = R"raw(

namespace Namespace

-

Namespaces

-

ChildNamespace

-

Records

-

ChildStruct

-

Functions

-

OneFunction

-

OneFunction()

-

Enums

-

enum OneEnum

+ std::string Expected = R"raw( + +namespace Namespace +
+

namespace Namespace

+

Namespaces

+
    +
  • ChildNamespace
  • +
+

Records

+
    +
  • ChildStruct
  • +
+

Functions

+
+

OneFunction

+

+ OneFunction() +

+
+

Enums

+
+

enum OneEnum

+
+
)raw"; EXPECT_EQ(Expected, Actual.str()); @@ -80,18 +95,37 @@ llvm::raw_string_ostream Actual(Buffer); auto Err = G->generateDocForInfo(&I, Actual); assert(!Err); - std::string Expected = R"raw(

class r

-

Defined at line 10 of test.cpp

-

Inherits from F, G

-

Members

-

private int X

-

Records

-

ChildStruct

-

Functions

-

OneFunction

-

OneFunction()

-

Enums

-

enum OneEnum

+ std::string Expected = R"raw( + +class r +
+

class r

+

+ Defined at line 10 of test.cpp +

+

+ Inherits from F, G +

+

Members

+
    +
  • private int X
  • +
+

Records

+
    +
  • ChildStruct
  • +
+

Functions

+
+

OneFunction

+

+ OneFunction() +

+
+

Enums

+
+

enum OneEnum

+
+
)raw"; EXPECT_EQ(Expected, Actual.str()); @@ -116,9 +150,18 @@ llvm::raw_string_ostream Actual(Buffer); auto Err = G->generateDocForInfo(&I, Actual); assert(!Err); - std::string Expected = R"raw(

f

-

void f(int P)

-

Defined at line 10 of test.cpp

+ std::string Expected = R"raw( + + +
+

f

+

+ void f(int P) +

+

+ Defined at line 10 of test.cpp +

+
)raw"; EXPECT_EQ(Expected, Actual.str()); @@ -141,11 +184,18 @@ llvm::raw_string_ostream Actual(Buffer); auto Err = G->generateDocForInfo(&I, Actual); assert(!Err); - std::string Expected = R"raw(

enum class e

-
    -
  • X
  • -
-

Defined at line 10 of test.cpp

+ std::string Expected = R"raw( + + +
+

enum class e

+
    +
  • X
  • +
+

+ Defined at line 10 of test.cpp +

+
)raw"; EXPECT_EQ(Expected, Actual.str()); @@ -194,12 +244,29 @@ llvm::raw_string_ostream Actual(Buffer); auto Err = G->generateDocForInfo(&I, Actual); assert(!Err); - std::string Expected = R"raw(

f

-

void f(int I, int J)

-

Defined at line 10 of test.cpp

-

-

Brief description.

-

Extended description that continues onto the next line.

+ std::string Expected = R"raw( + + +
+

f

+

+ void f(int I, int J) +

+

+ Defined at line 10 of test.cpp +

+
+
+

+ Brief description. +

+

+ Extended description that + continues onto the next line. +

+
+
+
)raw"; EXPECT_EQ(Expected, Actual.str());