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 
getAccess(AccessSpecifier AS)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   }
35   llvm_unreachable("Unknown AccessSpecifier");
36 }
37 
getTagType(TagTypeKind AS)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   }
51   llvm_unreachable("Unknown TagTypeKind");
52 }
53 
54 // Markdown generation
55 
genItalic(const Twine & Text)56 std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; }
57 
genEmphasis(const Twine & Text)58 std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; }
59 
genLink(const Twine & Text,const Twine & Link)60 std::string genLink(const Twine &Text, const Twine &Link) {
61   return "[" + Text.str() + "](" + Link.str() + ")";
62 }
63 
genReferenceList(const llvm::SmallVectorImpl<Reference> & Refs)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 
writeLine(const Twine & Text,raw_ostream & OS)77 void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n\n"; }
78 
writeNewLine(raw_ostream & OS)79 void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
80 
writeHeader(const Twine & Text,unsigned int Num,raw_ostream & OS)81 void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
82   OS << std::string(Num, '#') + " " + Text << "\n\n";
83 }
84 
writeFileDefinition(const Location & L,raw_ostream & OS)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\n";
89 }
90 
writeDescription(const CommentInfo & I,raw_ostream & OS)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\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\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\n";
136   }
137 }
138 
genMarkdown(const EnumInfo & I,llvm::raw_ostream & OS)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 
genMarkdown(const FunctionInfo & I,llvm::raw_ostream & OS)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   writeHeader(I.Name, 3, OS);
170   std::string Access = getAccess(I.Access);
171   if (Access != "")
172     writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
173                         "(" + Stream.str() + ")"),
174               OS);
175   else
176     writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
177                         Stream.str() + ")"),
178               OS);
179   if (I.DefLoc)
180     writeFileDefinition(I.DefLoc.getValue(), OS);
181 
182   for (const auto &C : I.Description)
183     writeDescription(C, OS);
184 }
185 
genMarkdown(const NamespaceInfo & I,llvm::raw_ostream & OS)186 void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) {
187   if (I.Name == "")
188     writeHeader("Global Namespace", 1, OS);
189   else
190     writeHeader("namespace " + I.Name, 1, OS);
191   writeNewLine(OS);
192 
193   if (!I.Description.empty()) {
194     for (const auto &C : I.Description)
195       writeDescription(C, OS);
196     writeNewLine(OS);
197   }
198 
199   if (!I.ChildNamespaces.empty()) {
200     writeHeader("Namespaces", 2, OS);
201     for (const auto &R : I.ChildNamespaces)
202       writeLine(R.Name, OS);
203     writeNewLine(OS);
204   }
205   if (!I.ChildRecords.empty()) {
206     writeHeader("Records", 2, OS);
207     for (const auto &R : I.ChildRecords)
208       writeLine(R.Name, OS);
209     writeNewLine(OS);
210   }
211   if (!I.ChildFunctions.empty()) {
212     writeHeader("Functions", 2, OS);
213     for (const auto &F : I.ChildFunctions)
214       genMarkdown(F, OS);
215     writeNewLine(OS);
216   }
217   if (!I.ChildEnums.empty()) {
218     writeHeader("Enums", 2, OS);
219     for (const auto &E : I.ChildEnums)
220       genMarkdown(E, OS);
221     writeNewLine(OS);
222   }
223 }
224 
genMarkdown(const RecordInfo & I,llvm::raw_ostream & OS)225 void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) {
226   writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
227   if (I.DefLoc)
228     writeFileDefinition(I.DefLoc.getValue(), OS);
229 
230   if (!I.Description.empty()) {
231     for (const auto &C : I.Description)
232       writeDescription(C, OS);
233     writeNewLine(OS);
234   }
235 
236   std::string Parents = genReferenceList(I.Parents);
237   std::string VParents = genReferenceList(I.VirtualParents);
238   if (!Parents.empty() || !VParents.empty()) {
239     if (Parents.empty())
240       writeLine("Inherits from " + VParents, OS);
241     else if (VParents.empty())
242       writeLine("Inherits from " + Parents, OS);
243     else
244       writeLine("Inherits from " + Parents + ", " + VParents, OS);
245     writeNewLine(OS);
246   }
247 
248   if (!I.Members.empty()) {
249     writeHeader("Members", 2, OS);
250     for (const auto Member : I.Members) {
251       std::string Access = getAccess(Member.Access);
252       if (Access != "")
253         writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
254       else
255         writeLine(Member.Type.Name + " " + Member.Name, OS);
256     }
257     writeNewLine(OS);
258   }
259 
260   if (!I.ChildRecords.empty()) {
261     writeHeader("Records", 2, OS);
262     for (const auto &R : I.ChildRecords)
263       writeLine(R.Name, OS);
264     writeNewLine(OS);
265   }
266   if (!I.ChildFunctions.empty()) {
267     writeHeader("Functions", 2, OS);
268     for (const auto &F : I.ChildFunctions)
269       genMarkdown(F, OS);
270     writeNewLine(OS);
271   }
272   if (!I.ChildEnums.empty()) {
273     writeHeader("Enums", 2, OS);
274     for (const auto &E : I.ChildEnums)
275       genMarkdown(E, OS);
276     writeNewLine(OS);
277   }
278 }
279 
280 /// Generator for Markdown documentation.
281 class MDGenerator : public Generator {
282 public:
283   static const char *Format;
284 
285   llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
286 };
287 
288 const char *MDGenerator::Format = "md";
289 
generateDocForInfo(Info * I,llvm::raw_ostream & OS)290 llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
291   switch (I->IT) {
292   case InfoType::IT_namespace:
293     genMarkdown(*static_cast<clang::doc::NamespaceInfo *>(I), OS);
294     break;
295   case InfoType::IT_record:
296     genMarkdown(*static_cast<clang::doc::RecordInfo *>(I), OS);
297     break;
298   case InfoType::IT_enum:
299     genMarkdown(*static_cast<clang::doc::EnumInfo *>(I), OS);
300     break;
301   case InfoType::IT_function:
302     genMarkdown(*static_cast<clang::doc::FunctionInfo *>(I), OS);
303     break;
304   case InfoType::IT_default:
305     return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
306                                                llvm::inconvertibleErrorCode());
307   }
308   return llvm::Error::success();
309 }
310 
311 static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,
312                                               "Generator for MD output.");
313 
314 // This anchor is used to force the linker to link in the generated object file
315 // and thus register the generator.
316 volatile int MDGeneratorAnchorSource = 0;
317 
318 } // namespace doc
319 } // namespace clang
320