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