forked from OSchip/llvm-project
313 lines
9.1 KiB
C++
313 lines
9.1 KiB
C++
|
//===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===//
|
||
|
//
|
||
|
// The LLVM Compiler Infrastructure
|
||
|
//
|
||
|
// This file is distributed under the University of Illinois Open Source
|
||
|
// License. See LICENSE.TXT for details.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "Generators.h"
|
||
|
#include "Representation.h"
|
||
|
#include "llvm/ADT/StringRef.h"
|
||
|
#include "llvm/Support/FileSystem.h"
|
||
|
#include "llvm/Support/Path.h"
|
||
|
#include <string>
|
||
|
|
||
|
using namespace llvm;
|
||
|
|
||
|
namespace clang {
|
||
|
namespace doc {
|
||
|
|
||
|
// Enum conversion
|
||
|
|
||
|
std::string getAccess(AccessSpecifier AS) {
|
||
|
switch (AS) {
|
||
|
case AccessSpecifier::AS_public:
|
||
|
return "public";
|
||
|
case AccessSpecifier::AS_protected:
|
||
|
return "protected";
|
||
|
case AccessSpecifier::AS_private:
|
||
|
return "private";
|
||
|
case AccessSpecifier::AS_none:
|
||
|
return {};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::string getTagType(TagTypeKind AS) {
|
||
|
switch (AS) {
|
||
|
case TagTypeKind::TTK_Class:
|
||
|
return "class";
|
||
|
case TagTypeKind::TTK_Union:
|
||
|
return "union";
|
||
|
case TagTypeKind::TTK_Interface:
|
||
|
return "interface";
|
||
|
case TagTypeKind::TTK_Struct:
|
||
|
return "struct";
|
||
|
case TagTypeKind::TTK_Enum:
|
||
|
return "enum";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Markdown generation
|
||
|
|
||
|
std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; }
|
||
|
|
||
|
std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; }
|
||
|
|
||
|
std::string genLink(const Twine &Text, const Twine &Link) {
|
||
|
return "[" + Text.str() + "](" + Link.str() + ")";
|
||
|
}
|
||
|
|
||
|
std::string genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
|
||
|
std::string Buffer;
|
||
|
llvm::raw_string_ostream Stream(Buffer);
|
||
|
bool First = true;
|
||
|
for (const auto &R : Refs) {
|
||
|
if (!First)
|
||
|
Stream << ", ";
|
||
|
Stream << R.Name;
|
||
|
First = false;
|
||
|
}
|
||
|
return Stream.str();
|
||
|
}
|
||
|
|
||
|
void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; }
|
||
|
|
||
|
void writeNewLine(raw_ostream &OS) { OS << "\n"; }
|
||
|
|
||
|
void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
|
||
|
OS << std::string(Num, '#') + " " + Text << "\n";
|
||
|
}
|
||
|
|
||
|
void writeFileDefinition(const Location &L, raw_ostream &OS) {
|
||
|
OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " +
|
||
|
L.Filename)
|
||
|
<< "\n";
|
||
|
}
|
||
|
|
||
|
void writeDescription(const CommentInfo &I, raw_ostream &OS) {
|
||
|
if (I.Kind == "FullComment") {
|
||
|
for (const auto &Child : I.Children)
|
||
|
writeDescription(*Child, OS);
|
||
|
} else if (I.Kind == "ParagraphComment") {
|
||
|
for (const auto &Child : I.Children)
|
||
|
writeDescription(*Child, OS);
|
||
|
writeNewLine(OS);
|
||
|
} else if (I.Kind == "BlockCommandComment") {
|
||
|
OS << genEmphasis(I.Name);
|
||
|
for (const auto &Child : I.Children)
|
||
|
writeDescription(*Child, OS);
|
||
|
} else if (I.Kind == "InlineCommandComment") {
|
||
|
OS << genEmphasis(I.Name) << " " << I.Text;
|
||
|
} else if (I.Kind == "ParamCommandComment") {
|
||
|
std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
|
||
|
OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n";
|
||
|
} else if (I.Kind == "TParamCommandComment") {
|
||
|
std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
|
||
|
OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n";
|
||
|
} else if (I.Kind == "VerbatimBlockComment") {
|
||
|
for (const auto &Child : I.Children)
|
||
|
writeDescription(*Child, OS);
|
||
|
} else if (I.Kind == "VerbatimBlockLineComment") {
|
||
|
OS << I.Text;
|
||
|
writeNewLine(OS);
|
||
|
} else if (I.Kind == "VerbatimLineComment") {
|
||
|
OS << I.Text;
|
||
|
writeNewLine(OS);
|
||
|
} else if (I.Kind == "HTMLStartTagComment") {
|
||
|
if (I.AttrKeys.size() != I.AttrValues.size())
|
||
|
return;
|
||
|
std::string Buffer;
|
||
|
llvm::raw_string_ostream Attrs(Buffer);
|
||
|
for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)
|
||
|
Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
|
||
|
|
||
|
std::string CloseTag = I.SelfClosing ? "/>" : ">";
|
||
|
writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
|
||
|
} else if (I.Kind == "HTMLEndTagComment") {
|
||
|
writeLine("</" + I.Name + ">", OS);
|
||
|
} else if (I.Kind == "TextComment") {
|
||
|
OS << I.Text;
|
||
|
} else {
|
||
|
OS << "Unknown comment kind: " << I.Kind << ".\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) {
|
||
|
if (I.Scoped)
|
||
|
writeLine("| enum class " + I.Name + " |", OS);
|
||
|
else
|
||
|
writeLine("| enum " + I.Name + " |", OS);
|
||
|
writeLine("--", OS);
|
||
|
|
||
|
std::string Buffer;
|
||
|
llvm::raw_string_ostream Members(Buffer);
|
||
|
if (!I.Members.empty())
|
||
|
for (const auto &N : I.Members)
|
||
|
Members << "| " << N << " |\n";
|
||
|
writeLine(Members.str(), OS);
|
||
|
if (I.DefLoc)
|
||
|
writeFileDefinition(I.DefLoc.getValue(), OS);
|
||
|
|
||
|
for (const auto &C : I.Description)
|
||
|
writeDescription(C, OS);
|
||
|
}
|
||
|
|
||
|
void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) {
|
||
|
std::string Buffer;
|
||
|
llvm::raw_string_ostream Stream(Buffer);
|
||
|
bool First = true;
|
||
|
for (const auto &N : I.Params) {
|
||
|
if (!First)
|
||
|
Stream << ", ";
|
||
|
Stream << N.Type.Name + " " + N.Name;
|
||
|
First = false;
|
||
|
}
|
||
|
std::string Access = getAccess(I.Access);
|
||
|
if (Access != "")
|
||
|
writeHeader(genItalic(Access) + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS);
|
||
|
else
|
||
|
writeHeader(I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS);
|
||
|
if (I.DefLoc)
|
||
|
writeFileDefinition(I.DefLoc.getValue(), OS);
|
||
|
|
||
|
for (const auto &C : I.Description)
|
||
|
writeDescription(C, OS);
|
||
|
}
|
||
|
|
||
|
void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) {
|
||
|
if (I.Name == "")
|
||
|
writeHeader("Global Namespace", 1, OS);
|
||
|
else
|
||
|
writeHeader("namespace " + I.Name, 1, OS);
|
||
|
writeNewLine(OS);
|
||
|
|
||
|
if (!I.Description.empty()) {
|
||
|
for (const auto &C : I.Description)
|
||
|
writeDescription(C, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
|
||
|
if (!I.ChildNamespaces.empty()) {
|
||
|
writeHeader("Namespaces", 2, OS);
|
||
|
for (const auto &R : I.ChildNamespaces)
|
||
|
writeLine(R.Name, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
if (!I.ChildRecords.empty()) {
|
||
|
writeHeader("Records", 2, OS);
|
||
|
for (const auto &R : I.ChildRecords)
|
||
|
writeLine(R.Name, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
if (!I.ChildFunctions.empty()) {
|
||
|
writeHeader("Functions", 2, OS);
|
||
|
for (const auto &F : I.ChildFunctions)
|
||
|
genMarkdown(F, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
if (!I.ChildEnums.empty()) {
|
||
|
writeHeader("Enums", 2, OS);
|
||
|
for (const auto &E : I.ChildEnums)
|
||
|
genMarkdown(E, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) {
|
||
|
writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
|
||
|
if (I.DefLoc)
|
||
|
writeFileDefinition(I.DefLoc.getValue(), OS);
|
||
|
|
||
|
if (!I.Description.empty()) {
|
||
|
for (const auto &C : I.Description)
|
||
|
writeDescription(C, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
|
||
|
std::string Parents = genReferenceList(I.Parents);
|
||
|
std::string VParents = genReferenceList(I.VirtualParents);
|
||
|
if (!Parents.empty() || !VParents.empty()) {
|
||
|
if (Parents.empty())
|
||
|
writeLine("Inherits from " + VParents, OS);
|
||
|
else if (VParents.empty())
|
||
|
writeLine("Inherits from " + Parents, OS);
|
||
|
else
|
||
|
writeLine("Inherits from " + Parents + ", " + VParents, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
|
||
|
if (!I.Members.empty()) {
|
||
|
writeHeader("Members", 2, OS);
|
||
|
for (const auto Member : I.Members) {
|
||
|
std::string Access = getAccess(Member.Access);
|
||
|
if (Access != "")
|
||
|
writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
|
||
|
else
|
||
|
writeLine(Member.Type.Name + " " + Member.Name, OS);
|
||
|
}
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
|
||
|
if (!I.ChildRecords.empty()) {
|
||
|
writeHeader("Records", 2, OS);
|
||
|
for (const auto &R : I.ChildRecords)
|
||
|
writeLine(R.Name, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
if (!I.ChildFunctions.empty()) {
|
||
|
writeHeader("Functions", 2, OS);
|
||
|
for (const auto &F : I.ChildFunctions)
|
||
|
genMarkdown(F, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
if (!I.ChildEnums.empty()) {
|
||
|
writeHeader("Enums", 2, OS);
|
||
|
for (const auto &E : I.ChildEnums)
|
||
|
genMarkdown(E, OS);
|
||
|
writeNewLine(OS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Generator for Markdown documentation.
|
||
|
class MDGenerator : public Generator {
|
||
|
public:
|
||
|
static const char *Format;
|
||
|
|
||
|
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
|
||
|
};
|
||
|
|
||
|
const char *MDGenerator::Format = "md";
|
||
|
|
||
|
llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
|
||
|
switch (I->IT) {
|
||
|
case InfoType::IT_namespace:
|
||
|
genMarkdown(*static_cast<clang::doc::NamespaceInfo *>(I), OS);
|
||
|
break;
|
||
|
case InfoType::IT_record:
|
||
|
genMarkdown(*static_cast<clang::doc::RecordInfo *>(I), OS);
|
||
|
break;
|
||
|
case InfoType::IT_enum:
|
||
|
genMarkdown(*static_cast<clang::doc::EnumInfo *>(I), OS);
|
||
|
break;
|
||
|
case InfoType::IT_function:
|
||
|
genMarkdown(*static_cast<clang::doc::FunctionInfo *>(I), OS);
|
||
|
break;
|
||
|
case InfoType::IT_default:
|
||
|
return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
|
||
|
llvm::inconvertibleErrorCode());
|
||
|
}
|
||
|
return llvm::Error::success();
|
||
|
}
|
||
|
|
||
|
static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,
|
||
|
"Generator for MD output.");
|
||
|
|
||
|
// This anchor is used to force the linker to link in the generated object file
|
||
|
// and thus register the generator.
|
||
|
volatile int MDGeneratorAnchorSource = 0;
|
||
|
|
||
|
} // namespace doc
|
||
|
} // namespace clang
|