MDGenerator.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. //===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "Generators.h"
  9. #include "Representation.h"
  10. #include "llvm/ADT/StringRef.h"
  11. #include "llvm/Support/FileSystem.h"
  12. #include "llvm/Support/Path.h"
  13. #include <string>
  14. using namespace llvm;
  15. namespace clang {
  16. namespace doc {
  17. // Markdown generation
  18. static std::string genItalic(const Twine &Text) {
  19. return "*" + Text.str() + "*";
  20. }
  21. static std::string genEmphasis(const Twine &Text) {
  22. return "**" + Text.str() + "**";
  23. }
  24. static std::string
  25. genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
  26. std::string Buffer;
  27. llvm::raw_string_ostream Stream(Buffer);
  28. for (const auto &R : Refs) {
  29. if (&R != Refs.begin())
  30. Stream << ", ";
  31. Stream << R.Name;
  32. }
  33. return Stream.str();
  34. }
  35. static void writeLine(const Twine &Text, raw_ostream &OS) {
  36. OS << Text << "\n\n";
  37. }
  38. static void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
  39. static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
  40. OS << std::string(Num, '#') + " " + Text << "\n\n";
  41. }
  42. static void writeFileDefinition(const ClangDocContext &CDCtx, const Location &L,
  43. raw_ostream &OS) {
  44. if (!CDCtx.RepositoryUrl) {
  45. OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
  46. << "*";
  47. } else {
  48. OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.LineNumber)
  49. << "](" << StringRef{CDCtx.RepositoryUrl.getValue()}
  50. << llvm::sys::path::relative_path(L.Filename) << "#"
  51. << std::to_string(L.LineNumber) << ")"
  52. << "*";
  53. }
  54. OS << "\n\n";
  55. }
  56. static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
  57. if (I.Kind == "FullComment") {
  58. for (const auto &Child : I.Children)
  59. writeDescription(*Child, OS);
  60. } else if (I.Kind == "ParagraphComment") {
  61. for (const auto &Child : I.Children)
  62. writeDescription(*Child, OS);
  63. writeNewLine(OS);
  64. } else if (I.Kind == "BlockCommandComment") {
  65. OS << genEmphasis(I.Name);
  66. for (const auto &Child : I.Children)
  67. writeDescription(*Child, OS);
  68. } else if (I.Kind == "InlineCommandComment") {
  69. OS << genEmphasis(I.Name) << " " << I.Text;
  70. } else if (I.Kind == "ParamCommandComment") {
  71. std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
  72. OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
  73. } else if (I.Kind == "TParamCommandComment") {
  74. std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
  75. OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
  76. } else if (I.Kind == "VerbatimBlockComment") {
  77. for (const auto &Child : I.Children)
  78. writeDescription(*Child, OS);
  79. } else if (I.Kind == "VerbatimBlockLineComment") {
  80. OS << I.Text;
  81. writeNewLine(OS);
  82. } else if (I.Kind == "VerbatimLineComment") {
  83. OS << I.Text;
  84. writeNewLine(OS);
  85. } else if (I.Kind == "HTMLStartTagComment") {
  86. if (I.AttrKeys.size() != I.AttrValues.size())
  87. return;
  88. std::string Buffer;
  89. llvm::raw_string_ostream Attrs(Buffer);
  90. for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)
  91. Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
  92. std::string CloseTag = I.SelfClosing ? "/>" : ">";
  93. writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
  94. } else if (I.Kind == "HTMLEndTagComment") {
  95. writeLine("</" + I.Name + ">", OS);
  96. } else if (I.Kind == "TextComment") {
  97. OS << I.Text;
  98. } else {
  99. OS << "Unknown comment kind: " << I.Kind << ".\n\n";
  100. }
  101. }
  102. static void writeNameLink(const StringRef &CurrentPath, const Reference &R,
  103. llvm::raw_ostream &OS) {
  104. llvm::SmallString<64> Path = R.getRelativeFilePath(CurrentPath);
  105. // Paths in Markdown use POSIX separators.
  106. llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
  107. llvm::sys::path::append(Path, llvm::sys::path::Style::posix,
  108. R.getFileBaseName() + ".md");
  109. OS << "[" << R.Name << "](" << Path << ")";
  110. }
  111. static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
  112. llvm::raw_ostream &OS) {
  113. if (I.Scoped)
  114. writeLine("| enum class " + I.Name + " |", OS);
  115. else
  116. writeLine("| enum " + I.Name + " |", OS);
  117. writeLine("--", OS);
  118. std::string Buffer;
  119. llvm::raw_string_ostream Members(Buffer);
  120. if (!I.Members.empty())
  121. for (const auto &N : I.Members)
  122. Members << "| " << N << " |\n";
  123. writeLine(Members.str(), OS);
  124. if (I.DefLoc)
  125. writeFileDefinition(CDCtx, I.DefLoc.getValue(), OS);
  126. for (const auto &C : I.Description)
  127. writeDescription(C, OS);
  128. }
  129. static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,
  130. llvm::raw_ostream &OS) {
  131. std::string Buffer;
  132. llvm::raw_string_ostream Stream(Buffer);
  133. bool First = true;
  134. for (const auto &N : I.Params) {
  135. if (!First)
  136. Stream << ", ";
  137. Stream << N.Type.Name + " " + N.Name;
  138. First = false;
  139. }
  140. writeHeader(I.Name, 3, OS);
  141. std::string Access = getAccessSpelling(I.Access).str();
  142. if (Access != "")
  143. writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
  144. "(" + Stream.str() + ")"),
  145. OS);
  146. else
  147. writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
  148. Stream.str() + ")"),
  149. OS);
  150. if (I.DefLoc)
  151. writeFileDefinition(CDCtx, I.DefLoc.getValue(), OS);
  152. for (const auto &C : I.Description)
  153. writeDescription(C, OS);
  154. }
  155. static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
  156. llvm::raw_ostream &OS) {
  157. if (I.Name == "")
  158. writeHeader("Global Namespace", 1, OS);
  159. else
  160. writeHeader("namespace " + I.Name, 1, OS);
  161. writeNewLine(OS);
  162. if (!I.Description.empty()) {
  163. for (const auto &C : I.Description)
  164. writeDescription(C, OS);
  165. writeNewLine(OS);
  166. }
  167. llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
  168. if (!I.ChildNamespaces.empty()) {
  169. writeHeader("Namespaces", 2, OS);
  170. for (const auto &R : I.ChildNamespaces) {
  171. OS << "* ";
  172. writeNameLink(BasePath, R, OS);
  173. OS << "\n";
  174. }
  175. writeNewLine(OS);
  176. }
  177. if (!I.ChildRecords.empty()) {
  178. writeHeader("Records", 2, OS);
  179. for (const auto &R : I.ChildRecords) {
  180. OS << "* ";
  181. writeNameLink(BasePath, R, OS);
  182. OS << "\n";
  183. }
  184. writeNewLine(OS);
  185. }
  186. if (!I.ChildFunctions.empty()) {
  187. writeHeader("Functions", 2, OS);
  188. for (const auto &F : I.ChildFunctions)
  189. genMarkdown(CDCtx, F, OS);
  190. writeNewLine(OS);
  191. }
  192. if (!I.ChildEnums.empty()) {
  193. writeHeader("Enums", 2, OS);
  194. for (const auto &E : I.ChildEnums)
  195. genMarkdown(CDCtx, E, OS);
  196. writeNewLine(OS);
  197. }
  198. }
  199. static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,
  200. llvm::raw_ostream &OS) {
  201. writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
  202. if (I.DefLoc)
  203. writeFileDefinition(CDCtx, I.DefLoc.getValue(), OS);
  204. if (!I.Description.empty()) {
  205. for (const auto &C : I.Description)
  206. writeDescription(C, OS);
  207. writeNewLine(OS);
  208. }
  209. std::string Parents = genReferenceList(I.Parents);
  210. std::string VParents = genReferenceList(I.VirtualParents);
  211. if (!Parents.empty() || !VParents.empty()) {
  212. if (Parents.empty())
  213. writeLine("Inherits from " + VParents, OS);
  214. else if (VParents.empty())
  215. writeLine("Inherits from " + Parents, OS);
  216. else
  217. writeLine("Inherits from " + Parents + ", " + VParents, OS);
  218. writeNewLine(OS);
  219. }
  220. if (!I.Members.empty()) {
  221. writeHeader("Members", 2, OS);
  222. for (const auto &Member : I.Members) {
  223. std::string Access = getAccessSpelling(Member.Access).str();
  224. if (Access != "")
  225. writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
  226. else
  227. writeLine(Member.Type.Name + " " + Member.Name, OS);
  228. }
  229. writeNewLine(OS);
  230. }
  231. if (!I.ChildRecords.empty()) {
  232. writeHeader("Records", 2, OS);
  233. for (const auto &R : I.ChildRecords)
  234. writeLine(R.Name, OS);
  235. writeNewLine(OS);
  236. }
  237. if (!I.ChildFunctions.empty()) {
  238. writeHeader("Functions", 2, OS);
  239. for (const auto &F : I.ChildFunctions)
  240. genMarkdown(CDCtx, F, OS);
  241. writeNewLine(OS);
  242. }
  243. if (!I.ChildEnums.empty()) {
  244. writeHeader("Enums", 2, OS);
  245. for (const auto &E : I.ChildEnums)
  246. genMarkdown(CDCtx, E, OS);
  247. writeNewLine(OS);
  248. }
  249. }
  250. static void serializeReference(llvm::raw_fd_ostream &OS, Index &I, int Level) {
  251. // Write out the heading level starting at ##
  252. OS << "##" << std::string(Level, '#') << " ";
  253. writeNameLink("", I, OS);
  254. OS << "\n";
  255. }
  256. static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
  257. std::error_code FileErr;
  258. llvm::SmallString<128> FilePath;
  259. llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
  260. llvm::sys::path::append(FilePath, "all_files.md");
  261. llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
  262. if (FileErr)
  263. return llvm::createStringError(llvm::inconvertibleErrorCode(),
  264. "error creating index file: " +
  265. FileErr.message());
  266. CDCtx.Idx.sort();
  267. OS << "# All Files";
  268. if (!CDCtx.ProjectName.empty())
  269. OS << " for " << CDCtx.ProjectName;
  270. OS << "\n\n";
  271. for (auto C : CDCtx.Idx.Children)
  272. serializeReference(OS, C, 0);
  273. return llvm::Error::success();
  274. }
  275. static llvm::Error genIndex(ClangDocContext &CDCtx) {
  276. std::error_code FileErr;
  277. llvm::SmallString<128> FilePath;
  278. llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
  279. llvm::sys::path::append(FilePath, "index.md");
  280. llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
  281. if (FileErr)
  282. return llvm::createStringError(llvm::inconvertibleErrorCode(),
  283. "error creating index file: " +
  284. FileErr.message());
  285. CDCtx.Idx.sort();
  286. OS << "# " << CDCtx.ProjectName << " C/C++ Reference\n\n";
  287. for (auto C : CDCtx.Idx.Children) {
  288. if (!C.Children.empty()) {
  289. const char *Type;
  290. switch (C.RefType) {
  291. case InfoType::IT_namespace:
  292. Type = "Namespace";
  293. break;
  294. case InfoType::IT_record:
  295. Type = "Type";
  296. break;
  297. case InfoType::IT_enum:
  298. Type = "Enum";
  299. break;
  300. case InfoType::IT_function:
  301. Type = "Function";
  302. break;
  303. case InfoType::IT_default:
  304. Type = "Other";
  305. }
  306. OS << "* " << Type << ": [" << C.Name << "](";
  307. if (!C.Path.empty())
  308. OS << C.Path << "/";
  309. OS << C.Name << ")\n";
  310. }
  311. }
  312. return llvm::Error::success();
  313. }
  314. /// Generator for Markdown documentation.
  315. class MDGenerator : public Generator {
  316. public:
  317. static const char *Format;
  318. llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
  319. const ClangDocContext &CDCtx) override;
  320. llvm::Error createResources(ClangDocContext &CDCtx) override;
  321. };
  322. const char *MDGenerator::Format = "md";
  323. llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
  324. const ClangDocContext &CDCtx) {
  325. switch (I->IT) {
  326. case InfoType::IT_namespace:
  327. genMarkdown(CDCtx, *static_cast<clang::doc::NamespaceInfo *>(I), OS);
  328. break;
  329. case InfoType::IT_record:
  330. genMarkdown(CDCtx, *static_cast<clang::doc::RecordInfo *>(I), OS);
  331. break;
  332. case InfoType::IT_enum:
  333. genMarkdown(CDCtx, *static_cast<clang::doc::EnumInfo *>(I), OS);
  334. break;
  335. case InfoType::IT_function:
  336. genMarkdown(CDCtx, *static_cast<clang::doc::FunctionInfo *>(I), OS);
  337. break;
  338. case InfoType::IT_default:
  339. return createStringError(llvm::inconvertibleErrorCode(),
  340. "unexpected InfoType");
  341. }
  342. return llvm::Error::success();
  343. }
  344. llvm::Error MDGenerator::createResources(ClangDocContext &CDCtx) {
  345. // Write an all_files.md
  346. auto Err = serializeIndex(CDCtx);
  347. if (Err)
  348. return Err;
  349. // Generate the index page.
  350. Err = genIndex(CDCtx);
  351. if (Err)
  352. return Err;
  353. return llvm::Error::success();
  354. }
  355. static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,
  356. "Generator for MD output.");
  357. // This anchor is used to force the linker to link in the generated object
  358. // file and thus register the generator.
  359. volatile int MDGeneratorAnchorSource = 0;
  360. } // namespace doc
  361. } // namespace clang