DiegoAstiazaran updated this revision to Diff 207174.
DiegoAstiazaran marked 2 inline comments as done.
DiegoAstiazaran added a comment.

Add a variable to use as a reference to the last item in a vector instead of 
doing vector.back().


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D63857/new/

https://reviews.llvm.org/D63857

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,276 @@
+//===-- 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(<!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());
+}
+
+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(<!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());
+}
+
+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(<!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());
+}
+
+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(<!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());
+}
+
+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.";
+
+  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(<!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());
+}
+
+} // 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,76 +18,33 @@
 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");
-}
-
 // 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() + ")";
 }
 
-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();
+static void writeLine(const Twine &Text, raw_ostream &OS) {
+  OS << Text << "\n\n";
 }
 
-void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n\n"; }
-
-void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
+static void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
 
-void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
+static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
   OS << std::string(Num, '#') + " " + Text << "\n\n";
 }
 
-void writeFileDefinition(const Location &L, raw_ostream &OS) {
+static void writeFileDefinition(const Location &L, raw_ostream &OS) {
   OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " +
                   L.Filename)
      << "\n\n";
 }
 
-void writeDescription(const CommentInfo &I, raw_ostream &OS) {
+static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
   if (I.Kind == "FullComment") {
     for (const auto &Child : I.Children)
       writeDescription(*Child, OS);
@@ -135,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
@@ -155,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;
@@ -182,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
@@ -221,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
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -0,0 +1,522 @@
+//===-- 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 {
+
+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
+
+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));
+  auto &DivBody = Out.back();
+  for (const auto &E : Enums) {
+    std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E);
+    std::move(Nodes.begin(), Nodes.end(),
+              std::back_inserter(DivBody->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));
+  auto &DivBody = Out.back();
+  for (const auto &F : Functions) {
+    std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(F);
+    std::move(Nodes.begin(), Nodes.end(),
+              std::back_inserter(DivBody->Children));
+  }
+  return Out;
+}
+
+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));
+  auto &ULBody = Out.back();
+  for (const auto &M : Members) {
+    std::string Access = getAccess(M.Access);
+    if (Access != "")
+      Access = Access + " ";
+    ULBody->Children.emplace_back(llvm::make_unique<TagNode>(
+        HTMLTag::TAG_LI, Access + M.Type.Name + " " + M.Name));
+  }
+  return Out;
+}
+
+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));
+  auto &ULBody = Out.back();
+  for (const auto &R : References)
+    ULBody->Children.emplace_back(
+        llvm::make_unique<TagNode>(HTMLTag::TAG_LI, R.Name));
+  return Out;
+}
+
+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 std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
+  if (I.Kind == "FullComment") {
+    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") {
+    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 == "")
+      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;
+}
+
+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 ";
+
+  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)
+    Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
+
+  std::string Description;
+  if (!I.Description.empty())
+    Out.emplace_back(genHTML(I.Description));
+
+  return Out;
+}
+
+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);
+  for (const auto &P : I.Params) {
+    if (&P != I.Params.begin())
+      Stream << ", ";
+    Stream << P.Type.Name + " " + P.Name;
+  }
+
+  std::string Access = getAccess(I.Access);
+  if (Access != "")
+    Access = Access + " ";
+
+  Out.emplace_back(llvm::make_unique<TagNode>(
+      HTMLTag::TAG_P, Access + I.ReturnType.Type.Name + " " + I.Name + "(" +
+                          Stream.str() + ")"));
+
+  if (I.DefLoc)
+    Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
+
+  std::string Description;
+  if (!I.Description.empty())
+    Out.emplace_back(genHTML(I.Description));
+
+  return Out;
+}
+
+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
+    InfoTitle = ("namespace " + I.Name).str();
+
+  Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
+
+  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;
+}
+
+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)
+    Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
+
+  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())
+      Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P,
+                                                  "Inherits from " + VParents));
+    else if (VParents.empty())
+      Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P,
+                                                  "Inherits from " + Parents));
+    else
+      Out.emplace_back(llvm::make_unique<TagNode>(
+          HTMLTag::TAG_P, "Inherits from " + Parents + ", " + VParents));
+  }
+
+  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));
+
+  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.
+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) {
+  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: {
+    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: {
+    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: {
+    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: {
+    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.
+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");
+}
+
+// Generates a comma-separated list of Refs
+// Used to display the parents of a record
+std::string genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+  for (const auto &R : Refs) {
+    if (&R != Refs.begin())
+      Stream << ", ";
+    Stream << R.Name;
+  }
+  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