DiegoAstiazaran updated this revision to Diff 207148. DiegoAstiazaran marked 6 inline comments as done. DiegoAstiazaran added a comment.
Make DoctypeDecl a constexpr. Early exit in TagNode::Render. Functions of HTMLGenerator now return HTMLNodes instead of adding children to a Node passed by reference. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D63857/new/ https://reviews.llvm.org/D63857 Files: clang-tools-extra/clang-doc/HTMLGenerator.cpp clang-tools-extra/clang-doc/MDGenerator.cpp clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
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(<h1>namespace Namespace</h1> -<h2>Namespaces</h2> -<p>ChildNamespace</p> -<h2>Records</h2> -<p>ChildStruct</p> -<h2>Functions</h2> -<h3>OneFunction</h3> -<p> OneFunction()</p> -<h2>Enums</h2> -<h3>enum OneEnum</h3> + std::string Expected = R"raw(<!DOCTYPE html> +<meta charset="utf-8"/> +<title>namespace Namespace</title> +<div> + <h1>namespace Namespace</h1> + <h2>Namespaces</h2> + <ul> + <li>ChildNamespace</li> + </ul> + <h2>Records</h2> + <ul> + <li>ChildStruct</li> + </ul> + <h2>Functions</h2> + <div> + <h3>OneFunction</h3> + <p> + OneFunction() + </p> + </div> + <h2>Enums</h2> + <div> + <h3>enum OneEnum</h3> + </div> +</div> )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(<h1>class r</h1> -<p>Defined at line 10 of test.cpp</p> -<p>Inherits from F, G</p> -<h2>Members</h2> -<p>private int X</p> -<h2>Records</h2> -<p>ChildStruct</p> -<h2>Functions</h2> -<h3>OneFunction</h3> -<p> OneFunction()</p> -<h2>Enums</h2> -<h3>enum OneEnum</h3> + std::string Expected = R"raw(<!DOCTYPE html> +<meta charset="utf-8"/> +<title>class r</title> +<div> + <h1>class r</h1> + <p> + Defined at line 10 of test.cpp + </p> + <p> + Inherits from F, G + </p> + <h2>Members</h2> + <ul> + <li>private int X</li> + </ul> + <h2>Records</h2> + <ul> + <li>ChildStruct</li> + </ul> + <h2>Functions</h2> + <div> + <h3>OneFunction</h3> + <p> + OneFunction() + </p> + </div> + <h2>Enums</h2> + <div> + <h3>enum OneEnum</h3> + </div> +</div> )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(<h3>f</h3> -<p>void f(int P)</p> -<p>Defined at line 10 of test.cpp</p> + std::string Expected = R"raw(<!DOCTYPE html> +<meta charset="utf-8"/> +<title></title> +<div> + <h3>f</h3> + <p> + void f(int P) + </p> + <p> + Defined at line 10 of test.cpp + </p> +</div> )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(<h3>enum class e</h3> -<ul> -<li>X</li> -</ul> -<p>Defined at line 10 of test.cpp</p> + std::string Expected = R"raw(<!DOCTYPE html> +<meta charset="utf-8"/> +<title></title> +<div> + <h3>enum class e</h3> + <ul> + <li>X</li> + </ul> + <p> + Defined at line 10 of test.cpp + </p> +</div> )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(<h3>f</h3> -<p>void f(int I, int J)</p> -<p>Defined at line 10 of test.cpp</p> -<p></p> -<p> Brief description.</p> -<p> Extended description that continues onto the next line.</p> + std::string Expected = R"raw(<!DOCTYPE html> +<meta charset="utf-8"/> +<title></title> +<div> + <h3>f</h3> + <p> + void f(int I, int J) + </p> + <p> + Defined at line 10 of test.cpp + </p> + <div> + <div> + <p> + Brief description. + </p> + <p> + Extended description that + continues onto the next line. + </p> + </div> + </div> +</div> )raw"; EXPECT_EQ(Expected, Actual.str()); 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/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<TextNode>(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<std::unique_ptr<HTMLNode>> Children; // List of child nodes + llvm::StringMap<llvm::SmallString<16>> + Attributes; // List of key-value attributes for tag + + void Render(llvm::raw_ostream &OS, int IndentationLevel) override; +}; + +constexpr const char *kDoctypeDecl = "<!DOCTYPE html>"; + +struct HTMLFile { + std::vector<std::unique_ptr<HTMLNode>> 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 << "</" << Tag.ToString() << ">"; +} + // HTML generation -std::string genTag(const Twine &Text, const Twine &Tag) { - return "<" + Tag.str() + ">" + Text.str() + "</" + Tag.str() + ">"; +static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I); +static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I); + +static std::vector<std::unique_ptr<TagNode>> +genEnumsBlock(const std::vector<EnumInfo> &Enums) { + if (Enums.empty()) + return {}; + + std::vector<std::unique_ptr<TagNode>> Out; + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Enums")); + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV)); + for (const auto &E : Enums) { + std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(Out.back()->Children)); + } + return Out; +} + +static std::unique_ptr<TagNode> +genEnumMembersBlock(const llvm::SmallVector<SmallString<16>, 4> &Members) { + if (Members.empty()) + return nullptr; + + auto List = llvm::make_unique<TagNode>(HTMLTag::TAG_UL); + for (const auto &M : Members) + List->Children.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_LI, M)); + return List; +} + +static std::vector<std::unique_ptr<TagNode>> +genFunctionsBlock(const std::vector<FunctionInfo> &Functions) { + if (Functions.empty()) + return {}; + + std::vector<std::unique_ptr<TagNode>> Out; + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions")); + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV)); + for (const auto &F : Functions) { + std::vector<std::unique_ptr<TagNode>> 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<std::unique_ptr<TagNode>> +genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members) { + if (Members.empty()) + return {}; + + std::vector<std::unique_ptr<TagNode>> Out; + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Members")); + Out.emplace_back(llvm::make_unique<TagNode>(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<TagNode>( + 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<std::unique_ptr<TagNode>> +genReferencesBlock(const std::vector<Reference> &References, + llvm::StringRef Title) { + if (References.empty()) + return {}; + + std::vector<std::unique_ptr<TagNode>> Out; + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, Title)); + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL)); + for (const auto &R : References) + Out.back()->Children.emplace_back( + llvm::make_unique<TagNode>(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<TagNode> writeFileDefinition(const Location &L) { + return llvm::make_unique<TagNode>( + 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<HTMLNode> genHTML(const CommentInfo &I) { if (I.Kind == "FullComment") { - for (const auto &Child : I.Children) - writeDescription(*Child, OS); + auto FullComment = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); + for (const auto &Child : I.Children) { + std::unique_ptr<HTMLNode> Node = genHTML(*Child); + if (Node) + FullComment->Children.emplace_back(std::move(Node)); + } + return std::move(FullComment); } else if (I.Kind == "ParagraphComment") { - OS << "<p>"; - for (const auto &Child : I.Children) - writeDescription(*Child, OS); - OS << "</p>\n"; + auto ParagraphComment = llvm::make_unique<TagNode>(HTMLTag::TAG_P); + for (const auto &Child : I.Children) { + std::unique_ptr<HTMLNode> 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<TextNode>(I.Text, true); + } + return nullptr; +} + +static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) { + auto CommentBlock = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); + for (const auto &Child : C) { + std::unique_ptr<HTMLNode> 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<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I) { + std::vector<std::unique_ptr<TagNode>> 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<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name)); + + std::unique_ptr<TagNode> 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<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I) { + std::vector<std::unique_ptr<TagNode>> Out; + Out.emplace_back(llvm::make_unique<TagNode>(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<TagNode>( + 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<std::unique_ptr<TagNode>> genHTML(const NamespaceInfo &I, + std::string &InfoTitle) { + std::vector<std::unique_ptr<TagNode>> 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<TagNode>(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<std::unique_ptr<TagNode>> ChildNamespaces = + genReferencesBlock(I.ChildNamespaces, "Namespaces"); + std::move(ChildNamespaces.begin(), ChildNamespaces.end(), + std::back_inserter(Out)); + std::vector<std::unique_ptr<TagNode>> ChildRecords = + genReferencesBlock(I.ChildRecords, "Records"); + std::move(ChildRecords.begin(), ChildRecords.end(), std::back_inserter(Out)); + + std::vector<std::unique_ptr<TagNode>> ChildFunctions = + genFunctionsBlock(I.ChildFunctions); + std::move(ChildFunctions.begin(), ChildFunctions.end(), + std::back_inserter(Out)); + std::vector<std::unique_ptr<TagNode>> 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<std::unique_ptr<TagNode>> genHTML(const RecordInfo &I, + std::string &InfoTitle) { + std::vector<std::unique_ptr<TagNode>> Out; + InfoTitle = (getTagType(I.TagType) + " " + I.Name).str(); + Out.emplace_back(llvm::make_unique<TagNode>(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<TagNode>(HTMLTag::TAG_P, + "Inherits from " + VParents)); else if (VParents.empty()) - writeLine("Inherits from " + Parents, OS); + Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P, + "Inherits from " + Parents)); else - writeLine("Inherits from " + Parents + ", " + VParents, OS); + Out.emplace_back(llvm::make_unique<TagNode>( + 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<std::unique_ptr<TagNode>> Members = + genRecordMembersBlock(I.Members); + std::move(Members.begin(), Members.end(), std::back_inserter(Out)); + std::vector<std::unique_ptr<TagNode>> 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<std::unique_ptr<TagNode>> ChildFunctions = + genFunctionsBlock(I.ChildFunctions); + std::move(ChildFunctions.begin(), ChildFunctions.end(), + std::back_inserter(Out)); + std::vector<std::unique_ptr<TagNode>> 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<TagNode>(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<TagNode>(HTMLTag::TAG_DIV); switch (I->IT) { - case InfoType::IT_namespace: - genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), OS); + case InfoType::IT_namespace: { + std::vector<std::unique_ptr<TagNode>> Nodes = + genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), InfoTitle); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; - case InfoType::IT_record: - genHTML(*static_cast<clang::doc::RecordInfo *>(I), OS); + } + case InfoType::IT_record: { + std::vector<std::unique_ptr<TagNode>> Nodes = + genHTML(*static_cast<clang::doc::RecordInfo *>(I), InfoTitle); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; - case InfoType::IT_enum: - genHTML(*static_cast<clang::doc::EnumInfo *>(I), OS); + } + case InfoType::IT_enum: { + std::vector<std::unique_ptr<TagNode>> Nodes = + genHTML(*static_cast<clang::doc::EnumInfo *>(I)); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; - case InfoType::IT_function: - genHTML(*static_cast<clang::doc::FunctionInfo *>(I), OS); + } + case InfoType::IT_function: { + std::vector<std::unique_ptr<TagNode>> Nodes = + genHTML(*static_cast<clang::doc::FunctionInfo *>(I)); + std::move(Nodes.begin(), Nodes.end(), + std::back_inserter(MainContentNode->Children)); break; + } case InfoType::IT_default: return llvm::make_error<llvm::StringError>("Unexpected info type.\n", llvm::inconvertibleErrorCode()); } + // std::move(Nodes.begin(), Nodes.end(), std::back_inserter(MainContentNode)); + + F.Children.emplace_back( + llvm::make_unique<TagNode>(HTMLTag::TAG_TITLE, InfoTitle)); + F.Children.push_back(std::move(MainContentNode)); + + F.Render(OS); + return llvm::Error::success(); } static GeneratorRegistry::Add<HTMLGenerator> 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
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits