Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -268,15 +268,22 @@ return LinkNode; } -static std::unique_ptr genTypeReference(const Reference &Type, - StringRef CurrentDirectory) { - if (Type.Path.empty() && !Type.IsInGlobalNamespace) - return llvm::make_unique(Type.Name); +static std::unique_ptr +genTypeReference(const Reference &Type, StringRef CurrentDirectory, + llvm::Optional JumpToSection = None) { + if (Type.Path.empty() && !Type.IsInGlobalNamespace) { + if (!JumpToSection) + return llvm::make_unique(Type.Name); + else + return genLink(Type.Name, "#" + JumpToSection.getValue()); + } llvm::SmallString<128> Path = computeRelativePath(Type.Path, CurrentDirectory); llvm::sys::path::append(Path, Type.Name + ".html"); // Paths in HTML must be in posix-style llvm::sys::path::native(Path, llvm::sys::path::Style::posix); + if (JumpToSection) + Path += ("#" + JumpToSection.getValue()).str(); return genLink(Type.Name, Path); } @@ -303,6 +310,7 @@ std::vector> Out; Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, "Enums")); + Out.back()->Attributes.try_emplace("id", "Enums"); Out.emplace_back(llvm::make_unique(HTMLTag::TAG_DIV)); auto &DivBody = Out.back(); for (const auto &E : Enums) { @@ -331,6 +339,7 @@ std::vector> Out; Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, "Functions")); + Out.back()->Attributes.try_emplace("id", "Functions"); Out.emplace_back(llvm::make_unique(HTMLTag::TAG_DIV)); auto &DivBody = Out.back(); for (const auto &F : Functions) { @@ -348,6 +357,7 @@ std::vector> Out; Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, "Members")); + Out.back()->Attributes.try_emplace("id", "Members"); Out.emplace_back(llvm::make_unique(HTMLTag::TAG_UL)); auto &ULBody = Out.back(); for (const auto &M : Members) { @@ -371,6 +381,7 @@ std::vector> Out; Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H2, Title)); + Out.back()->Attributes.try_emplace("id", Title); Out.emplace_back(llvm::make_unique(HTMLTag::TAG_UL)); auto &ULBody = Out.back(); for (const auto &R : References) @@ -407,6 +418,41 @@ return Out; } +template ::value>> +static Index genInfoIndexItem(const std::vector &Infos, StringRef Title) { + Index Idx(Title, Title); + for (const auto &C : Infos) + Idx.Children.emplace_back(C.extractName(), + llvm::toHex(llvm::toStringRef(C.USR))); + return Idx; +} + +static std::vector> genHTML(const Index &Index, + StringRef InfoPath) { + std::vector> Out; + if (!Index.Name.empty()) { + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_SPAN)); + auto &SpanBody = Out.back(); + if (!Index.JumpToSection) + SpanBody->Children.emplace_back(genTypeReference(Index, InfoPath)); + else + SpanBody->Children.emplace_back(genTypeReference( + Index, InfoPath, StringRef{Index.JumpToSection.getValue()})); + } + if (Index.Children.empty()) + return Out; + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_UL)); + const auto &UlBody = Out.back(); + for (const auto &C : Index.Children) { + auto LiBody = llvm::make_unique(HTMLTag::TAG_LI); + std::vector> Nodes = genHTML(C, InfoPath); + AppendVector(std::move(Nodes), LiBody->Children); + UlBody->Children.emplace_back(std::move(LiBody)); + } + return Out; +} + static std::unique_ptr genHTML(const CommentInfo &I) { if (I.Kind == "FullComment") { auto FullComment = llvm::make_unique(HTMLTag::TAG_DIV); @@ -453,6 +499,8 @@ Out.emplace_back( llvm::make_unique(HTMLTag::TAG_H3, EnumType + I.Name)); + Out.back()->Attributes.try_emplace("id", + llvm::toHex(llvm::toStringRef(I.USR))); std::unique_ptr Node = genEnumMembersBlock(I.Members); if (Node) @@ -472,6 +520,10 @@ StringRef ParentInfoDir) { std::vector> Out; Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H3, I.Name)); + // USR is used as id for functions instead of name to disambiguate function + // overloads. + Out.back()->Attributes.try_emplace("id", + llvm::toHex(llvm::toStringRef(I.USR))); Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P)); auto &FunctionHeader = Out.back(); @@ -508,8 +560,8 @@ return Out; } -static std::vector> genHTML(const NamespaceInfo &I, - std::string &InfoTitle) { +static std::vector> +genHTML(const NamespaceInfo &I, Index &InfoIndex, std::string &InfoTitle) { std::vector> Out; if (I.Name.str() == "") InfoTitle = "Global Namespace"; @@ -536,11 +588,21 @@ genEnumsBlock(I.ChildEnums); AppendVector(std::move(ChildEnums), Out); + if (!I.ChildNamespaces.empty()) + InfoIndex.Children.emplace_back("Namespaces", "Namespaces"); + if (!I.ChildRecords.empty()) + InfoIndex.Children.emplace_back("Records", "Records"); + if (!I.ChildFunctions.empty()) + InfoIndex.Children.emplace_back( + genInfoIndexItem(I.ChildFunctions, "Functions")); + if (!I.ChildEnums.empty()) + InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums")); + return Out; } -static std::vector> genHTML(const RecordInfo &I, - std::string &InfoTitle) { +static std::vector> +genHTML(const RecordInfo &I, Index &InfoIndex, std::string &InfoTitle) { std::vector> Out; InfoTitle = (getTagType(I.TagType) + " " + I.Name).str(); Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H1, InfoTitle)); @@ -585,6 +647,16 @@ genEnumsBlock(I.ChildEnums); AppendVector(std::move(ChildEnums), Out); + if (!I.Members.empty()) + InfoIndex.Children.emplace_back("Members", "Members"); + if (!I.ChildRecords.empty()) + InfoIndex.Children.emplace_back("Records", "Records"); + if (!I.ChildFunctions.empty()) + InfoIndex.Children.emplace_back( + genInfoIndexItem(I.ChildFunctions, "Functions")); + if (!I.ChildEnums.empty()) + InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums")); + return Out; } @@ -605,16 +677,17 @@ HTMLFile F; std::string InfoTitle; auto MainContentNode = llvm::make_unique(HTMLTag::TAG_DIV); + Index InfoIndex; switch (I->IT) { case InfoType::IT_namespace: { - std::vector> Nodes = - genHTML(*static_cast(I), InfoTitle); + std::vector> Nodes = genHTML( + *static_cast(I), InfoIndex, InfoTitle); AppendVector(std::move(Nodes), MainContentNode->Children); break; } case InfoType::IT_record: { - std::vector> Nodes = - genHTML(*static_cast(I), InfoTitle); + std::vector> Nodes = genHTML( + *static_cast(I), InfoIndex, InfoTitle); AppendVector(std::move(Nodes), MainContentNode->Children); break; } @@ -638,6 +711,9 @@ std::vector> BasicNodes = genCommonFileNodes(InfoTitle, I->Path, CDCtx); AppendVector(std::move(BasicNodes), F.Children); + std::vector> InfoIndexHTML = + genHTML(InfoIndex, I->Path); + AppendVector(std::move(InfoIndexHTML), F.Children); F.Children.emplace_back(std::move(MainContentNode)); F.Render(OS); Index: clang-tools-extra/clang-doc/Representation.h =================================================================== --- clang-tools-extra/clang-doc/Representation.h +++ clang-tools-extra/clang-doc/Representation.h @@ -350,12 +350,15 @@ struct Index : public Reference { Index() = default; + Index(StringRef Name, StringRef JumpToSection) + : Reference(Name), JumpToSection(JumpToSection) {} Index(SymbolID USR, StringRef Name, InfoType IT, StringRef Path) : Reference(USR, Name, IT, Path) {} // This is used to look for a USR in a vector of Indexes using std::find bool operator==(const SymbolID &Other) const { return USR == Other; } bool operator<(const Index &Other) const { return Name < Other.Name; } + llvm::Optional> JumpToSection; std::vector Children; void sort(); 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 { @@ -59,24 +60,60 @@
+

namespace Namespace

-

Namespaces

+

Namespaces

  • ChildNamespace
-

Records

+

Records

  • ChildStruct
-

Functions

+

Functions

-

OneFunction

+

OneFunction

OneFunction()

-

Enums

+

Enums

-

enum OneEnum

+

enum OneEnum

)raw"; @@ -119,6 +156,42 @@
+

class r

Defined at line 10 of test.cpp

@@ -127,7 +200,7 @@ F , G

-

Members

+

Members

  • private @@ -135,18 +208,18 @@ X
-

Records

+

Records

  • ChildStruct
-

Functions

+

Functions

-

OneFunction

+

OneFunction

OneFunction()

-

Enums

+

Enums

-

enum OneEnum

+

enum OneEnum

)raw"; @@ -155,26 +228,39 @@ } TEST(HTMLGeneratorTest, emitFunctionHTML) { + llvm::errs() << "1\n"; FunctionInfo I; + llvm::errs() << "2\n"; I.Name = "f"; + llvm::errs() << "3\n"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); + llvm::errs() << "4\n"; I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); + llvm::errs() << "5\n"; I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); - + llvm::errs() << "6\n"; SmallString<16> PathTo; + llvm::errs() << "7\n"; llvm::sys::path::native("path/to", PathTo); + llvm::errs() << "8\n"; I.ReturnType = TypeInfo(EmptySID, "float", InfoType::IT_default, PathTo); + llvm::errs() << "9\n"; I.Params.emplace_back("int", PathTo, "P"); + llvm::errs() << "1\n"; I.IsMethod = true; + llvm::errs() << "2\n"; I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record); + llvm::errs() << "3\n"; auto G = getHTMLGenerator(); assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); ClangDocContext CDCtx = getClangDocContext(); + llvm::errs() << "4\n"; auto Err = G->generateDocForInfo(&I, Actual, CDCtx); + llvm::errs() << "5\n"; assert(!Err); std::string Expected = R"raw( @@ -183,7 +269,7 @@
-

f

+

f

float f( @@ -193,8 +279,10 @@

Defined at line 10 of test.cpp

)raw"; + llvm::errs() << "6\n"; EXPECT_EQ(Expected, Actual.str()); + llvm::errs() << "7\n"; } TEST(HTMLGeneratorTest, emitEnumHTML) { @@ -222,7 +310,7 @@
-

enum class e

+

enum class e

  • X
@@ -293,7 +381,7 @@
-

f

+

f

void f(int I, int J)

Defined at line 10 of test.cpp