> 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;
}
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) {
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));
std::unique_ptr 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> genHTML(const FunctionInfo &I,
StringRef ParentInfoDir) {
std::vector> Out;
Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H3, I.Name));
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)
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> genHTML(const NamespaceInfo &I,
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, I.Path);
AppendVector(std::move(ChildFunctions), Out);
std::vector> ChildEnums =
genEnumsBlock(I.ChildEnums);
AppendVector(std::move(ChildEnums), Out);
return Out;
}
static std::vector> genHTML(const RecordInfo &I,
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)
Out.emplace_back(writeFileDefinition(I.DefLoc.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, I.Path);
AppendVector(std::move(ChildFunctions), Out);
std::vector> ChildEnums =
genEnumsBlock(I.ChildEnums);
AppendVector(std::move(ChildEnums), 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,
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);
switch (I->IT) {
case InfoType::IT_namespace: {
std::vector> Nodes =
genHTML(*static_cast(I), InfoTitle);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_record: {
std::vector> Nodes =
genHTML(*static_cast(I), InfoTitle);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_enum: {
std::vector> Nodes =
genHTML(*static_cast(I));
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_function: {
std::vector> Nodes =
genHTML(*static_cast(I), "");
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);
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