[clang-doc] Add index in each info html file

An index structure is created while generating the output file for each
info. This structure is parsed to JSON and written to a file in the
output directory. The html for the index is not rendered by clang-doc. A
Javascript file is included in the output directory, this will the JSON
file and insert HTML elements into the file.

Differential Revision: https://reviews.llvm.org/D65690

llvm-svn: 368070
This commit is contained in:
Diego Astiazaran 2019-08-06 18:31:46 +00:00
parent a63417fe6c
commit 7dfe0bc3c1
16 changed files with 432 additions and 60 deletions

View File

@ -57,6 +57,57 @@ std::string getTagType(TagTypeKind AS) {
llvm_unreachable("Unknown TagTypeKind");
}
bool Generator::createResources(ClangDocContext &CDCtx) { return true; }
// A function to add a reference to Info in Idx.
// Given an Info X with the following namespaces: [B,A]; a reference to X will
// be added in the children of a reference to B, which should be also a child of
// a reference to A, where A is a child of Idx.
// Idx
// |-- A
// |--B
// |--X
// If the references to the namespaces do not exist, they will be created. If
// the references already exist, the same one will be used.
void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
// Index pointer that will be moving through Idx until the first parent
// namespace of Info (where the reference has to be inserted) is found.
Index *I = &Idx;
// The Namespace vector includes the upper-most namespace at the end so the
// loop will start from the end to find each of the namespaces.
for (const auto &R : llvm::reverse(Info->Namespace)) {
// Look for the current namespace in the children of the index I is
// pointing.
auto It = std::find(I->Children.begin(), I->Children.end(), R.USR);
if (It != I->Children.end()) {
// If it is found, just change I to point the namespace refererence found.
I = &*It;
} else {
// If it is not found a new reference is created
I->Children.emplace_back(R.USR, R.Name, R.RefType, R.Path);
// I is updated with the reference of the new namespace reference
I = &I->Children.back();
}
}
// Look for Info in the vector where it is supposed to be; it could already
// exist if it is a parent namespace of an Info already passed to this
// function.
auto It = std::find(I->Children.begin(), I->Children.end(), Info->USR);
if (It == I->Children.end()) {
// If it is not in the vector it is inserted
I->Children.emplace_back(Info->USR, Info->extractName(), Info->IT,
Info->Path);
} else {
// If it not in the vector we only check if Path and Name are not empty
// because if the Info was included by a namespace it may not have those
// values.
if (It->Path.empty())
It->Path = Info->Path;
if (It->Name.empty())
It->Name = Info->extractName();
}
}
// This anchor is used to force the linker to link in the generated object file
// and thus register the generators.
extern volatile int YAMLGeneratorAnchorSource;

View File

@ -28,7 +28,13 @@ public:
// Write out the decl info in the specified format.
virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) = 0;
virtual bool createResources(ClangDocContext CDCtx) = 0;
// This function writes a file with the index previously constructed.
// It can be overwritten by any of the inherited generators.
// If the override method wants to run this it should call
// Generator::createResources(CDCtx);
virtual bool createResources(ClangDocContext &CDCtx);
static void addInfoToIndex(Index &Idx, const doc::Info *Info);
};
typedef llvm::Registry<Generator> GeneratorRegistry;

View File

@ -11,7 +11,9 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
using namespace llvm;
@ -25,17 +27,19 @@ class HTMLTag {
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_SCRIPT,
TAG_SPAN,
TAG_TITLE,
TAG_UL,
};
HTMLTag() = default;
@ -106,15 +110,17 @@ bool HTMLTag::IsSelfClosing() const {
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_P:
case HTMLTag::TAG_UL:
case HTMLTag::TAG_LI:
case HTMLTag::TAG_A:
case HTMLTag::TAG_P:
case HTMLTag::TAG_SCRIPT:
case HTMLTag::TAG_SPAN:
case HTMLTag::TAG_TITLE:
case HTMLTag::TAG_UL:
return false;
}
llvm_unreachable("Unhandled HTMLTag::TagType");
@ -122,10 +128,8 @@ bool HTMLTag::IsSelfClosing() const {
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 +138,22 @@ llvm::SmallString<16> HTMLTag::ToString() const {
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_SCRIPT:
return llvm::SmallString<16>("script");
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");
}
@ -222,7 +232,7 @@ static SmallString<128> computeRelativePath(StringRef FilePath,
// HTML generation
std::vector<std::unique_ptr<TagNode>>
static std::vector<std::unique_ptr<TagNode>>
genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
for (const auto &FilePath : CDCtx.UserStylesheets) {
@ -239,6 +249,19 @@ genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
return Out;
}
static std::vector<std::unique_ptr<TagNode>>
genJsScriptsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
for (const auto &FilePath : CDCtx.JsScripts) {
auto ScriptNode = llvm::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
SmallString<128> ScriptPath = computeRelativePath("", InfoPath);
llvm::sys::path::append(ScriptPath, llvm::sys::path::filename(FilePath));
ScriptNode->Attributes.try_emplace("src", ScriptPath);
Out.emplace_back(std::move(ScriptNode));
}
return Out;
}
static std::unique_ptr<TagNode> genLink(const Twine &Text, const Twine &Link) {
auto LinkNode = llvm::make_unique<TagNode>(HTMLTag::TAG_A, Text);
LinkNode->Attributes.try_emplace("href", Link.str());
@ -362,6 +385,28 @@ static std::unique_ptr<TagNode> writeFileDefinition(const Location &L) {
"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);
std::vector<std::unique_ptr<TagNode>> JsNodes =
genJsScriptsHTML(InfoPath, CDCtx);
AppendVector(std::move(JsNodes), Out);
// An empty <div> is generated but the index will be then rendered here
auto IndexNode = llvm::make_unique<TagNode>(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<HTMLNode> genHTML(const CommentInfo &I) {
if (I.Kind == "FullComment") {
auto FullComment = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV);
@ -550,7 +595,7 @@ public:
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) override;
bool createResources(ClangDocContext CDCtx) override;
bool createResources(ClangDocContext &CDCtx) override;
};
const char *HTMLGenerator::Format = "html";
@ -558,13 +603,7 @@ const char *HTMLGenerator::Format = "html";
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: {
@ -596,36 +635,88 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
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);
F.Children.emplace_back(std::move(MainContentNode));
F.Render(OS);
return llvm::Error::success();
}
bool HTMLGenerator::createResources(ClangDocContext CDCtx) {
llvm::outs() << "Generating stylesheet for docs...\n";
for (const auto &FilePath : CDCtx.UserStylesheets) {
llvm::SmallString<128> StylesheetPathWrite;
llvm::sys::path::native(CDCtx.OutDirectory, StylesheetPathWrite);
llvm::sys::path::append(StylesheetPathWrite,
llvm::sys::path::filename(FilePath));
llvm::SmallString<128> StylesheetPathRead;
llvm::sys::path::native(FilePath, StylesheetPathRead);
std::error_code OK;
std::error_code FileErr =
llvm::sys::fs::copy_file(StylesheetPathRead, StylesheetPathWrite);
if (FileErr != OK) {
llvm::errs() << "Error creating stylesheet file "
<< llvm::sys::path::filename(FilePath) << ": "
<< FileErr.message() << "\n";
return false;
}
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<void(Index)> 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;
}

View File

@ -252,7 +252,6 @@ public:
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) override;
bool createResources(ClangDocContext CDCtx) override { return true; }
};
const char *MDGenerator::Format = "md";

View File

@ -197,7 +197,7 @@ void FunctionInfo::merge(FunctionInfo &&Other) {
SymbolInfo::merge(std::move(Other));
}
llvm::SmallString<16> Info::extractName() {
llvm::SmallString<16> Info::extractName() const {
if (!Name.empty())
return Name;
@ -229,5 +229,11 @@ llvm::SmallString<16> Info::extractName() {
return llvm::SmallString<16>("");
}
void Index::sort() {
std::sort(Children.begin(), Children.end());
for (auto &C : Children)
C.sort();
}
} // namespace doc
} // namespace clang

View File

@ -249,7 +249,7 @@ struct Info {
void mergeBase(Info &&I);
bool mergeable(const Info &Other);
llvm::SmallString<16> extractName();
llvm::SmallString<16> extractName() const;
// Returns a reference to the parent scope (that is, the immediate parent
// namespace or class in which this decl resides).
@ -348,6 +348,19 @@ struct EnumInfo : public SymbolInfo {
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) {}
// 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; }
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.
@ -357,10 +370,24 @@ llvm::Expected<std::unique_ptr<Info>>
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,
std::vector<std::string> JsScripts)
: ECtx(ECtx), PublicOnly(PublicOnly), OutDirectory(OutDirectory),
UserStylesheets(UserStylesheets), JsScripts(JsScripts) {}
tooling::ExecutionContext *ECtx;
bool PublicOnly;
std::string OutDirectory;
// Path of CSS stylesheets that will be copied to OutDirectory and used to
// style all HTML files.
std::vector<std::string> UserStylesheets;
// JavaScript files that will be imported in allHTML file.
std::vector<std::string> JsScripts;
// Other files that should be copied to OutDirectory, besides UserStylesheets.
std::vector<std::string> FilesToCopy;
Index Idx;
};
} // namespace doc

View File

@ -246,7 +246,6 @@ public:
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) override;
bool createResources(ClangDocContext CDCtx) override { return true; }
};
const char *YAMLGenerator::Format = "yaml";

View File

@ -0,0 +1,81 @@
// Append using posix-style a file name or directory to Base
function append(Base, New) {
if (!New)
return Base;
if (Base)
Base += "/";
Base += New;
return Base;
}
// Get relative path to access FilePath from CurrentDirectory
function computeRelativePath(FilePath, CurrentDirectory) {
var Path = FilePath;
while (Path) {
if (CurrentDirectory == Path)
return FilePath.substring(Path.length + 1);
Path = Path.substring(0, Path.lastIndexOf("/"));
}
var Dir = CurrentDirectory;
var Result = "";
while (Dir) {
if (Dir == FilePath)
break;
Dir = Dir.substring(0, Dir.lastIndexOf("/"));
Result = append(Result, "..")
}
Result = append(Result, FilePath.substring(Dir.length))
return Result;
}
function genLink(Ref, CurrentDirectory) {
var Path = computeRelativePath(Ref.Path, CurrentDirectory);
Path = append(Path, Ref.Name + ".html")
ANode = document.createElement("a");
ANode.setAttribute("href", Path);
var TextNode = document.createTextNode(Ref.Name);
ANode.appendChild(TextNode);
return ANode;
}
function genHTMLOfIndex(Index, CurrentDirectory) {
// Out will store the HTML elements that Index requires to be generated
var Out = [];
if (Index.Name) {
var SpanNode = document.createElement("span");
var TextNode = document.createTextNode(Index.Name);
SpanNode.appendChild(genLink(Index, CurrentDirectory));
Out.push(SpanNode);
}
if (Index.Children.length == 0)
return Out;
var UlNode = document.createElement("ul");
for (Child of Index.Children) {
var LiNode = document.createElement("li");
ChildNodes = genHTMLOfIndex(Child, CurrentDirectory);
for (Node of ChildNodes)
LiNode.appendChild(Node);
UlNode.appendChild(LiNode);
}
Out.push(UlNode);
return Out;
}
function createIndex(Index) {
// Get the DOM element where the index will be created
var IndexDiv = document.getElementById("index");
// Get the relative path of this file
CurrentDirectory = IndexDiv.getAttribute("path");
var IndexNodes = genHTMLOfIndex(Index, CurrentDirectory);
for (Node of IndexNodes)
IndexDiv.appendChild(Node);
}
// Runs after DOM loads
document.addEventListener("DOMContentLoaded", function() {
// JsonIndex is a variable from another file that contains the index
// in JSON format
var Index = JSON.parse(JsonIndex);
createIndex(Index);
});

View File

@ -15,6 +15,10 @@ target_link_libraries(clang-doc
clangToolingCore
)
install(FILES ../stylesheets/clang-doc-default-stylesheet.css
install(FILES ../assets/clang-doc-default-stylesheet.css
DESTINATION share/clang
COMPONENT clang-doc)
install(FILES ../assets/index.js
DESTINATION share/clang
COMPONENT clang-doc)

View File

@ -215,18 +215,26 @@ int main(int argc, const char **argv) {
Exec->get()->getExecutionContext(),
PublicOnly,
OutDirectory,
{UserStylesheets.begin(), UserStylesheets.end()}};
{UserStylesheets.begin(), UserStylesheets.end()},
{"index.js", "index_json.js"}};
if (Format == "html") {
void *MainAddr = (void *)(intptr_t)GetExecutablePath;
std::string ClangDocPath = GetExecutablePath(argv[0], MainAddr);
llvm::SmallString<128> AssetsPath;
llvm::sys::path::native(ClangDocPath, AssetsPath);
AssetsPath = llvm::sys::path::parent_path(AssetsPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang");
llvm::SmallString<128> DefaultStylesheet;
llvm::sys::path::native(ClangDocPath, DefaultStylesheet);
DefaultStylesheet = llvm::sys::path::parent_path(DefaultStylesheet);
llvm::sys::path::native(AssetsPath, DefaultStylesheet);
llvm::sys::path::append(DefaultStylesheet,
"../share/clang/clang-doc-default-stylesheet.css");
"clang-doc-default-stylesheet.css");
llvm::SmallString<128> IndexJS;
llvm::sys::path::native(AssetsPath, IndexJS);
llvm::sys::path::append(IndexJS, "index.js");
CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
DefaultStylesheet.str());
CDCtx.FilesToCopy.emplace_back(IndexJS.str());
}
// Mapping phase
@ -276,10 +284,14 @@ int main(int argc, const char **argv) {
continue;
}
// Add a reference to this Info in the Index
clang::doc::Generator::addInfoToIndex(CDCtx.Idx, I);
if (auto Err = G->get()->generateDocForInfo(I, InfoOS, CDCtx))
llvm::errs() << toString(std::move(Err)) << "\n";
}
llvm::outs() << "Generating assets for docs...\n";
if (!G->get()->createResources(CDCtx))
return 1;

View File

@ -12,6 +12,7 @@ include_directories(
add_extra_unittest(ClangDocTests
BitcodeTest.cpp
ClangDocTest.cpp
GeneratorTest.cpp
HTMLGeneratorTest.cpp
MDGeneratorTest.cpp
MergeTest.cpp

View File

@ -63,6 +63,7 @@ void CheckCommentInfo(CommentInfo &Expected, CommentInfo &Actual) {
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 @@ void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
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

View File

@ -44,6 +44,8 @@ void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual);
void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual);
void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual);
void CheckIndex(Index &Expected, Index &Actual);
} // namespace doc
} // namespace clang

View File

@ -0,0 +1,74 @@
//===-- 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) {
Index Idx;
auto InfoA = llvm::make_unique<Info>();
InfoA->Name = "A";
InfoA->USR = serialize::hashUSR("1");
Generator::addInfoToIndex(Idx, InfoA.get());
auto InfoC = llvm::make_unique<Info>();
InfoC->Name = "C";
InfoC->USR = serialize::hashUSR("3");
Reference RefB = Reference("B");
RefB.USR = serialize::hashUSR("2");
InfoC->Namespace = {std::move(RefB)};
Generator::addInfoToIndex(Idx, InfoC.get());
auto InfoD = llvm::make_unique<Info>();
InfoD->Name = "D";
InfoD->USR = serialize::hashUSR("4");
auto InfoF = llvm::make_unique<Info>();
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)};
Generator::addInfoToIndex(Idx, InfoF.get());
auto InfoG = llvm::make_unique<Info>(InfoType::IT_namespace);
Generator::addInfoToIndex(Idx, InfoG.get());
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));
Index IndexG;
IndexG.Name = "GlobalNamespace";
IndexG.RefType = InfoType::IT_namespace;
ExpectedIdx.Children.emplace_back(std::move(IndexG));
CheckIndex(ExpectedIdx, Idx);
}
} // namespace doc
} // namespace clang

View File

@ -28,6 +28,7 @@ getClangDocContext(std::vector<std::string> UserStylesheets = {}) {
CDCtx.UserStylesheets.insert(
CDCtx.UserStylesheets.begin(),
"../share/clang/clang-doc-default-stylesheet.css");
CDCtx.JsScripts.emplace_back("index.js");
return CDCtx;
}
@ -56,6 +57,8 @@ TEST(HTMLGeneratorTest, emitNamespaceHTML) {
<title>namespace Namespace</title>
<link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
<link rel="stylesheet" href="user-provided-stylesheet.css"/>
<script src="index.js"></script>
<div id="index" path=""></div>
<div>
<h1>namespace Namespace</h1>
<h2>Namespaces</h2>
@ -114,6 +117,8 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
<meta charset="utf-8"/>
<title>class r</title>
<link rel="stylesheet" href="../../../clang-doc-default-stylesheet.css"/>
<script src="../../../index.js"></script>
<div id="index" path="X/Y/Z"></div>
<div>
<h1>class r</h1>
<p>Defined at line 10 of test.cpp</p>
@ -175,6 +180,8 @@ TEST(HTMLGeneratorTest, emitFunctionHTML) {
<meta charset="utf-8"/>
<title></title>
<link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
<script src="index.js"></script>
<div id="index" path=""></div>
<div>
<h3>f</h3>
<p>
@ -212,6 +219,8 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
<meta charset="utf-8"/>
<title></title>
<link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
<script src="index.js"></script>
<div id="index" path=""></div>
<div>
<h3>enum class e</h3>
<ul>
@ -281,6 +290,8 @@ TEST(HTMLGeneratorTest, emitCommentHTML) {
<meta charset="utf-8"/>
<title></title>
<link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
<script src="index.js"></script>
<div id="index" path=""></div>
<div>
<h3>f</h3>
<p>void f(int I, int J)</p>