DiegoAstiazaran created this revision.
DiegoAstiazaran added reviewers: juliehockett, jakehehrlich, lebedev.ri.
DiegoAstiazaran added a project: clang-tools-extra.
Herald added subscribers: kadircet, arphaman, mgorny.

Implement a simple HMTL generator based on the existing Markdown generator.
Basic HTML tags are included: h, p, em, strong, br.
HTML templates will be used in the future instead of generating each of the 
HTML tags individually.


https://reviews.llvm.org/D63180

Files:
  clang-tools-extra/clang-doc/CMakeLists.txt
  clang-tools-extra/clang-doc/Generators.cpp
  clang-tools-extra/clang-doc/Generators.h
  clang-tools-extra/clang-doc/HTMLGenerator.cpp
  clang-tools-extra/clang-doc/MDGenerator.cpp
  clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
  clang-tools-extra/unittests/clang-doc/CMakeLists.txt
  clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp

Index: clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -0,0 +1,306 @@
+//===-- clang-doc/HTMLGeneratorTest.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangDocTest.h"
+#include "Generators.h"
+#include "Representation.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace doc {
+
+std::unique_ptr<Generator> getHTMLGenerator() {
+  auto G = doc::findGeneratorByName("html");
+  if (!G)
+    return nullptr;
+  return std::move(G.get());
+}
+
+TEST(HTMLGeneratorTest, emitNamespaceHTML) {
+  NamespaceInfo I;
+  I.Name = "Namespace";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
+                                 InfoType::IT_namespace);
+  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+  I.ChildFunctions.emplace_back();
+  I.ChildFunctions.back().Name = "OneFunction";
+  I.ChildEnums.emplace_back();
+  I.ChildEnums.back().Name = "OneEnum";
+
+  auto G = getHTMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual);
+  assert(!Err);
+  std::string Expected = R"raw(<h1>namespace Namespace</h1>
+<br>
+<h2>Namespaces</h2>
+<p>ChildNamespace</p>
+<br>
+<h2>Records</h2>
+<p>ChildStruct</p>
+<br>
+<h2>Functions</h2>
+<h3>OneFunction</h3>
+<p><em> OneFunction()</em></p>
+<br>
+<h2>Enums</h2>
+<p>| enum OneEnum |</p>
+<p>--</p>
+<p></p>
+<br>
+)raw";
+
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitRecordHTML) {
+  RecordInfo I;
+  I.Name = "r";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+
+  I.Members.emplace_back("int", "X", AccessSpecifier::AS_private);
+  I.TagType = TagTypeKind::TTK_Class;
+  I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
+  I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
+
+  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+  I.ChildFunctions.emplace_back();
+  I.ChildFunctions.back().Name = "OneFunction";
+  I.ChildEnums.emplace_back();
+  I.ChildEnums.back().Name = "OneEnum";
+
+  auto G = getHTMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual);
+  assert(!Err);
+  std::string Expected = R"raw(<h1>class r</h1>
+<p><em>Defined at line 10 of test.cpp</em></p>
+<p>Inherits from F, G</p>
+<br>
+<h2>Members</h2>
+<p>private int X</p>
+<br>
+<h2>Records</h2>
+<p>ChildStruct</p>
+<br>
+<h2>Functions</h2>
+<h3>OneFunction</h3>
+<p><em> OneFunction()</em></p>
+<br>
+<h2>Enums</h2>
+<p>| enum OneEnum |</p>
+<p>--</p>
+<p></p>
+<br>
+)raw";
+
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitFunctionHTML) {
+  FunctionInfo I;
+  I.Name = "f";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+
+  I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
+  I.Params.emplace_back("int", "P");
+  I.IsMethod = true;
+  I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
+
+  auto G = getHTMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual);
+  assert(!Err);
+  std::string Expected = R"raw(<h3>f</h3>
+<p><em>void f(int P)</em></p>
+<p><em>Defined at line 10 of test.cpp</em></p>
+)raw";
+
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitEnumHTML) {
+  EnumInfo I;
+  I.Name = "e";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+
+  I.Members.emplace_back("X");
+  I.Scoped = true;
+
+  auto G = getHTMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual);
+  assert(!Err);
+  std::string Expected = R"raw(<p>| enum class e |</p>
+<p>--</p>
+<p>| X |
+</p>
+<p><em>Defined at line 10 of test.cpp</em></p>
+)raw";
+
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitCommentHTML) {
+  FunctionInfo I;
+  I.Name = "f";
+  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
+  I.Params.emplace_back("int", "I");
+  I.Params.emplace_back("int", "J");
+
+  CommentInfo Top;
+  Top.Kind = "FullComment";
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *BlankLine = Top.Children.back().get();
+  BlankLine->Kind = "ParagraphComment";
+  BlankLine->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  BlankLine->Children.back()->Kind = "TextComment";
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *Brief = Top.Children.back().get();
+  Brief->Kind = "ParagraphComment";
+  Brief->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  Brief->Children.back()->Kind = "TextComment";
+  Brief->Children.back()->Name = "ParagraphComment";
+  Brief->Children.back()->Text = " Brief description.";
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *Extended = Top.Children.back().get();
+  Extended->Kind = "ParagraphComment";
+  Extended->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  Extended->Children.back()->Kind = "TextComment";
+  Extended->Children.back()->Text = " Extended description that";
+  Extended->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  Extended->Children.back()->Kind = "TextComment";
+  Extended->Children.back()->Text = " continues onto the next line.";
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *HTML = Top.Children.back().get();
+  HTML->Kind = "ParagraphComment";
+  HTML->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  HTML->Children.back()->Kind = "TextComment";
+  HTML->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  HTML->Children.back()->Kind = "HTMLStartTagComment";
+  HTML->Children.back()->Name = "ul";
+  HTML->Children.back()->AttrKeys.emplace_back("class");
+  HTML->Children.back()->AttrValues.emplace_back("test");
+  HTML->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  HTML->Children.back()->Kind = "HTMLStartTagComment";
+  HTML->Children.back()->Name = "li";
+  HTML->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  HTML->Children.back()->Kind = "TextComment";
+  HTML->Children.back()->Text = " Testing.";
+  HTML->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  HTML->Children.back()->Kind = "HTMLEndTagComment";
+  HTML->Children.back()->Name = "ul";
+  HTML->Children.back()->SelfClosing = true;
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *Verbatim = Top.Children.back().get();
+  Verbatim->Kind = "VerbatimBlockComment";
+  Verbatim->Name = "verbatim";
+  Verbatim->CloseName = "endverbatim";
+  Verbatim->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  Verbatim->Children.back()->Kind = "VerbatimBlockLineComment";
+  Verbatim->Children.back()->Text = " The description continues.";
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *ParamOut = Top.Children.back().get();
+  ParamOut->Kind = "ParamCommandComment";
+  ParamOut->Direction = "[out]";
+  ParamOut->ParamName = "I";
+  ParamOut->Explicit = true;
+  ParamOut->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  ParamOut->Children.back()->Kind = "ParagraphComment";
+  ParamOut->Children.back()->Children.emplace_back(
+      llvm::make_unique<CommentInfo>());
+  ParamOut->Children.back()->Children.back()->Kind = "TextComment";
+  ParamOut->Children.back()->Children.emplace_back(
+      llvm::make_unique<CommentInfo>());
+  ParamOut->Children.back()->Children.back()->Kind = "TextComment";
+  ParamOut->Children.back()->Children.back()->Text = " is a parameter.";
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *ParamIn = Top.Children.back().get();
+  ParamIn->Kind = "ParamCommandComment";
+  ParamIn->Direction = "[in]";
+  ParamIn->ParamName = "J";
+  ParamIn->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  ParamIn->Children.back()->Kind = "ParagraphComment";
+  ParamIn->Children.back()->Children.emplace_back(
+      llvm::make_unique<CommentInfo>());
+  ParamIn->Children.back()->Children.back()->Kind = "TextComment";
+  ParamIn->Children.back()->Children.back()->Text = " is a parameter.";
+  ParamIn->Children.back()->Children.emplace_back(
+      llvm::make_unique<CommentInfo>());
+  ParamIn->Children.back()->Children.back()->Kind = "TextComment";
+
+  Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
+  CommentInfo *Return = Top.Children.back().get();
+  Return->Kind = "BlockCommandComment";
+  Return->Name = "return";
+  Return->Explicit = true;
+  Return->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  Return->Children.back()->Kind = "ParagraphComment";
+  Return->Children.back()->Children.emplace_back(
+      llvm::make_unique<CommentInfo>());
+  Return->Children.back()->Children.back()->Kind = "TextComment";
+  Return->Children.back()->Children.back()->Text = "void";
+
+  I.Description.emplace_back(std::move(Top));
+
+  auto G = getHTMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual);
+  assert(!Err);
+  std::string Expected = R"raw(<h3>f</h3>
+<p><em>void f(int I, int J)</em></p>
+<p><em>Defined at line 10 of test.cpp</em></p>
+<br>
+ Brief description.<br>
+ Extended description that continues onto the next line.<br>
+<p><ul "class=test"></p>
+<p><li></p>
+ Testing.<p></ul></p>
+<br>
+ The description continues.<br>
+<strong>I</strong> [out]
+
+<strong>J</strong>
+
+<strong>return</strong>void<br>
+)raw";
+
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+} // namespace doc
+} // namespace clang
Index: clang-tools-extra/unittests/clang-doc/CMakeLists.txt
===================================================================
--- clang-tools-extra/unittests/clang-doc/CMakeLists.txt
+++ clang-tools-extra/unittests/clang-doc/CMakeLists.txt
@@ -13,6 +13,7 @@
 add_extra_unittest(ClangDocTests
   BitcodeTest.cpp
   ClangDocTest.cpp
+  HTMLGeneratorTest.cpp
   MDGeneratorTest.cpp
   MergeTest.cpp
   SerializeTest.cpp
Index: clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
===================================================================
--- clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -65,6 +65,7 @@
 enum OutputFormatTy {
   md,
   yaml,
+  html,
 };
 
 static llvm::cl::opt<OutputFormatTy>
@@ -72,7 +73,9 @@
                llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
                                            "Documentation in YAML format."),
                                 clEnumValN(OutputFormatTy::md, "md",
-                                           "Documentation in MD format.")),
+                                           "Documentation in MD format."),
+                                clEnumValN(OutputFormatTy::html, "html",
+                                           "Documentation in HTML format.")),
                llvm::cl::init(OutputFormatTy::yaml),
                llvm::cl::cat(ClangDocCategory));
 
@@ -82,6 +85,8 @@
     return "yaml";
   case OutputFormatTy::md:
     return "md";
+  case OutputFormatTy::html:
+    return "html";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
Index: clang-tools-extra/clang-doc/MDGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/MDGenerator.cpp
+++ clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -18,37 +18,7 @@
 namespace clang {
 namespace doc {
 
-// Enum conversion
-
-std::string getAccess(AccessSpecifier AS) {
-  switch (AS) {
-  case AccessSpecifier::AS_public:
-    return "public";
-  case AccessSpecifier::AS_protected:
-    return "protected";
-  case AccessSpecifier::AS_private:
-    return "private";
-  case AccessSpecifier::AS_none:
-    return {};
-  }
-  llvm_unreachable("Unknown AccessSpecifier");
-}
-
-std::string getTagType(TagTypeKind AS) {
-  switch (AS) {
-  case TagTypeKind::TTK_Class:
-    return "class";
-  case TagTypeKind::TTK_Union:
-    return "union";
-  case TagTypeKind::TTK_Interface:
-    return "interface";
-  case TagTypeKind::TTK_Struct:
-    return "struct";
-  case TagTypeKind::TTK_Enum:
-    return "enum";
-  }
-  llvm_unreachable("Unknown TagTypeKind");
-}
+namespace {
 
 // Markdown generation
 
@@ -60,19 +30,6 @@
   return "[" + Text.str() + "](" + Link.str() + ")";
 }
 
-std::string genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
-  std::string Buffer;
-  llvm::raw_string_ostream Stream(Buffer);
-  bool First = true;
-  for (const auto &R : Refs) {
-    if (!First)
-      Stream << ", ";
-    Stream << R.Name;
-    First = false;
-  }
-  return Stream.str();
-}
-
 void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n\n"; }
 
 void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
@@ -135,6 +92,8 @@
   }
 }
 
+} // namespace
+
 void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) {
   if (I.Scoped)
     writeLine("| enum class " + I.Name + " |", OS);
Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -0,0 +1,279 @@
+//===-- HTMLGenerator.cpp - HTML Generator ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Generators.h"
+#include "Representation.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include <string>
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+namespace {
+
+// HTML generation
+
+std::string genTag(const Twine &Text, const Twine &Tag) {
+  return "<" + Tag.str() + ">" + Text.str() + "</" + Tag.str() + ">";
+}
+
+std::string genItalic(const Twine &Text) { return genTag(Text, "em"); }
+
+std::string genEmphasis(const Twine &Text) { return genTag(Text, "strong"); }
+
+void writeLine(const Twine &Text, raw_ostream &OS) {
+  OS << genTag(Text, "p") << "\n";
+}
+
+void writeNewLine(raw_ostream &OS) { OS << "<br>\n"; }
+
+void writeHeader(const Twine &Text, const Twine &Num, raw_ostream &OS) {
+  OS << genTag(Text, "h" + Num) << "\n";
+}
+
+void writeFileDefinition(const Location &L, raw_ostream &OS) {
+  writeLine(genItalic("Defined at line " + std::to_string(L.LineNumber) +
+                      " of " + L.Filename),
+            OS);
+}
+
+void writeDescription(const CommentInfo &I, raw_ostream &OS) {
+  if (I.Kind == "FullComment") {
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+  } else if (I.Kind == "ParagraphComment") {
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+    writeNewLine(OS);
+  } else if (I.Kind == "BlockCommandComment") {
+    OS << genEmphasis(I.Name);
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+  } else if (I.Kind == "InlineCommandComment") {
+    OS << genEmphasis(I.Name) << " " << I.Text;
+  } else if (I.Kind == "ParamCommandComment") {
+    std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
+    OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
+  } else if (I.Kind == "TParamCommandComment") {
+    std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
+    OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
+  } else if (I.Kind == "VerbatimBlockComment") {
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+  } else if (I.Kind == "VerbatimBlockLineComment") {
+    OS << I.Text;
+    writeNewLine(OS);
+  } else if (I.Kind == "VerbatimLineComment") {
+    OS << I.Text;
+    writeNewLine(OS);
+  } else if (I.Kind == "HTMLStartTagComment") {
+    if (I.AttrKeys.size() != I.AttrValues.size())
+      return;
+    std::string Buffer;
+    llvm::raw_string_ostream Attrs(Buffer);
+    for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)
+      Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
+
+    std::string CloseTag = I.SelfClosing ? "/>" : ">";
+    writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
+  } else if (I.Kind == "HTMLEndTagComment") {
+    writeLine("</" + I.Name + ">", OS);
+  } else if (I.Kind == "TextComment") {
+    OS << I.Text;
+  } else {
+    OS << "Unknown comment kind: " << I.Kind << ".\n\n";
+  }
+}
+
+} // namespace
+
+void genHTML(const EnumInfo &I, llvm::raw_ostream &OS) {
+  if (I.Scoped)
+    writeLine("| enum class " + I.Name + " |", OS);
+  else
+    writeLine("| enum " + I.Name + " |", OS);
+  writeLine("--", OS);
+
+  std::string Buffer;
+  llvm::raw_string_ostream Members(Buffer);
+  if (!I.Members.empty())
+    for (const auto &N : I.Members)
+      Members << "| " << N << " |\n";
+  writeLine(Members.str(), OS);
+  if (I.DefLoc)
+    writeFileDefinition(I.DefLoc.getValue(), OS);
+
+  for (const auto &C : I.Description)
+    writeDescription(C, OS);
+}
+
+void genHTML(const FunctionInfo &I, llvm::raw_ostream &OS) {
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+  bool First = true;
+  for (const auto &N : I.Params) {
+    if (!First)
+      Stream << ", ";
+    Stream << N.Type.Name + " " + N.Name;
+    First = false;
+  }
+  writeHeader(I.Name, "3", OS);
+  std::string Access = getAccess(I.Access);
+  if (Access != "")
+    writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
+                        "(" + Stream.str() + ")"),
+              OS);
+  else
+    writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
+                        Stream.str() + ")"),
+              OS);
+  if (I.DefLoc)
+    writeFileDefinition(I.DefLoc.getValue(), OS);
+
+  for (const auto &C : I.Description)
+    writeDescription(C, OS);
+}
+
+void genHTML(const NamespaceInfo &I, llvm::raw_ostream &OS) {
+  if (I.Name == "")
+    writeHeader("Global Namespace", "1", OS);
+  else
+    writeHeader("namespace " + I.Name, "1", OS);
+  writeNewLine(OS);
+
+  if (!I.Description.empty()) {
+    for (const auto &C : I.Description)
+      writeDescription(C, OS);
+    writeNewLine(OS);
+  }
+
+  if (!I.ChildNamespaces.empty()) {
+    writeHeader("Namespaces", "2", OS);
+    for (const auto &R : I.ChildNamespaces)
+      writeLine(R.Name, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildRecords.empty()) {
+    writeHeader("Records", "2", OS);
+    for (const auto &R : I.ChildRecords)
+      writeLine(R.Name, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildFunctions.empty()) {
+    writeHeader("Functions", "2", OS);
+    for (const auto &F : I.ChildFunctions)
+      genHTML(F, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildEnums.empty()) {
+    writeHeader("Enums", "2", OS);
+    for (const auto &E : I.ChildEnums)
+      genHTML(E, OS);
+    writeNewLine(OS);
+  }
+}
+
+void genHTML(const RecordInfo &I, llvm::raw_ostream &OS) {
+  writeHeader(getTagType(I.TagType) + " " + I.Name, "1", OS);
+  if (I.DefLoc)
+    writeFileDefinition(I.DefLoc.getValue(), OS);
+
+  if (!I.Description.empty()) {
+    for (const auto &C : I.Description)
+      writeDescription(C, OS);
+    writeNewLine(OS);
+  }
+
+  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);
+    else if (VParents.empty())
+      writeLine("Inherits from " + Parents, OS);
+    else
+      writeLine("Inherits from " + Parents + ", " + VParents, OS);
+    writeNewLine(OS);
+  }
+
+  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);
+    }
+    writeNewLine(OS);
+  }
+
+  if (!I.ChildRecords.empty()) {
+    writeHeader("Records", "2", OS);
+    for (const auto &R : I.ChildRecords)
+      writeLine(R.Name, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildFunctions.empty()) {
+    writeHeader("Functions", "2", OS);
+    for (const auto &F : I.ChildFunctions)
+      genHTML(F, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildEnums.empty()) {
+    writeHeader("Enums", "2", OS);
+    for (const auto &E : I.ChildEnums)
+      genHTML(E, OS);
+    writeNewLine(OS);
+  }
+}
+
+/// Generator for HTML documentation.
+class HTMLGenerator : public Generator {
+public:
+  static const char *Format;
+
+  llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
+};
+
+const char *HTMLGenerator::Format = "html";
+
+llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
+  switch (I->IT) {
+  case InfoType::IT_namespace:
+    genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), OS);
+    break;
+  case InfoType::IT_record:
+    genHTML(*static_cast<clang::doc::RecordInfo *>(I), OS);
+    break;
+  case InfoType::IT_enum:
+    genHTML(*static_cast<clang::doc::EnumInfo *>(I), OS);
+    break;
+  case InfoType::IT_function:
+    genHTML(*static_cast<clang::doc::FunctionInfo *>(I), OS);
+    break;
+  case InfoType::IT_default:
+    return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
+                                               llvm::inconvertibleErrorCode());
+  }
+  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.
+volatile int HTMLGeneratorAnchorSource = 0;
+
+} // namespace doc
+} // namespace clang
Index: clang-tools-extra/clang-doc/Generators.h
===================================================================
--- clang-tools-extra/clang-doc/Generators.h
+++ clang-tools-extra/clang-doc/Generators.h
@@ -34,6 +34,12 @@
 llvm::Expected<std::unique_ptr<Generator>>
 findGeneratorByName(llvm::StringRef Format);
 
+std::string getAccess(AccessSpecifier AS);
+
+std::string getTagType(TagTypeKind AS);
+
+std::string genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs);
+
 } // namespace doc
 } // namespace clang
 
Index: clang-tools-extra/clang-doc/Generators.cpp
===================================================================
--- clang-tools-extra/clang-doc/Generators.cpp
+++ clang-tools-extra/clang-doc/Generators.cpp
@@ -25,14 +25,62 @@
                                              llvm::inconvertibleErrorCode());
 }
 
+// Enum conversion
+
+std::string getAccess(AccessSpecifier AS) {
+  switch (AS) {
+  case AccessSpecifier::AS_public:
+    return "public";
+  case AccessSpecifier::AS_protected:
+    return "protected";
+  case AccessSpecifier::AS_private:
+    return "private";
+  case AccessSpecifier::AS_none:
+    return {};
+  }
+  llvm_unreachable("Unknown AccessSpecifier");
+}
+
+std::string getTagType(TagTypeKind AS) {
+  switch (AS) {
+  case TagTypeKind::TTK_Class:
+    return "class";
+  case TagTypeKind::TTK_Union:
+    return "union";
+  case TagTypeKind::TTK_Interface:
+    return "interface";
+  case TagTypeKind::TTK_Struct:
+    return "struct";
+  case TagTypeKind::TTK_Enum:
+    return "enum";
+  }
+  llvm_unreachable("Unknown TagTypeKind");
+}
+
+std::string genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+  bool First = true;
+  for (const auto &R : Refs) {
+    if (!First)
+      Stream << ", ";
+    Stream << R.Name;
+    First = false;
+  }
+  return Stream.str();
+}
+
 // This anchor is used to force the linker to link in the generated object file
 // and thus register the generators.
 extern volatile int YAMLGeneratorAnchorSource;
 extern volatile int MDGeneratorAnchorSource;
+extern volatile int HTMLGeneratorAnchorSource;
 static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest =
     YAMLGeneratorAnchorSource;
 static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest =
     MDGeneratorAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED HTMLGeneratorAnchorDest =
+    HTMLGeneratorAnchorSource;
 
 } // namespace doc
 } // namespace clang
Index: clang-tools-extra/clang-doc/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-doc/CMakeLists.txt
+++ clang-tools-extra/clang-doc/CMakeLists.txt
@@ -9,6 +9,7 @@
   BitcodeWriter.cpp
   ClangDoc.cpp
   Generators.cpp
+  HTMLGenerator.cpp
   Mapper.cpp
   MDGenerator.cpp
   Representation.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to