DiegoAstiazaran created this revision.
DiegoAstiazaran added reviewers: juliehockett, jakehehrlich, lebedev.ri.
DiegoAstiazaran added a project: clang-tools-extra.

Nodes are used to represent each part of the HTML file. There are TagNodes that 
represent every HTML tag (p, h1, div, ...) and they have children nodes, which 
can be TagNodes or TextNodes (these nodes only have text).
Proper indentation is now rendered within the file.

Depends on D63180 <https://reviews.llvm.org/D63180>.


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
@@ -28,23 +28,23 @@
   return "[" + Text.str() + "](" + Link.str() + ")";
 }
 
-static void writeLine(const Twine &Text, raw_ostream &OS) {
+void writeLine(const Twine &Text, raw_ostream &OS) {
   OS << Text << "\n\n";
 }
 
-static void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
+void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
 
-static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
+void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
   OS << std::string(Num, '#') + " " + Text << "\n\n";
 }
 
-static void writeFileDefinition(const Location &L, raw_ostream &OS) {
+void writeFileDefinition(const Location &L, raw_ostream &OS) {
   OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " +
                   L.Filename)
      << "\n\n";
 }
 
-static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
+void writeDescription(const CommentInfo &I, raw_ostream &OS) {
   if (I.Kind == "FullComment") {
     for (const auto &Child : I.Children)
       writeDescription(*Child, 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,280 @@
 namespace clang {
 namespace doc {
 
+class HTMLTag {
+public:
+  // Any other tag can be added if required
+  enum Tag {
+    meta,
+    title,
+    div,
+    h1,
+    h2,
+    h3,
+    p,
+    ul,
+    li,
+  };
+
+  HTMLTag() = default;
+  constexpr HTMLTag(Tag Value) : Value(Value) {}
+
+  operator Tag() const { return Value; }
+  operator bool() = delete;
+
+  bool IsSelfClosing() const {
+    switch (Value) {
+    case HTMLTag::meta:
+      return true;
+    case HTMLTag::title:
+    case HTMLTag::div:
+    case HTMLTag::h1:
+    case HTMLTag::h2:
+    case HTMLTag::h3:
+    case HTMLTag::p:
+    case HTMLTag::ul:
+    case HTMLTag::li:
+      return false;
+    }
+  }
+
+  bool HasInlineChildren() const {
+    switch (Value) {
+    case HTMLTag::meta:
+    case HTMLTag::title:
+    case HTMLTag::h1:
+    case HTMLTag::h2:
+    case HTMLTag::h3:
+    case HTMLTag::li:
+      return true;
+    case HTMLTag::div:
+    case HTMLTag::p:
+    case HTMLTag::ul:
+      return false;
+    }
+  }
+
+  llvm::SmallString<16> ToString() const {
+    switch (Value) {
+    case HTMLTag::meta:
+      return llvm::SmallString<16>("meta");
+    case HTMLTag::title:
+      return llvm::SmallString<16>("title");
+    case HTMLTag::div:
+      return llvm::SmallString<16>("div");
+    case HTMLTag::h1:
+      return llvm::SmallString<16>("h1");
+    case HTMLTag::h2:
+      return llvm::SmallString<16>("h2");
+    case HTMLTag::h3:
+      return llvm::SmallString<16>("h3");
+    case HTMLTag::p:
+      return llvm::SmallString<16>("p");
+    case HTMLTag::ul:
+      return llvm::SmallString<16>("ul");
+    case HTMLTag::li:
+      return llvm::SmallString<16>("li");
+    }
+  }
+
+private:
+  Tag 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 {
+    if (Indented)
+      OS.indent(IndentationLevel * 2);
+    OS << Text;
+  }
+};
+
+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 {
+    OS.indent(IndentationLevel * 2);
+    OS << "<" << Tag.ToString();
+    for (const auto &A : Attributes)
+      OS << " " << A.getKey() << "=\"" << A.getValue() << "\"";
+    OS << (SelfClosing ? "/>" : ">");
+    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);
+    if (!SelfClosing)
+      OS << "</" << Tag.ToString() << ">";
+  }
+};
+
+struct HTMLFile {
+  llvm::SmallString<16> DoctypeDecl{"<!DOCTYPE html>"};
+  std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes
+  void Render(llvm::raw_ostream &OS) {
+    OS << DoctypeDecl << "\n";
+    for (const auto &C : Children) {
+      C->Render(OS, 0);
+      OS << "\n";
+    }
+  }
+};
+
 // HTML generation
 
-std::string genTag(const Twine &Text, const Twine &Tag) {
-  return "<" + Tag.str() + ">" + Text.str() + "</" + Tag.str() + ">";
+void genHTML(const EnumInfo &I, TagNode *N);
+void genHTML(const FunctionInfo &I, TagNode *N);
+
+void genEnumsBlock(const std::vector<EnumInfo> &Enums, TagNode *N) {
+  if (Enums.empty())
+    return;
+
+  N->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::h2, "Enums"));
+
+  auto EnumsBlock = llvm::make_unique<TagNode>(HTMLTag::div);
+  for (const auto &E : Enums)
+    genHTML(E, EnumsBlock.get());
+  N->Children.push_back(std::move(EnumsBlock));
+}
+
+void genEnumMembersBlock(const llvm::SmallVector<SmallString<16>, 4> &Members,
+                         TagNode *N) {
+  if (Members.empty())
+    return;
+  auto List = llvm::make_unique<TagNode>(HTMLTag::ul);
+  for (const auto &M : Members)
+    List->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::li, M));
+  N->Children.push_back(std::move(List));
 }
 
-static void writeLine(const Twine &Text, raw_ostream &OS) {
-  OS << genTag(Text, "p") << "\n";
+void genFunctionsBlock(const std::vector<FunctionInfo> &Functions, TagNode *N) {
+  if (Functions.empty())
+    return;
+
+  N->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::h2, "Functions"));
+
+  auto FunctionsBlock = llvm::make_unique<TagNode>(HTMLTag::div);
+  for (const auto &F : Functions)
+    genHTML(F, FunctionsBlock.get());
+  N->Children.push_back(std::move(FunctionsBlock));
+}
+
+void genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
+                           TagNode *N) {
+  if (Members.empty())
+    return;
+
+  N->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::h2, "Members"));
+
+  auto List = llvm::make_unique<TagNode>(HTMLTag::ul);
+  for (const auto &M : Members) {
+    std::string Access = getAccess(M.Access);
+    if (Access != "")
+      Access = Access + " ";
+    List->Children.push_back(llvm::make_unique<TagNode>(
+        HTMLTag::li, Access + M.Type.Name + " " + M.Name));
+  }
+  N->Children.push_back(std::move(List));
 }
 
-static void writeHeader(const Twine &Text, const Twine &Num, raw_ostream &OS) {
-  OS << genTag(Text, "h" + Num) << "\n";
+void genReferencesBlock(const std::vector<Reference> &References,
+                        llvm::StringRef Title, TagNode *N) {
+  if (References.empty())
+    return;
+
+  N->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::h2, Title));
+
+  auto List = llvm::make_unique<TagNode>(HTMLTag::ul);
+  for (const auto &R : References)
+    List->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::li, R.Name));
+  N->Children.push_back(std::move(List));
 }
 
-static void writeFileDefinition(const Location &L, raw_ostream &OS) {
-  writeLine("Defined at line " + std::to_string(L.LineNumber) + " of " +
-                L.Filename,
-            OS);
+void writeFileDefinition(const Location &L, TagNode *N) {
+  N->Children.push_back(llvm::make_unique<TagNode>(
+      HTMLTag::p,
+      "Defined at line " + std::to_string(L.LineNumber) + " of " + L.Filename));
 }
 
-static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
+void genHTML(const CommentInfo &I, TagNode *N) {
   if (I.Kind == "FullComment") {
+    auto FullComment = llvm::make_unique<TagNode>(HTMLTag::div);
     for (const auto &Child : I.Children)
-      writeDescription(*Child, OS);
+      genHTML(*Child, FullComment.get());
+    N->Children.push_back(std::move(FullComment));
   } else if (I.Kind == "ParagraphComment") {
-    OS << "<p>";
+    auto ParagraphComment = llvm::make_unique<TagNode>(HTMLTag::p);
     for (const auto &Child : I.Children)
-      writeDescription(*Child, OS);
-    OS << "</p>\n";
+      genHTML(*Child, ParagraphComment.get());
+    if (!ParagraphComment->Children.empty())
+      N->Children.push_back(std::move(ParagraphComment));
   } else if (I.Kind == "TextComment") {
     if (I.Text != "")
-      OS << I.Text;
+      N->Children.push_back(llvm::make_unique<TextNode>(I.Text, true));
   }
 }
 
-void genHTML(const EnumInfo &I, llvm::raw_ostream &OS) {
+void genHTML(const std::vector<CommentInfo> &C, TagNode *N) {
+  auto CommentBlock = llvm::make_unique<TagNode>(HTMLTag::div);
+  for (const auto &Child : C)
+    genHTML(Child, CommentBlock.get());
+  N->Children.push_back(std::move(CommentBlock));
+}
+
+void genHTML(const EnumInfo &I, TagNode *N) {
   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";
-  }
+  N->Children.push_back(
+      llvm::make_unique<TagNode>(HTMLTag::h3, EnumType + I.Name));
+
+  genEnumMembersBlock(I.Members, N);
 
   if (I.DefLoc)
-    writeFileDefinition(I.DefLoc.getValue(), OS);
+    writeFileDefinition(I.DefLoc.getValue(), N);
 
-  for (const auto &C : I.Description)
-    writeDescription(C, OS);
+  std::string Description;
+  if (!I.Description.empty())
+    genHTML(I.Description, N);
 }
 
-void genHTML(const FunctionInfo &I, llvm::raw_ostream &OS) {
-  writeHeader(I.Name, "3", OS);
+void genHTML(const FunctionInfo &I, TagNode *N) {
+  N->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::h3, I.Name));
 
   std::string Buffer;
   llvm::raw_string_ostream Stream(Buffer);
@@ -87,101 +300,72 @@
       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 + " ";
+
+  N->Children.push_back(llvm::make_unique<TagNode>(
+      HTMLTag::p, Access + I.ReturnType.Type.Name + " " + I.Name + "(" +
+                      Stream.str() + ")"));
+
   if (I.DefLoc)
-    writeFileDefinition(I.DefLoc.getValue(), OS);
+    writeFileDefinition(I.DefLoc.getValue(), N);
 
-  for (const auto &C : I.Description)
-    writeDescription(C, OS);
+  std::string Description;
+  if (!I.Description.empty())
+    genHTML(I.Description, N);
 }
 
-void genHTML(const NamespaceInfo &I, llvm::raw_ostream &OS) {
-  if (I.Name == "")
-    writeHeader("Global Namespace", "1", OS);
+void genHTML(const NamespaceInfo &I, TagNode *N, std::string &InfoTitle) {
+  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);
-  }
+  N->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::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())
+    genHTML(I.Description, N);
+
+  genReferencesBlock(I.ChildNamespaces, "Namespaces", N);
+  genReferencesBlock(I.ChildRecords, "Records", N);
+
+  genFunctionsBlock(I.ChildFunctions, N);
+  genEnumsBlock(I.ChildEnums, N);
 }
 
-void genHTML(const RecordInfo &I, llvm::raw_ostream &OS) {
-  writeHeader(getTagType(I.TagType) + " " + I.Name, "1", OS);
+void genHTML(const RecordInfo &I, TagNode *N, std::string &InfoTitle) {
+  InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
+  N->Children.push_back(llvm::make_unique<TagNode>(HTMLTag::h1, InfoTitle));
+
   if (I.DefLoc)
-    writeFileDefinition(I.DefLoc.getValue(), OS);
+    writeFileDefinition(I.DefLoc.getValue(), N);
 
-  if (!I.Description.empty()) {
-    for (const auto &C : I.Description)
-      writeDescription(C, OS);
-  }
+  std::string Description;
+  if (!I.Description.empty())
+    genHTML(I.Description, N);
 
   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);
+      N->Children.push_back(
+          llvm::make_unique<TagNode>(HTMLTag::p, "Inherits from " + VParents));
     else if (VParents.empty())
-      writeLine("Inherits from " + Parents, OS);
+      N->Children.push_back(
+          llvm::make_unique<TagNode>(HTMLTag::p, "Inherits from " + Parents));
     else
-      writeLine("Inherits from " + Parents + ", " + VParents, OS);
+      N->Children.push_back(llvm::make_unique<TagNode>(
+          HTMLTag::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);
-    }
-  }
+  genRecordMembersBlock(I.Members, N);
+  genReferencesBlock(I.ChildRecords, "Records", N);
 
-  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);
-  }
+  genFunctionsBlock(I.ChildFunctions, N);
+  genEnumsBlock(I.ChildEnums, N);
 }
 
 /// Generator for HTML documentation.
@@ -195,31 +379,49 @@
 const char *HTMLGenerator::Format = "html";
 
 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
+  HTMLFile F;
+
+  auto MetaNode = llvm::make_unique<TagNode>(HTMLTag::meta);
+  MetaNode->Attributes.try_emplace("charset", "utf-8");
+  F.Children.push_back(std::move(MetaNode));
+
+  std::string InfoTitle;
+
+  auto MainContentNode = llvm::make_unique<TagNode>(HTMLTag::div);
+
   switch (I->IT) {
   case InfoType::IT_namespace:
-    genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), OS);
+    genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), MainContentNode.get(),
+            InfoTitle);
     break;
   case InfoType::IT_record:
-    genHTML(*static_cast<clang::doc::RecordInfo *>(I), OS);
+    genHTML(*static_cast<clang::doc::RecordInfo *>(I), MainContentNode.get(),
+            InfoTitle);
     break;
   case InfoType::IT_enum:
-    genHTML(*static_cast<clang::doc::EnumInfo *>(I), OS);
+    genHTML(*static_cast<clang::doc::EnumInfo *>(I), MainContentNode.get());
     break;
   case InfoType::IT_function:
-    genHTML(*static_cast<clang::doc::FunctionInfo *>(I), OS);
+    genHTML(*static_cast<clang::doc::FunctionInfo *>(I), MainContentNode.get());
     break;
   case InfoType::IT_default:
     return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
                                                llvm::inconvertibleErrorCode());
   }
+
+  F.Children.push_back(llvm::make_unique<TagNode>(HTMLTag::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

Reply via email to