> JsNodes =
genJsScriptsHTML(InfoPath, CDCtx);
AppendVector(std::move(JsNodes), Out);
// An empty is generated but the index will be then rendered here
auto IndexNode = llvm::make_unique(HTMLTag::TAG_DIV);
IndexNode->Attributes.try_emplace("id", "index");
IndexNode->Attributes.try_emplace("path", InfoPath);
Out.emplace_back(std::move(IndexNode));
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);
for (const auto &Child : I.Children) {
std::unique_ptr 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(HTMLTag::TAG_P);
for (const auto &Child : I.Children) {
std::unique_ptr 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(I.Text);
}
return nullptr;
}
static std::unique_ptr genHTML(const std::vector &C) {
auto CommentBlock = llvm::make_unique(HTMLTag::TAG_DIV);
for (const auto &Child : C) {
if (std::unique_ptr Node = genHTML(Child))
CommentBlock->Children.emplace_back(std::move(Node));
}
return CommentBlock;
}
static std::vector>
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
std::vector> Out;
std::string EnumType;
if (I.Scoped)
EnumType = "enum class ";
else
EnumType = "enum ";
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)
Out.emplace_back(std::move(Node));
if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
else
Out.emplace_back(writeFileDefinition(
I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
}
std::string Description;
if (!I.Description.empty())
Out.emplace_back(genHTML(I.Description));
return Out;
}
static std::vector>
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
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();
std::string Access = getAccess(I.Access);
if (Access != "")
FunctionHeader->Children.emplace_back(
llvm::make_unique(Access + " "));
if (I.ReturnType.Type.Name != "") {
FunctionHeader->Children.emplace_back(
genTypeReference(I.ReturnType.Type, ParentInfoDir));
FunctionHeader->Children.emplace_back(llvm::make_unique(" "));
}
FunctionHeader->Children.emplace_back(
llvm::make_unique(I.Name + "("));
for (const auto &P : I.Params) {
if (&P != I.Params.begin())
FunctionHeader->Children.emplace_back(llvm::make_unique(", "));
FunctionHeader->Children.emplace_back(
genTypeReference(P.Type, ParentInfoDir));
FunctionHeader->Children.emplace_back(
llvm::make_unique(" " + P.Name));
}
FunctionHeader->Children.emplace_back(llvm::make_unique(")"));
if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
else
Out.emplace_back(writeFileDefinition(
I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
}
std::string Description;
if (!I.Description.empty())
Out.emplace_back(genHTML(I.Description));
return Out;
}
static std::vector>
genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
std::string &InfoTitle) {
std::vector> Out;
if (I.Name.str() == "")
InfoTitle = "Global Namespace";
else
InfoTitle = ("namespace " + I.Name).str();
Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H1, InfoTitle));
std::string Description;
if (!I.Description.empty())
Out.emplace_back(genHTML(I.Description));
std::vector> ChildNamespaces =
genReferencesBlock(I.ChildNamespaces, "Namespaces");
AppendVector(std::move(ChildNamespaces), Out);
std::vector> ChildRecords =
genReferencesBlock(I.ChildRecords, "Records");
AppendVector(std::move(ChildRecords), Out);
std::vector> ChildFunctions =
genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path);
AppendVector(std::move(ChildFunctions), Out);
std::vector> ChildEnums =
genEnumsBlock(I.ChildEnums, CDCtx);
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, Index &InfoIndex, const ClangDocContext &CDCtx,
std::string &InfoTitle) {
std::vector> Out;
InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H1, InfoTitle));
if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
else
Out.emplace_back(writeFileDefinition(
I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
}
std::string Description;
if (!I.Description.empty())
Out.emplace_back(genHTML(I.Description));
std::vector> Parents =
genReferenceList(I.Parents, I.Path);
std::vector> VParents =
genReferenceList(I.VirtualParents, I.Path);
if (!Parents.empty() || !VParents.empty()) {
Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P));
auto &PBody = Out.back();
PBody->Children.emplace_back(llvm::make_unique("Inherits from "));
if (Parents.empty())
AppendVector(std::move(VParents), PBody->Children);
else if (VParents.empty())
AppendVector(std::move(Parents), PBody->Children);
else {
AppendVector(std::move(Parents), PBody->Children);
PBody->Children.emplace_back(llvm::make_unique(", "));
AppendVector(std::move(VParents), PBody->Children);
}
}
std::vector> Members =
genRecordMembersBlock(I.Members, I.Path);
AppendVector(std::move(Members), Out);
std::vector> ChildRecords =
genReferencesBlock(I.ChildRecords, "Records");
AppendVector(std::move(ChildRecords), Out);
std::vector> ChildFunctions =
genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path);
AppendVector(std::move(ChildFunctions), Out);
std::vector> ChildEnums =
genEnumsBlock(I.ChildEnums, CDCtx);
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;
}
/// Generator for HTML documentation.
class HTMLGenerator : public Generator {
public:
static const char *Format;
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) override;
bool createResources(ClangDocContext &CDCtx) override;
};
const char *HTMLGenerator::Format = "html";
llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) {
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), InfoIndex, CDCtx,
InfoTitle);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_record: {
std::vector> Nodes = genHTML(
*static_cast(I), InfoIndex, CDCtx, InfoTitle);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_enum: {
std::vector> Nodes =
genHTML(*static_cast(I), CDCtx);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_function: {
std::vector> Nodes =
genHTML(*static_cast(I), CDCtx, "");
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_default:
return llvm::make_error("Unexpected info type.\n",
llvm::inconvertibleErrorCode());
}
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);
return llvm::Error::success();
}
static std::string getRefType(InfoType IT) {
switch (IT) {
case InfoType::IT_default:
return "default";
case InfoType::IT_namespace:
return "namespace";
case InfoType::IT_record:
return "record";
case InfoType::IT_function:
return "function";
case InfoType::IT_enum:
return "enum";
}
llvm_unreachable("Unknown InfoType");
}
static bool SerializeIndex(ClangDocContext &CDCtx) {
std::error_code OK;
std::error_code FileErr;
llvm::SmallString<128> FilePath;
llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
llvm::sys::path::append(FilePath, "index_json.js");
llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::F_None);
if (FileErr != OK) {
llvm::errs() << "Error creating index file: " << FileErr.message() << "\n";
return false;
}
CDCtx.Idx.sort();
llvm::json::OStream J(OS, 2);
std::function IndexToJSON = [&](Index I) {
J.object([&] {
J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
J.attribute("Name", I.Name);
J.attribute("RefType", getRefType(I.RefType));
J.attribute("Path", I.Path);
J.attributeArray("Children", [&] {
for (const Index &C : I.Children)
IndexToJSON(C);
});
});
};
OS << "var JsonIndex = `\n";
IndexToJSON(CDCtx.Idx);
OS << "`;\n";
return true;
}
static bool CopyFile(StringRef FilePath, StringRef OutDirectory) {
llvm::SmallString<128> PathWrite;
llvm::sys::path::native(OutDirectory, PathWrite);
llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
llvm::SmallString<128> PathRead;
llvm::sys::path::native(FilePath, PathRead);
std::error_code OK;
std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
if (FileErr != OK) {
llvm::errs() << "Error creating file "
<< llvm::sys::path::filename(FilePath) << ": "
<< FileErr.message() << "\n";
return false;
}
return true;
}
bool HTMLGenerator::createResources(ClangDocContext &CDCtx) {
if (!SerializeIndex(CDCtx))
return false;
for (const auto &FilePath : CDCtx.UserStylesheets)
if (!CopyFile(FilePath, CDCtx.OutDirectory))
return false;
for (const auto &FilePath : CDCtx.FilesToCopy)
if (!CopyFile(FilePath, CDCtx.OutDirectory))
return false;
return true;
}
static GeneratorRegistry::Add 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