DiegoAstiazaran updated this revision to Diff 211866.
DiegoAstiazaran marked 3 inline comments as done.
DiegoAstiazaran added a comment.

Rebase and add comments.


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

https://reviews.llvm.org/D65003

Files:
  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/Representation.cpp
  clang-tools-extra/clang-doc/Representation.h
  clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
  clang-tools-extra/unittests/clang-doc/CMakeLists.txt
  clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
  clang-tools-extra/unittests/clang-doc/ClangDocTest.h
  clang-tools-extra/unittests/clang-doc/GeneratorTest.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
@@ -9,6 +9,7 @@
 #include "ClangDocTest.h"
 #include "Generators.h"
 #include "Representation.h"
+#include "Serialize.h"
 #include "gtest/gtest.h"
 
 namespace clang {
@@ -315,5 +316,79 @@
   EXPECT_EQ(Expected, Actual.str());
 }
 
+TEST(HTMLGeneratorTest, emitIndexHTML) {
+  RecordInfo I;
+  I.Path = "";
+  ClangDocContext CDCtx;
+  std::vector<std::unique_ptr<Info>> Infos;
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoA = Infos.back().get();
+  InfoA->Name = "A";
+  InfoA->USR = serialize::hashUSR("1");
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoC = Infos.back().get();
+  InfoC->Name = "C";
+  InfoC->USR = serialize::hashUSR("3");
+  Reference RefB = Reference("B");
+  RefB.USR = serialize::hashUSR("2");
+  InfoC->Namespace = {std::move(RefB)};
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoD = Infos.back().get();
+  InfoD->Name = "D";
+  InfoD->USR = serialize::hashUSR("4");
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoF = Infos.back().get();
+  InfoF->Name = "F";
+  InfoF->USR = serialize::hashUSR("6");
+  Reference RefD = Reference("D");
+  RefD.USR = serialize::hashUSR("4");
+  Reference RefE = Reference("E");
+  RefE.USR = serialize::hashUSR("5");
+  InfoF->Namespace = {std::move(RefE), std::move(RefD)};
+  CDCtx.Idx = Generator::genIndex(Infos);
+
+  auto G = getHTMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
+  assert(!Err);
+  std::string Expected = R"raw(<!DOCTYPE html>
+<meta charset="utf-8"/>
+<title>struct </title>
+<ul>
+  <li>
+    <span>A</span>
+  </li>
+  <li>
+    <span>B</span>
+    <ul>
+      <li>
+        <span>C</span>
+      </li>
+    </ul>
+  </li>
+  <li>
+    <span>D</span>
+    <ul>
+      <li>
+        <span>E</span>
+        <ul>
+          <li>
+            <span>F</span>
+          </li>
+        </ul>
+      </li>
+    </ul>
+  </li>
+</ul>
+<div>
+  <h1>struct </h1>
+</div>
+)raw";
+
+  EXPECT_EQ(Expected, Actual.str());
+}
+
 } // namespace doc
 } // namespace clang
Index: clang-tools-extra/unittests/clang-doc/GeneratorTest.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/unittests/clang-doc/GeneratorTest.cpp
@@ -0,0 +1,70 @@
+//===-- clang-doc/GeneratorTest.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 "Serialize.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace doc {
+
+TEST(GeneratorTest, emitIndex) {
+  std::vector<std::unique_ptr<Info>> Infos;
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoA = Infos.back().get();
+  InfoA->Name = "A";
+  InfoA->USR = serialize::hashUSR("1");
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoC = Infos.back().get();
+  InfoC->Name = "C";
+  InfoC->USR = serialize::hashUSR("3");
+  Reference RefB = Reference("B");
+  RefB.USR = serialize::hashUSR("2");
+  InfoC->Namespace = {std::move(RefB)};
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoD = Infos.back().get();
+  InfoD->Name = "D";
+  InfoD->USR = serialize::hashUSR("4");
+  Infos.emplace_back(llvm::make_unique<Info>());
+  Info *InfoF = Infos.back().get();
+  InfoF->Name = "F";
+  InfoF->USR = serialize::hashUSR("6");
+  Reference RefD = Reference("D");
+  RefD.USR = serialize::hashUSR("4");
+  Reference RefE = Reference("E");
+  RefE.USR = serialize::hashUSR("5");
+  InfoF->Namespace = {std::move(RefE), std::move(RefD)};
+  Index Idx = Generator::genIndex(Infos);
+
+  Index ExpectedIdx;
+  Index IndexA;
+  IndexA.Name = "A";
+  ExpectedIdx.Children.emplace_back(std::move(IndexA));
+  Index IndexB;
+  IndexB.Name = "B";
+  Index IndexC;
+  IndexC.Name = "C";
+  IndexB.Children.emplace_back(std::move(IndexC));
+  ExpectedIdx.Children.emplace_back(std::move(IndexB));
+  Index IndexD;
+  IndexD.Name = "D";
+  Index IndexE;
+  IndexE.Name = "E";
+  Index IndexF;
+  IndexF.Name = "F";
+  IndexE.Children.emplace_back(std::move(IndexF));
+  IndexD.Children.emplace_back(std::move(IndexE));
+  ExpectedIdx.Children.emplace_back(std::move(IndexD));
+
+  CheckIndex(ExpectedIdx, Idx);
+}
+
+} // namespace doc
+} // namespace clang
Index: clang-tools-extra/unittests/clang-doc/ClangDocTest.h
===================================================================
--- clang-tools-extra/unittests/clang-doc/ClangDocTest.h
+++ clang-tools-extra/unittests/clang-doc/ClangDocTest.h
@@ -44,6 +44,8 @@
 void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual);
 void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual);
 
+void CheckIndex(Index &Expected, Index &Actual);
+
 } // namespace doc
 } // namespace clang
 
Index: clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
+++ clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
@@ -63,6 +63,7 @@
 void CheckReference(Reference &Expected, Reference &Actual) {
   EXPECT_EQ(Expected.Name, Actual.Name);
   EXPECT_EQ(Expected.RefType, Actual.RefType);
+  EXPECT_EQ(Expected.Path, Actual.Path);
 }
 
 void CheckTypeInfo(TypeInfo *Expected, TypeInfo *Actual) {
@@ -180,5 +181,12 @@
     CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]);
 }
 
+void CheckIndex(Index &Expected, Index &Actual) {
+  CheckReference(Expected, Actual);
+  ASSERT_EQ(Expected.Children.size(), Actual.Children.size());
+  for (size_t Idx = 0; Idx < Actual.Children.size(); ++Idx)
+    CheckIndex(Expected.Children[Idx], Actual.Children[Idx]);
+}
+
 } // 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
@@ -12,6 +12,7 @@
 add_extra_unittest(ClangDocTests
   BitcodeTest.cpp
   ClangDocTest.cpp
+  GeneratorTest.cpp
   HTMLGeneratorTest.cpp
   MDGeneratorTest.cpp
   MergeTest.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
@@ -243,16 +243,23 @@
 
   // First reducing phase (reduce all decls into one info per decl).
   llvm::outs() << "Reducing " << USRToInfos.size() << " infos...\n";
+  std::vector<std::unique_ptr<clang::doc::Info>> ReducedInfos;
   for (auto &Group : USRToInfos) {
     auto Reduced = doc::mergeInfos(Group.getValue());
     if (!Reduced) {
       llvm::errs() << llvm::toString(Reduced.takeError());
       continue;
     }
+    ReducedInfos.emplace_back(std::move(Reduced.get()));
+  }
+
+  llvm::outs() << "Constructing index...\n";
+  CDCtx.Idx = clang::doc::Generator::genIndex(ReducedInfos);
 
-    doc::Info *I = Reduced.get().get();
-    auto InfoPath = getInfoOutputFile(OutDirectory, I->Path, I->extractName(),
-                                      "." + Format);
+  llvm::outs() << "Generating docs...\n";
+  for (auto &Info : ReducedInfos) {
+    auto InfoPath = getInfoOutputFile(OutDirectory, Info->Path,
+                                      Info->extractName(), "." + Format);
     if (!InfoPath) {
       llvm::errs() << toString(InfoPath.takeError()) << "\n";
       return 1;
@@ -264,7 +271,7 @@
       continue;
     }
 
-    if (auto Err = G->get()->generateDocForInfo(I, InfoOS, CDCtx))
+    if (auto Err = G->get()->generateDocForInfo(Info.get(), InfoOS, CDCtx))
       llvm::errs() << toString(std::move(Err)) << "\n";
   }
 
Index: clang-tools-extra/clang-doc/Representation.h
===================================================================
--- clang-tools-extra/clang-doc/Representation.h
+++ clang-tools-extra/clang-doc/Representation.h
@@ -338,6 +338,18 @@
   llvm::SmallVector<SmallString<16>, 4> Members; // List of enum members.
 };
 
+struct Index : public Reference {
+  Index() = default;
+  Index(SymbolID USR, StringRef Name, InfoType IT, StringRef Path)
+      : Reference(USR, Name, IT, Path) {}
+  bool operator==(const SymbolID &Other) const { return USR == Other; }
+  bool operator<(const Index &Other) const { return Name < Other.Name; }
+
+  std::vector<Index> Children;
+
+  void sort();
+};
+
 // TODO: Add functionality to include separate markdown pages.
 
 // A standalone function to call to merge a vector of infos into one.
@@ -347,10 +359,17 @@
 mergeInfos(std::vector<std::unique_ptr<Info>> &Values);
 
 struct ClangDocContext {
+  ClangDocContext() = default;
+  ClangDocContext(tooling::ExecutionContext *ECtx, bool PublicOnly,
+                  StringRef OutDirectory,
+                  std::vector<std::string> UserStylesheets)
+      : ECtx(ECtx), PublicOnly(PublicOnly), OutDirectory(OutDirectory),
+        UserStylesheets(UserStylesheets) {}
   tooling::ExecutionContext *ECtx;
   bool PublicOnly;
   std::string OutDirectory;
   std::vector<std::string> UserStylesheets;
+  Index Idx;
 };
 
 } // namespace doc
Index: clang-tools-extra/clang-doc/Representation.cpp
===================================================================
--- clang-tools-extra/clang-doc/Representation.cpp
+++ clang-tools-extra/clang-doc/Representation.cpp
@@ -229,5 +229,11 @@
   return llvm::SmallString<16>("");
 }
 
+void Index::sort() {
+  std::sort(Children.begin(), Children.end());
+  for (auto &C : Children)
+    C.sort();
+}
+
 } // namespace doc
 } // namespace clang
Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -25,17 +25,18 @@
 public:
   // Any other tag can be added if required
   enum TagType {
-    TAG_META,
-    TAG_TITLE,
+    TAG_A,
     TAG_DIV,
     TAG_H1,
     TAG_H2,
     TAG_H3,
-    TAG_P,
-    TAG_UL,
     TAG_LI,
-    TAG_A,
     TAG_LINK,
+    TAG_META,
+    TAG_P,
+    TAG_SPAN,
+    TAG_TITLE,
+    TAG_UL,
   };
 
   HTMLTag() = default;
@@ -106,15 +107,16 @@
   case HTMLTag::TAG_META:
   case HTMLTag::TAG_LINK:
     return true;
-  case HTMLTag::TAG_TITLE:
+  case HTMLTag::TAG_A:
   case HTMLTag::TAG_DIV:
   case HTMLTag::TAG_H1:
   case HTMLTag::TAG_H2:
   case HTMLTag::TAG_H3:
+  case HTMLTag::TAG_LI:
   case HTMLTag::TAG_P:
+  case HTMLTag::TAG_SPAN:
+  case HTMLTag::TAG_TITLE:
   case HTMLTag::TAG_UL:
-  case HTMLTag::TAG_LI:
-  case HTMLTag::TAG_A:
     return false;
   }
   llvm_unreachable("Unhandled HTMLTag::TagType");
@@ -122,10 +124,8 @@
 
 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_A:
+    return llvm::SmallString<16>("a");
   case HTMLTag::TAG_DIV:
     return llvm::SmallString<16>("div");
   case HTMLTag::TAG_H1:
@@ -134,16 +134,20 @@
     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");
-  case HTMLTag::TAG_A:
-    return llvm::SmallString<16>("a");
   case HTMLTag::TAG_LINK:
     return llvm::SmallString<16>("link");
+  case HTMLTag::TAG_META:
+    return llvm::SmallString<16>("meta");
+  case HTMLTag::TAG_P:
+    return llvm::SmallString<16>("p");
+  case HTMLTag::TAG_SPAN:
+    return llvm::SmallString<16>("span");
+  case HTMLTag::TAG_TITLE:
+    return llvm::SmallString<16>("title");
+  case HTMLTag::TAG_UL:
+    return llvm::SmallString<16>("ul");
   }
   llvm_unreachable("Unhandled HTMLTag::TagType");
 }
@@ -358,6 +362,40 @@
       "Defined at line " + std::to_string(L.LineNumber) + " of " + L.Filename);
 }
 
+static std::vector<std::unique_ptr<TagNode>>
+genCommonFileNodes(StringRef Title, StringRef InfoPath, const ClangDocContext &CDCtx) {
+  std::vector<std::unique_ptr<TagNode>> Out;
+  auto MetaNode = llvm::make_unique<TagNode>(HTMLTag::TAG_META);
+  MetaNode->Attributes.try_emplace("charset", "utf-8");
+  Out.emplace_back(std::move(MetaNode));
+  Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_TITLE, Title));
+  std::vector<std::unique_ptr<TagNode>> StylesheetsNodes =
+    genStylesheetsHTML(InfoPath, CDCtx);
+  AppendVector(std::move(StylesheetsNodes), Out);
+  return Out;
+}
+
+static std::vector<std::unique_ptr<TagNode>> genHTML(const Index &Index,
+                                                     StringRef InfoPath) {
+  std::vector<std::unique_ptr<TagNode>> Out;
+  if (!Index.Name.empty()) {
+    Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_SPAN));
+    auto &SpanBody = Out.back();
+    SpanBody->Children.emplace_back(genTypeReference(Index, InfoPath));
+  }
+  if (Index.Children.empty())
+    return Out;
+  Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL));
+  const auto &UlBody = Out.back();
+  for (const auto &C : Index.Children) {
+    auto LiBody = llvm::make_unique<TagNode>(HTMLTag::TAG_LI);
+    std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(C, InfoPath);
+    AppendVector(std::move(Nodes), LiBody->Children);
+    UlBody->Children.emplace_back(std::move(LiBody));
+  }
+  return Out;
+}
+
 static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
   if (I.Kind == "FullComment") {
     auto FullComment = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV);
@@ -554,13 +592,7 @@
 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
                                               const ClangDocContext &CDCtx) {
   HTMLFile F;
-
-  auto MetaNode = llvm::make_unique<TagNode>(HTMLTag::TAG_META);
-  MetaNode->Attributes.try_emplace("charset", "utf-8");
-  F.Children.emplace_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: {
@@ -592,11 +624,11 @@
                                                llvm::inconvertibleErrorCode());
   }
 
-  F.Children.emplace_back(
-      llvm::make_unique<TagNode>(HTMLTag::TAG_TITLE, InfoTitle));
-  std::vector<std::unique_ptr<TagNode>> StylesheetsNodes =
-      genStylesheetsHTML(I->Path, CDCtx);
-  AppendVector(std::move(StylesheetsNodes), F.Children);
+  std::vector<std::unique_ptr<TagNode>> BasicNodes =
+      genCommonFileNodes(InfoTitle, I->Path, CDCtx);
+  AppendVector(std::move(BasicNodes), F.Children);
+  std::vector<std::unique_ptr<TagNode>> Index = genHTML(CDCtx.Idx, I->Path);
+  AppendVector(std::move(Index), F.Children);
   F.Children.emplace_back(std::move(MainContentNode));
   F.Render(OS);
 
Index: clang-tools-extra/clang-doc/Generators.h
===================================================================
--- clang-tools-extra/clang-doc/Generators.h
+++ clang-tools-extra/clang-doc/Generators.h
@@ -25,6 +25,14 @@
 public:
   virtual ~Generator() = default;
 
+  // This should be called before calling any generateDocForInfo.
+  // FIXME: This currently needs to be run before generating any individual
+  // documentation pages, since the content it generates is directly included in
+  // every page. A better design would be to lazily include it in the individual
+  // documentation pages, in which case this could be run in parallel with calls
+  // to generateDocForInfo().
+  static Index genIndex(const std::vector<std::unique_ptr<Info>> &Infos);
+
   // Write out the decl info in the specified format.
   virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
                                          const ClangDocContext &CDCtx) = 0;
Index: clang-tools-extra/clang-doc/Generators.cpp
===================================================================
--- clang-tools-extra/clang-doc/Generators.cpp
+++ clang-tools-extra/clang-doc/Generators.cpp
@@ -13,6 +13,30 @@
 namespace clang {
 namespace doc {
 
+Index Generator::genIndex(const std::vector<std::unique_ptr<Info>> &Infos) {
+  Index Idx;
+  for (const auto &Info : Infos) {
+    Index *I = &Idx;
+    for (auto R = Info->Namespace.rbegin(), E = Info->Namespace.rend(); R != E;
+         ++R) {
+      auto It = std::find(I->Children.begin(), I->Children.end(), R->USR);
+      if (It != I->Children.end())
+        I = &*It;
+      else {
+        I->Children.emplace_back(R->USR, R->Name, R->RefType, R->Path);
+        I = &I->Children.back();
+      }
+    }
+    auto It = std::find(I->Children.begin(), I->Children.end(), Info->USR);
+    if (It == I->Children.end())
+      I->Children.emplace_back(Info->USR, Info->Name, Info->IT, Info->Path);
+    else if (It->Path.empty())
+      It->Path = I->Path;
+  }
+  Idx.sort();
+  return Idx;
+}
+
 llvm::Expected<std::unique_ptr<Generator>>
 findGeneratorByName(llvm::StringRef Format) {
   for (auto I = GeneratorRegistry::begin(), E = GeneratorRegistry::end();
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to