phosek created this revision.
phosek added reviewers: abrachet, paulkirth.
Herald added a project: All.
phosek requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.
This change tries to improve the usability of Markdown output, taking
the inspiration from other documentation generators where appropriate.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D130231
Files:
clang-tools-extra/clang-doc/MDGenerator.cpp
Index: clang-tools-extra/clang-doc/MDGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/MDGenerator.cpp
+++ clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -20,6 +20,37 @@
// Markdown generation
+static std::string genEscaped(StringRef Name) {
+ std::string Buffer;
+ llvm::raw_string_ostream Stream(Buffer);
+ for (unsigned I = 0, E = Name.size(); I != E; ++I) {
+ unsigned char C = Name[I];
+ switch (C) {
+ case '\\':
+ case '`':
+ case '*':
+ case '_':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '#':
+ case '+':
+ case '-':
+ case '.':
+ case '!':
+ Stream << '\\';
+ LLVM_FALLTHROUGH;
+ default:
+ Stream << C;
+ break;
+ }
+ }
+ return Stream.str();
+}
+
static std::string genItalic(const Twine &Text) {
return "*" + Text.str() + "*";
}
@@ -28,6 +59,10 @@
return "**" + Text.str() + "**";
}
+static std::string genCode(const Twine &Text) {
+ return "`" + Text.str() + "`";
+}
+
static std::string
genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
std::string Buffer;
@@ -40,21 +75,18 @@
return Stream.str();
}
-static void writeLine(const Twine &Text, raw_ostream &OS) {
- OS << Text << "\n\n";
-}
-
-static void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
-
-static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
- OS << std::string(Num, '#') + " " + Text << "\n\n";
+static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS, const Twine &Anchor = "") {
+ OS << std::string(Num, '#') << " " << genEscaped(Text.str());
+ if (!Anchor.isTriviallyEmpty()) {
+ OS << " {#" << Anchor << "}";
+ }
+ OS << "\n";
}
static void writeFileDefinition(const ClangDocContext &CDCtx, const Location &L,
raw_ostream &OS) {
-
if (!CDCtx.RepositoryUrl) {
- OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
+ OS << "*foobar Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
<< "*";
} else {
OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.LineNumber)
@@ -63,7 +95,7 @@
<< std::to_string(L.LineNumber) << ")"
<< "*";
}
- OS << "\n\n";
+ OS << "\n";
}
static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
@@ -73,28 +105,37 @@
} else if (I.Kind == "ParagraphComment") {
for (const auto &Child : I.Children)
writeDescription(*Child, OS);
- writeNewLine(OS);
+ OS << "\n";
} else if (I.Kind == "BlockCommandComment") {
+ // TODO: @return is a block command and should be included in the "Returns" table.
+ // TODO: @see block commands should be grouped and rendered as "See Also" section.
+ // TODO: Figure out handling for @brief.
+ // TODO: What other block commands need special handling?
OS << genEmphasis(I.Name);
for (const auto &Child : I.Children)
writeDescription(*Child, OS);
} else if (I.Kind == "InlineCommandComment") {
- OS << genEmphasis(I.Name) << " " << I.Text;
+ OS << genEmphasis(I.ParamName) << " " << I.Text;
+ for (const auto &Child : I.Children)
+ writeDescription(*Child, OS);
} else if (I.Kind == "ParamCommandComment") {
+ // TODO: @param commands should included in the "Parameters" table.
std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
} else if (I.Kind == "TParamCommandComment") {
+ // TODO: @tparam commands should included in the "Template Parameters" table.
std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
} else if (I.Kind == "VerbatimBlockComment") {
+ // TODO: We should use ``` or indentation for verbatim blocks.
for (const auto &Child : I.Children)
writeDescription(*Child, OS);
} else if (I.Kind == "VerbatimBlockLineComment") {
- OS << I.Text;
- writeNewLine(OS);
+ // TODO: We should use ` for verbatim block lines.
+ OS << I.Text << "\n";
} else if (I.Kind == "VerbatimLineComment") {
- OS << I.Text;
- writeNewLine(OS);
+ // TODO: We should use ` for verbatim lines.
+ OS << I.Text << "\n";
} else if (I.Kind == "HTMLStartTagComment") {
if (I.AttrKeys.size() != I.AttrValues.size())
return;
@@ -104,11 +145,11 @@
Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
std::string CloseTag = I.SelfClosing ? "/>" : ">";
- writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
+ OS << "<" << I.Name << Attrs.str() << CloseTag << "\n";
} else if (I.Kind == "HTMLEndTagComment") {
- writeLine("</" + I.Name + ">", OS);
+ OS << "</" << I.Name << ">" << "\n";
} else if (I.Kind == "TextComment") {
- OS << I.Text;
+ OS << I.Text.substr(1) << "\n";
} else {
OS << "Unknown comment kind: " << I.Kind << ".\n\n";
}
@@ -127,17 +168,17 @@
static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
llvm::raw_ostream &OS) {
if (I.Scoped)
- writeLine("| enum class " + I.Name + " |", OS);
+ OS << "| enum class " << I.Name << " |" << "\n";
else
- writeLine("| enum " + I.Name + " |", OS);
- writeLine("--", OS);
+ OS << "| enum " << I.Name << " |" << "\n";
+ OS << "--" << "\n";
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);
+ OS << Members.str() << "\n";
if (I.DefLoc)
writeFileDefinition(CDCtx, *I.DefLoc, OS);
@@ -145,6 +186,18 @@
writeDescription(C, OS);
}
+static void genMarkdown(const ClangDocContext &CDCtx, const MemberTypeInfo &I,
+ llvm::raw_ostream &OS) {
+ writeHeader(I.Name, 3, OS, I.Name);
+ OS << "\n";
+ std::string Access = getAccessSpelling(I.Access).str();
+ if (Access != "")
+ OS << genCode(Access + " " + I.Type.Name + " " + I.Name) << "\n";
+ else
+ OS << genCode(I.Type.Name + " " + I.Name) << "\n";
+ OS << "\n";
+}
+
static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,
llvm::raw_ostream &OS) {
std::string Buffer;
@@ -157,20 +210,39 @@
First = false;
}
writeHeader(I.Name, 3, OS);
+ OS << "\n";
std::string Access = getAccessSpelling(I.Access).str();
- if (Access != "")
- writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
- "(" + Stream.str() + ")"),
- OS);
- else
- writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
- Stream.str() + ")"),
- OS);
+ if (Access != "") {
+ OS << "```cpp\n";
+ OS << Access + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")\n";
+ OS << "```\n";
+ } else {
+ OS << "```cpp\n";
+ OS << I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")\n";
+ OS << "```\n";
+ }
if (I.DefLoc)
writeFileDefinition(CDCtx, *I.DefLoc, OS);
+ OS << "\n";
for (const auto &C : I.Description)
writeDescription(C, OS);
+
+ // Write function parameters as a table.
+
+ OS << "| Parameters | |\n";
+ OS << "| --- | --- |\n";
+ for (const auto &N : I.Params) {
+ OS << "| " << genCode(N.Name) << " | " << genCode(N.Type.Name) << "|\n";
+ }
+ OS << "\n";
+
+ // Write function return value as a table.
+
+ OS << "| Returns | |\n";
+ OS << "| --- | --- |\n";
+ OS << "| " << genCode(I.ReturnType.Type.Name) << " | " << "" << "|\n";
+ OS << "\n";
}
static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
@@ -179,12 +251,12 @@
writeHeader("Global Namespace", 1, OS);
else
writeHeader("namespace " + I.Name, 1, OS);
- writeNewLine(OS);
+ OS << "\n";
if (!I.Description.empty()) {
for (const auto &C : I.Description)
writeDescription(C, OS);
- writeNewLine(OS);
+ OS << "\n";
}
llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
@@ -196,7 +268,7 @@
writeNameLink(BasePath, R, OS);
OS << "\n";
}
- writeNewLine(OS);
+ OS << "\n";
}
if (!I.ChildRecords.empty()) {
@@ -206,76 +278,73 @@
writeNameLink(BasePath, R, OS);
OS << "\n";
}
- writeNewLine(OS);
+ OS << "\n";
}
if (!I.ChildFunctions.empty()) {
writeHeader("Functions", 2, OS);
for (const auto &F : I.ChildFunctions)
genMarkdown(CDCtx, F, OS);
- writeNewLine(OS);
+ OS << "\n";
}
if (!I.ChildEnums.empty()) {
writeHeader("Enums", 2, OS);
for (const auto &E : I.ChildEnums)
genMarkdown(CDCtx, E, OS);
- writeNewLine(OS);
+ OS << "\n";
}
}
static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,
llvm::raw_ostream &OS) {
- writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
+ writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS, I.Name);
if (I.DefLoc)
writeFileDefinition(CDCtx, *I.DefLoc, OS);
+ OS << "\n";
if (!I.Description.empty()) {
for (const auto &C : I.Description)
writeDescription(C, OS);
- writeNewLine(OS);
+ OS << "\n";
}
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);
+ OS << "Inherits from " << VParents << "\n";
else if (VParents.empty())
- writeLine("Inherits from " + Parents, OS);
+ OS << "Inherits from " << Parents << "\n";
else
- writeLine("Inherits from " + Parents + ", " + VParents, OS);
- writeNewLine(OS);
+ OS << "Inherits from " << Parents << ", " << VParents << "\n";
+ OS << "\n";
}
if (!I.Members.empty()) {
writeHeader("Members", 2, OS);
- for (const auto &Member : I.Members) {
- std::string Access = getAccessSpelling(Member.Access).str();
- if (Access != "")
- writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
- else
- writeLine(Member.Type.Name + " " + Member.Name, OS);
- }
- writeNewLine(OS);
+ OS << "\n";
+ for (const auto &Member : I.Members)
+ genMarkdown(CDCtx, Member, OS);
+ OS << "\n";
}
if (!I.ChildRecords.empty()) {
writeHeader("Records", 2, OS);
for (const auto &R : I.ChildRecords)
- writeLine(R.Name, OS);
- writeNewLine(OS);
+ OS << R.Name << "\n";
+ OS << "\n";
}
if (!I.ChildFunctions.empty()) {
writeHeader("Functions", 2, OS);
for (const auto &F : I.ChildFunctions)
genMarkdown(CDCtx, F, OS);
- writeNewLine(OS);
+ OS << "\n";
}
if (!I.ChildEnums.empty()) {
writeHeader("Enums", 2, OS);
for (const auto &E : I.ChildEnums)
genMarkdown(CDCtx, E, OS);
- writeNewLine(OS);
+ OS << "\n";
}
}
@@ -348,6 +417,43 @@
}
return llvm::Error::success();
}
+
+static llvm::Error genTableOfContents(ClangDocContext &CDCtx) {
+ std::error_code FileErr;
+ llvm::SmallString<128> FilePath;
+ llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
+ llvm::sys::path::append(FilePath, "_toc.yaml");
+ llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
+ if (FileErr)
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "error creating toc file: " + FileErr.message());
+
+ // Table of Contents has the following structure:
+ //
+ // toc:
+ // - title: namespace foo
+ // section:
+ // - title: Classes
+ // section:
+ // - title: Foo
+ // path: /path/to/foo.md
+
+ CDCtx.Idx.sort();
+ OS << "toc:\n";
+ for (auto C : CDCtx.Idx.Children) {
+ OS << "- title: " << C.Name << "\n";
+ llvm::SmallString<64> Path = C.getRelativeFilePath(""); // TODO: is this correct?
+ // Paths in Markdown use POSIX separators.
+ llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
+ llvm::sys::path::append(Path, llvm::sys::path::Style::posix,
+ C.getFileBaseName() + ".md");
+ OS << " path: " << Path << "\n";
+
+ // TODO: Handle the nested elements.
+ }
+ return llvm::Error::success();
+}
+
/// Generator for Markdown documentation.
class MDGenerator : public Generator {
public:
@@ -362,6 +468,7 @@
llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) {
+ OS << "[TOC]\n\n";
switch (I->IT) {
case InfoType::IT_namespace:
genMarkdown(CDCtx, *static_cast<clang::doc::NamespaceInfo *>(I), OS);
@@ -393,6 +500,10 @@
if (Err)
return Err;
+ Err = genTableOfContents(CDCtx);
+ if (Err)
+ return Err;
+
return llvm::Error::success();
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits