[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:
parent
a63417fe6c
commit
7dfe0bc3c1
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
});
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ include_directories(
|
|||
add_extra_unittest(ClangDocTests
|
||||
BitcodeTest.cpp
|
||||
ClangDocTest.cpp
|
||||
GeneratorTest.cpp
|
||||
HTMLGeneratorTest.cpp
|
||||
MDGeneratorTest.cpp
|
||||
MergeTest.cpp
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue