blob: 2f523e43989779ba03a0a0e975db61281d74f315 [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 }
Simon Pilgrima3467962018-09-12 12:56:58 +000035 llvm_unreachable("Unknown AccessSpecifier");
Julie Hockettac68cab2018-09-11 15:56:55 +000036}
37
38std::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 Pilgrima3467962018-09-12 12:56:58 +000051 llvm_unreachable("Unknown TagTypeKind");
Julie Hockettac68cab2018-09-11 15:56:55 +000052}
53
54// Markdown generation
55
56std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; }
57
58std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; }
59
60std::string genLink(const Twine &Text, const Twine &Link) {
61 return "[" + Text.str() + "](" + Link.str() + ")";
62}
63
64std::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
77void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; }
78
79void writeNewLine(raw_ostream &OS) { OS << "\n"; }
80
81void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
82 OS << std::string(Num, '#') + " " + Text << "\n";
83}
84
85void 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
91void 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
139void 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
159void 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
181void 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
220void 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.
276class MDGenerator : public Generator {
277public:
278 static const char *Format;
279
280 llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
281};
282
283const char *MDGenerator::Format = "md";
284
285llvm::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
306static 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.
311volatile int MDGeneratorAnchorSource = 0;
312
313} // namespace doc
314} // namespace clang