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