Julie Hockett | ac68cab | 2018-09-11 15:56:55 +0000 | [diff] [blame] | 1 | //===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #include "Generators.h" |
| 11 | #include "Representation.h" |
| 12 | #include "llvm/ADT/StringRef.h" |
| 13 | #include "llvm/Support/FileSystem.h" |
| 14 | #include "llvm/Support/Path.h" |
| 15 | #include <string> |
| 16 | |
| 17 | using namespace llvm; |
| 18 | |
| 19 | namespace clang { |
| 20 | namespace doc { |
| 21 | |
| 22 | // Enum conversion |
| 23 | |
| 24 | std::string getAccess(AccessSpecifier AS) { |
| 25 | switch (AS) { |
| 26 | case AccessSpecifier::AS_public: |
| 27 | return "public"; |
| 28 | case AccessSpecifier::AS_protected: |
| 29 | return "protected"; |
| 30 | case AccessSpecifier::AS_private: |
| 31 | return "private"; |
| 32 | case AccessSpecifier::AS_none: |
| 33 | return {}; |
| 34 | } |
Simon Pilgrim | a346796 | 2018-09-12 12:56:58 +0000 | [diff] [blame^] | 35 | llvm_unreachable("Unknown AccessSpecifier"); |
Julie Hockett | ac68cab | 2018-09-11 15:56:55 +0000 | [diff] [blame] | 36 | } |
| 37 | |
| 38 | std::string getTagType(TagTypeKind AS) { |
| 39 | switch (AS) { |
| 40 | case TagTypeKind::TTK_Class: |
| 41 | return "class"; |
| 42 | case TagTypeKind::TTK_Union: |
| 43 | return "union"; |
| 44 | case TagTypeKind::TTK_Interface: |
| 45 | return "interface"; |
| 46 | case TagTypeKind::TTK_Struct: |
| 47 | return "struct"; |
| 48 | case TagTypeKind::TTK_Enum: |
| 49 | return "enum"; |
| 50 | } |
Simon Pilgrim | a346796 | 2018-09-12 12:56:58 +0000 | [diff] [blame^] | 51 | llvm_unreachable("Unknown TagTypeKind"); |
Julie Hockett | ac68cab | 2018-09-11 15:56:55 +0000 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | // Markdown generation |
| 55 | |
| 56 | std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; } |
| 57 | |
| 58 | std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; } |
| 59 | |
| 60 | std::string genLink(const Twine &Text, const Twine &Link) { |
| 61 | return "[" + Text.str() + "](" + Link.str() + ")"; |
| 62 | } |
| 63 | |
| 64 | std::string genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) { |
| 65 | std::string Buffer; |
| 66 | llvm::raw_string_ostream Stream(Buffer); |
| 67 | bool First = true; |
| 68 | for (const auto &R : Refs) { |
| 69 | if (!First) |
| 70 | Stream << ", "; |
| 71 | Stream << R.Name; |
| 72 | First = false; |
| 73 | } |
| 74 | return Stream.str(); |
| 75 | } |
| 76 | |
| 77 | void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; } |
| 78 | |
| 79 | void writeNewLine(raw_ostream &OS) { OS << "\n"; } |
| 80 | |
| 81 | void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) { |
| 82 | OS << std::string(Num, '#') + " " + Text << "\n"; |
| 83 | } |
| 84 | |
| 85 | void writeFileDefinition(const Location &L, raw_ostream &OS) { |
| 86 | OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " + |
| 87 | L.Filename) |
| 88 | << "\n"; |
| 89 | } |
| 90 | |
| 91 | void writeDescription(const CommentInfo &I, raw_ostream &OS) { |
| 92 | if (I.Kind == "FullComment") { |
| 93 | for (const auto &Child : I.Children) |
| 94 | writeDescription(*Child, OS); |
| 95 | } else if (I.Kind == "ParagraphComment") { |
| 96 | for (const auto &Child : I.Children) |
| 97 | writeDescription(*Child, OS); |
| 98 | writeNewLine(OS); |
| 99 | } else if (I.Kind == "BlockCommandComment") { |
| 100 | OS << genEmphasis(I.Name); |
| 101 | for (const auto &Child : I.Children) |
| 102 | writeDescription(*Child, OS); |
| 103 | } else if (I.Kind == "InlineCommandComment") { |
| 104 | OS << genEmphasis(I.Name) << " " << I.Text; |
| 105 | } else if (I.Kind == "ParamCommandComment") { |
| 106 | std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; |
| 107 | OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; |
| 108 | } else if (I.Kind == "TParamCommandComment") { |
| 109 | std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; |
| 110 | OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; |
| 111 | } else if (I.Kind == "VerbatimBlockComment") { |
| 112 | for (const auto &Child : I.Children) |
| 113 | writeDescription(*Child, OS); |
| 114 | } else if (I.Kind == "VerbatimBlockLineComment") { |
| 115 | OS << I.Text; |
| 116 | writeNewLine(OS); |
| 117 | } else if (I.Kind == "VerbatimLineComment") { |
| 118 | OS << I.Text; |
| 119 | writeNewLine(OS); |
| 120 | } else if (I.Kind == "HTMLStartTagComment") { |
| 121 | if (I.AttrKeys.size() != I.AttrValues.size()) |
| 122 | return; |
| 123 | std::string Buffer; |
| 124 | llvm::raw_string_ostream Attrs(Buffer); |
| 125 | for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx) |
| 126 | Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\""; |
| 127 | |
| 128 | std::string CloseTag = I.SelfClosing ? "/>" : ">"; |
| 129 | writeLine("<" + I.Name + Attrs.str() + CloseTag, OS); |
| 130 | } else if (I.Kind == "HTMLEndTagComment") { |
| 131 | writeLine("</" + I.Name + ">", OS); |
| 132 | } else if (I.Kind == "TextComment") { |
| 133 | OS << I.Text; |
| 134 | } else { |
| 135 | OS << "Unknown comment kind: " << I.Kind << ".\n"; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) { |
| 140 | if (I.Scoped) |
| 141 | writeLine("| enum class " + I.Name + " |", OS); |
| 142 | else |
| 143 | writeLine("| enum " + I.Name + " |", OS); |
| 144 | writeLine("--", OS); |
| 145 | |
| 146 | std::string Buffer; |
| 147 | llvm::raw_string_ostream Members(Buffer); |
| 148 | if (!I.Members.empty()) |
| 149 | for (const auto &N : I.Members) |
| 150 | Members << "| " << N << " |\n"; |
| 151 | writeLine(Members.str(), OS); |
| 152 | if (I.DefLoc) |
| 153 | writeFileDefinition(I.DefLoc.getValue(), OS); |
| 154 | |
| 155 | for (const auto &C : I.Description) |
| 156 | writeDescription(C, OS); |
| 157 | } |
| 158 | |
| 159 | void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) { |
| 160 | std::string Buffer; |
| 161 | llvm::raw_string_ostream Stream(Buffer); |
| 162 | bool First = true; |
| 163 | for (const auto &N : I.Params) { |
| 164 | if (!First) |
| 165 | Stream << ", "; |
| 166 | Stream << N.Type.Name + " " + N.Name; |
| 167 | First = false; |
| 168 | } |
| 169 | std::string Access = getAccess(I.Access); |
| 170 | if (Access != "") |
| 171 | writeHeader(genItalic(Access) + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS); |
| 172 | else |
| 173 | writeHeader(I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS); |
| 174 | if (I.DefLoc) |
| 175 | writeFileDefinition(I.DefLoc.getValue(), OS); |
| 176 | |
| 177 | for (const auto &C : I.Description) |
| 178 | writeDescription(C, OS); |
| 179 | } |
| 180 | |
| 181 | void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) { |
| 182 | if (I.Name == "") |
| 183 | writeHeader("Global Namespace", 1, OS); |
| 184 | else |
| 185 | writeHeader("namespace " + I.Name, 1, OS); |
| 186 | writeNewLine(OS); |
| 187 | |
| 188 | if (!I.Description.empty()) { |
| 189 | for (const auto &C : I.Description) |
| 190 | writeDescription(C, OS); |
| 191 | writeNewLine(OS); |
| 192 | } |
| 193 | |
| 194 | if (!I.ChildNamespaces.empty()) { |
| 195 | writeHeader("Namespaces", 2, OS); |
| 196 | for (const auto &R : I.ChildNamespaces) |
| 197 | writeLine(R.Name, OS); |
| 198 | writeNewLine(OS); |
| 199 | } |
| 200 | if (!I.ChildRecords.empty()) { |
| 201 | writeHeader("Records", 2, OS); |
| 202 | for (const auto &R : I.ChildRecords) |
| 203 | writeLine(R.Name, OS); |
| 204 | writeNewLine(OS); |
| 205 | } |
| 206 | if (!I.ChildFunctions.empty()) { |
| 207 | writeHeader("Functions", 2, OS); |
| 208 | for (const auto &F : I.ChildFunctions) |
| 209 | genMarkdown(F, OS); |
| 210 | writeNewLine(OS); |
| 211 | } |
| 212 | if (!I.ChildEnums.empty()) { |
| 213 | writeHeader("Enums", 2, OS); |
| 214 | for (const auto &E : I.ChildEnums) |
| 215 | genMarkdown(E, OS); |
| 216 | writeNewLine(OS); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) { |
| 221 | writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS); |
| 222 | if (I.DefLoc) |
| 223 | writeFileDefinition(I.DefLoc.getValue(), OS); |
| 224 | |
| 225 | if (!I.Description.empty()) { |
| 226 | for (const auto &C : I.Description) |
| 227 | writeDescription(C, OS); |
| 228 | writeNewLine(OS); |
| 229 | } |
| 230 | |
| 231 | std::string Parents = genReferenceList(I.Parents); |
| 232 | std::string VParents = genReferenceList(I.VirtualParents); |
| 233 | if (!Parents.empty() || !VParents.empty()) { |
| 234 | if (Parents.empty()) |
| 235 | writeLine("Inherits from " + VParents, OS); |
| 236 | else if (VParents.empty()) |
| 237 | writeLine("Inherits from " + Parents, OS); |
| 238 | else |
| 239 | writeLine("Inherits from " + Parents + ", " + VParents, OS); |
| 240 | writeNewLine(OS); |
| 241 | } |
| 242 | |
| 243 | if (!I.Members.empty()) { |
| 244 | writeHeader("Members", 2, OS); |
| 245 | for (const auto Member : I.Members) { |
| 246 | std::string Access = getAccess(Member.Access); |
| 247 | if (Access != "") |
| 248 | writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS); |
| 249 | else |
| 250 | writeLine(Member.Type.Name + " " + Member.Name, OS); |
| 251 | } |
| 252 | writeNewLine(OS); |
| 253 | } |
| 254 | |
| 255 | if (!I.ChildRecords.empty()) { |
| 256 | writeHeader("Records", 2, OS); |
| 257 | for (const auto &R : I.ChildRecords) |
| 258 | writeLine(R.Name, OS); |
| 259 | writeNewLine(OS); |
| 260 | } |
| 261 | if (!I.ChildFunctions.empty()) { |
| 262 | writeHeader("Functions", 2, OS); |
| 263 | for (const auto &F : I.ChildFunctions) |
| 264 | genMarkdown(F, OS); |
| 265 | writeNewLine(OS); |
| 266 | } |
| 267 | if (!I.ChildEnums.empty()) { |
| 268 | writeHeader("Enums", 2, OS); |
| 269 | for (const auto &E : I.ChildEnums) |
| 270 | genMarkdown(E, OS); |
| 271 | writeNewLine(OS); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /// Generator for Markdown documentation. |
| 276 | class MDGenerator : public Generator { |
| 277 | public: |
| 278 | static const char *Format; |
| 279 | |
| 280 | llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; |
| 281 | }; |
| 282 | |
| 283 | const char *MDGenerator::Format = "md"; |
| 284 | |
| 285 | llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { |
| 286 | switch (I->IT) { |
| 287 | case InfoType::IT_namespace: |
| 288 | genMarkdown(*static_cast<clang::doc::NamespaceInfo *>(I), OS); |
| 289 | break; |
| 290 | case InfoType::IT_record: |
| 291 | genMarkdown(*static_cast<clang::doc::RecordInfo *>(I), OS); |
| 292 | break; |
| 293 | case InfoType::IT_enum: |
| 294 | genMarkdown(*static_cast<clang::doc::EnumInfo *>(I), OS); |
| 295 | break; |
| 296 | case InfoType::IT_function: |
| 297 | genMarkdown(*static_cast<clang::doc::FunctionInfo *>(I), OS); |
| 298 | break; |
| 299 | case InfoType::IT_default: |
| 300 | return llvm::make_error<llvm::StringError>("Unexpected info type.\n", |
| 301 | llvm::inconvertibleErrorCode()); |
| 302 | } |
| 303 | return llvm::Error::success(); |
| 304 | } |
| 305 | |
| 306 | static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format, |
| 307 | "Generator for MD output."); |
| 308 | |
| 309 | // This anchor is used to force the linker to link in the generated object file |
| 310 | // and thus register the generator. |
| 311 | volatile int MDGeneratorAnchorSource = 0; |
| 312 | |
| 313 | } // namespace doc |
| 314 | } // namespace clang |