Index: clang-doc/generators/CMakeLists.txt =================================================================== --- clang-doc/generators/CMakeLists.txt +++ clang-doc/generators/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangDocGenerators GeneratorBase.cpp YAMLGenerator.cpp + MDGenerator.cpp LINK_LIBS clangDoc Index: clang-doc/generators/Generators.h =================================================================== --- clang-doc/generators/Generators.h +++ clang-doc/generators/Generators.h @@ -41,6 +41,54 @@ std::error_code OK; }; +class MDGenerator : public Generator { +public: + MDGenerator(std::unique_ptr &IS, StringRef Root, StringRef Format) : Generator(IS, Root, Format) {}; + virtual ~MDGenerator() {}; + + virtual int generate(); + +private: + int buildDirTree(); + + bool writeNamespaces(); + bool writeClasses(); + bool writeNamespacePage(const std::unique_ptr &I); + bool writeClassPage(const std::unique_ptr &I); + + void writeRecordSummary(const RecordInfo &I, raw_ostream &OS); + void writeType(const std::unique_ptr &N, raw_ostream &OS); + void writeFieldType(const std::unique_ptr &N, raw_ostream &OS); + void writeMemberType(const std::unique_ptr &N, raw_ostream &OS); + void writeEnum(const std::unique_ptr &I, raw_ostream &OS); + void writeFunction(const std::unique_ptr &I, raw_ostream &OS); + void writeDescription(const std::unique_ptr &I, raw_ostream &OS); + void writeFileDefinition(const Location &L, raw_ostream &OS); + + void sortRecordInfos(llvm::SmallVector Records); + + void writeLine(StringRef Text, raw_ostream &OS); + void writeBlankLine(raw_ostream &OS); + std::string genItalic(const Twine &Text); + std::string genEmphasis(const Twine &Text); + std::string genStrikethrough(const Twine &Text); + std::string genH1(const Twine &Text); + std::string genH2(const Twine &Text); + std::string genH3(const Twine &Text); + std::string genH4(const Twine &Text); + std::string genH5(const Twine &Text); + std::string genH6(const Twine &Text); + std::string genTableCell(const Twine &Text); + std::string genUnorderedItem(const Twine &Text, int Indent=0); + std::string genOrderedItem(const Twine &Text, int Pos, int Indent=0); + std::string genLink(const Twine &Text, const Twine &Link); + std::string genLinkWithTooltip(const Twine &Text, const Twine &Link, const Twine &Tooltip); + std::string genInlineCode(const Twine &Text); + + SmallString<128> NamespacesPath; + SmallString<128> ClassPath; +}; + class YAMLGenerator : public Generator { public: YAMLGenerator(std::unique_ptr &IS, StringRef Root, StringRef Format) @@ -56,7 +104,8 @@ StringRef Root, StringRef Format) { if (Format == "yaml") return llvm::make_unique(IS, Root, Format); - + if (Format == "md") + return llvm::make_unique(IS, Root, Format); errs() << "Unsupported documentation format.\n"; return nullptr; } Index: clang-doc/generators/MDGenerator.cpp =================================================================== --- /dev/null +++ clang-doc/generators/MDGenerator.cpp @@ -0,0 +1,366 @@ +//===-- 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 "../Representation.h" +#include "Generators.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace llvm; + +namespace clang { +namespace doc { + +int MDGenerator::generate() { + if (buildDirTree() && writeNamespaces() && writeClasses()) return 0; + return 1; +} + +// File creation and I/O + +int MDGenerator::buildDirTree() { + removeExistingDirectory(Root); + sys::path::native(Root, NamespacesPath); + sys::path::append(NamespacesPath, "namespaces"); + sys::path::native(Root, ClassPath); + sys::path::append(ClassPath, "classes"); + return buildDirectory(NamespacesPath) && buildDirectory(ClassPath); +} + +// Documentation generation + +bool MDGenerator::writeNamespaces() { + // TODO: Generate summary page + bool Success = true; + for (const auto &I : IS->getNamespaceInfos()) Success = writeNamespacePage(I); + return Success; +} + +bool MDGenerator::writeClasses() { + bool Success = true; + for (const auto &I : IS->getRecordInfos()) { + if (I->TagType == TagTypeKind::TTK_Class) Success = writeClassPage(I); + } + return Success; +} + +bool MDGenerator::writeNamespacePage(const std::unique_ptr &I) { + SmallString<128> Path; + sys::path::native(NamespacesPath, Path); + // for (const auto &Namespace : I->Namespace) + // sys::path::append(Path, IS->find(Namespace)->Name); + // std::error_code DirectoryStatus = sys::fs::create_directories(Path); + // if (DirectoryStatus != OK) { + // errs() << "Unable to create class directories.\n"; + // return 1; + // } + sys::path::append(Path, I->Name + ".md"); + std::error_code OutErrorInfo; + raw_fd_ostream OS(Path, OutErrorInfo, sys::fs::F_None); + if (OutErrorInfo != OK) { + errs() << "Error opening class file.\n"; + return false; + } + + writeLine(genH1("namespace " + I->Name), OS); + writeBlankLine(OS); + + for (const auto &C : I->Description) writeDescription(C, OS); + + // TODO: Write subnamespaces + + // Write functions. + bool wroteFunctionHeader = false; + for (const auto &F : IS->getFunctionInfos()) { + if (!F->Namespace.empty() && F->Namespace[0] == I->Name) { + if (!wroteFunctionHeader) { + wroteFunctionHeader = true; + writeLine(genH2("Functions"), OS); + } + writeFunction(F, OS); + writeBlankLine(OS); + } + } + + // Fetch and sort records. + llvm::SmallVector Structs; + llvm::SmallVector Classes; + llvm::SmallVector Unions; + for (const auto &R : IS->getRecordInfos()) { + if (!R->Namespace.empty() && R->Namespace[0] == I->Name) { + switch (R->TagType) { + case TagTypeKind::TTK_Class: + Classes.push_back(R.get()); + break; + case TagTypeKind::TTK_Struct: + Structs.push_back(R.get()); + break; + case TagTypeKind::TTK_Union: + Unions.push_back(R.get()); + break; + default: + continue; + } + } + } + + // Write structs. + bool wroteHeader = false; + sortRecordInfos(Structs); + for (const auto &S : Structs) { + if (!wroteHeader) { + wroteHeader = true; + writeLine(genH2("Structs"), OS); + } + writeRecordSummary(*S, OS); + writeBlankLine(OS); + } + + // Write classes. + wroteHeader = false; + sortRecordInfos(Classes); + for (const auto &C : Classes) { + if (!wroteHeader) { + wroteHeader = true; + writeLine(genH2("Classes"), OS); + } + writeRecordSummary(*C, OS); + writeBlankLine(OS); + } + + // Write unions. + wroteHeader = false; + sortRecordInfos(Unions); + for (const auto &U : Unions) { + if (!wroteHeader) { + wroteHeader = true; + writeLine(genH2("Unions"), OS); + } + writeRecordSummary(*U, OS); + writeBlankLine(OS); + } + + // Write enums. + wroteHeader = false; + for (const auto &E : IS->getEnumInfos()) { + if (!E->Namespace.empty() && E->Namespace[0] == I->Name) { + if (!wroteHeader) { + wroteHeader = true; + writeLine(genH2("Enums"), OS); + } + writeEnum(E, OS); + writeBlankLine(OS); + } + } + return true; +} + +bool MDGenerator::writeClassPage(const std::unique_ptr &I) { + SmallString<128> Path; + sys::path::native(ClassPath, Path); + // for (const auto &Namespace : I->Namespace) + // sys::path::append(Path, IS->find(Namespace)->Name); + // std::error_code DirectoryStatus = sys::fs::create_directories(Path); + // if (DirectoryStatus != OK) { + // errs() << "Unable to create class directories.\n"; + // return 1; + // } + sys::path::append(Path, I->Name + ".md"); + std::error_code OutErrorInfo; + raw_fd_ostream OS(Path, OutErrorInfo, sys::fs::F_None); + if (OutErrorInfo != OK) { + errs() << "Error opening class file.\n"; + return false; + } + + writeLine(genH1("class " + I->Name), OS); + writeFileDefinition(I->DefLoc, OS); + writeBlankLine(OS); + + for (const auto &C : I->Description) writeDescription(C, OS); + + bool wroteMethodHeader = false; + for (const auto &F : IS->getFunctionInfos()) { + if (F->ParentUSR == I->Name) { + if (!wroteMethodHeader) { + wroteMethodHeader = true; + writeLine(genH2("Methods"), OS); + } + writeFunction(F, OS); + writeBlankLine(OS); + } + } + + if (!I->Members.empty()) writeLine(genH2("Members"), OS); + for (const auto &M : I->Members) { + writeMemberType(M, OS); + writeBlankLine(OS); + } + + bool wroteEnumHeader = false; + for (const auto &E : IS->getEnumInfos()) { + if (!E->Namespace.empty() && E->Namespace[0] == I->Name) { + if (!wroteEnumHeader) { + wroteEnumHeader = true; + writeLine(genH2("Enums"), OS); + } + writeEnum(E, OS); + writeBlankLine(OS); + } + } + + OS.close(); + return true; +} + +void MDGenerator::writeRecordSummary(const RecordInfo &I, + raw_ostream &OS) { + // TODO: Add brief comment. + switch (I.TagType) { + case TagTypeKind::TTK_Class: + writeLine("class " + I.Name, OS); + break; + case TagTypeKind::TTK_Struct: + writeLine("struct " + I.Name, OS); + break; + case TagTypeKind::TTK_Union: + writeLine("union " + I.Name, OS); + break; + default: + return; + } +} + +void MDGenerator::writeFunction(const std::unique_ptr &I, + raw_ostream &OS) { + std::string Params; + llvm::raw_string_ostream Stream(Params); + for (const auto &N : I->Params) Stream << N->TypeUSR + " " + N->Name << ", "; + writeLine(I->ReturnType->TypeUSR + " " + I->Name + "(" + Params + ")", OS); + for (const auto &C : I->Description) writeDescription(C, OS); + writeFileDefinition(I->DefLoc, OS); +} + +void MDGenerator::writeEnum(const std::unique_ptr &I, + raw_ostream &OS) { + writeLine(genTableCell(I->Name), OS); + writeLine(genTableCell("--"), OS); + for (const auto &M : I->Members) writeLine(genTableCell(M->TypeUSR), OS); + for (const auto &C : I->Description) writeDescription(C, OS); + writeFileDefinition(I->DefLoc, OS); +} + +void MDGenerator::writeType(const std::unique_ptr &N, + raw_ostream &OS) { + writeLine(N->TypeUSR, OS); + for (const auto &C : N->Description) writeDescription(C, OS); +} + +void MDGenerator::writeFieldType(const std::unique_ptr &N, + raw_ostream &OS) { + writeLine(N->TypeUSR + " " + N->Name, OS); + for (const auto &C : N->Description) writeDescription(C, OS); +} + +void MDGenerator::writeMemberType(const std::unique_ptr &N, + raw_ostream &OS) { + // TODO: Add access output. + writeLine(N->TypeUSR + " " + N->Name, OS); + for (const auto &C : N->Description) writeDescription(C, OS); +} + +void MDGenerator::writeDescription(const std::unique_ptr &I, + raw_ostream &OS) {} + +void MDGenerator::writeFileDefinition(const Location &L, raw_ostream &OS) { + writeLine(genItalic("Defined at line " + std::to_string(L.LineNumber) + + " of " + L.Filename + "."), + OS); +} + +// Helper functions + +void MDGenerator::sortRecordInfos( + llvm::SmallVector Records) { + std::sort(Records.begin(), Records.end(), + [](const RecordInfo *A, const RecordInfo *B) { + return A->Name.compare(B->Name) > 0 ? false : true; + }); +} + +// Markdown generation + +void MDGenerator::writeLine(StringRef Text, raw_ostream &OS) { + OS << Text << "\n"; +} + +void MDGenerator::writeBlankLine(raw_ostream &OS) { OS << "\n"; } + +std::string MDGenerator::genItalic(const Twine &Text) { + return "*" + Text.str() + "*"; +} + +std::string MDGenerator::genEmphasis(const Twine &Text) { + return "**" + Text.str() + "**"; +} + +std::string MDGenerator::genStrikethrough(const Twine &Text) { + return "~~" + Text.str() + "~~"; +} + +std::string MDGenerator::genH1(const Twine &Text) { return "# " + Text.str(); } + +std::string MDGenerator::genH2(const Twine &Text) { return "## " + Text.str(); } + +std::string MDGenerator::genH3(const Twine &Text) { + return "### " + Text.str(); +} + +std::string MDGenerator::genH4(const Twine &Text) { + return "#### " + Text.str(); +} + +std::string MDGenerator::genH5(const Twine &Text) { + return "##### " + Text.str(); +} + +std::string MDGenerator::genH6(const Twine &Text) { + return "###### " + Text.str(); +} + +std::string MDGenerator::genTableCell(const Twine &Text) { + return "| " + Text.str() + " |"; +} + +std::string MDGenerator::genUnorderedItem(const Twine &Text, int Indent) { + return ' ' * Indent + "- " + Text.str(); +} + +std::string MDGenerator::genOrderedItem(const Twine &Text, int Pos, + int Indent) { + return ' ' * Indent + Pos + ". " + Text.str(); +} + +std::string MDGenerator::genLink(const Twine &Text, const Twine &Link) { + return "[" + Text.str() + "](" + Link.str() + ")"; +} + +std::string MDGenerator::genLinkWithTooltip(const Twine &Text, + const Twine &Link, + const Twine &Tooltip) { + return "[" + Text.str() + "](" + Link.str() + " \"" + Tooltip.str() + "\")"; +} + +std::string MDGenerator::genInlineCode(const Twine &Text) { + return "`" + Text.str() + "`"; +} + +} // namespace doc +} // namespace clang Index: clang-doc/tool/ClangDocMain.cpp =================================================================== --- clang-doc/tool/ClangDocMain.cpp +++ clang-doc/tool/ClangDocMain.cpp @@ -49,7 +49,7 @@ cl::init("docs"), cl::cat(ClangDocCategory)); static cl::opt Format( - "format", cl::desc("Format for outputted docs (Current options are yaml)."), + "format", cl::desc("Format for outputted docs (Current options are yaml, md)."), cl::init("yaml"), cl::cat(ClangDocCategory)); static cl::opt DumpResult(