Bump version to 19.1.0git
[llvm-project.git] / clang-tools-extra / clang-doc / MDGenerator.cpp
blob795eb4b904e3efba7b3716e40cfa3afd9ac0fb0a
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 //===----------------------------------------------------------------------===//
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>
16 using namespace llvm;
18 namespace clang {
19 namespace doc {
21 // Markdown generation
23 static std::string genItalic(const Twine &Text) {
24 return "*" + Text.str() + "*";
27 static std::string genEmphasis(const Twine &Text) {
28 return "**" + Text.str() + "**";
31 static std::string
32 genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
33 std::string Buffer;
34 llvm::raw_string_ostream Stream(Buffer);
35 for (const auto &R : Refs) {
36 if (&R != Refs.begin())
37 Stream << ", ";
38 Stream << R.Name;
40 return Stream.str();
43 static void writeLine(const Twine &Text, raw_ostream &OS) {
44 OS << Text << "\n\n";
47 static void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
49 static void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
50 OS << std::string(Num, '#') + " " + Text << "\n\n";
53 static void writeFileDefinition(const ClangDocContext &CDCtx, const Location &L,
54 raw_ostream &OS) {
56 if (!CDCtx.RepositoryUrl) {
57 OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
58 << "*";
59 } else {
60 OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.LineNumber)
61 << "](" << StringRef{*CDCtx.RepositoryUrl}
62 << llvm::sys::path::relative_path(L.Filename) << "#"
63 << std::to_string(L.LineNumber) << ")"
64 << "*";
66 OS << "\n\n";
69 static void writeDescription(const CommentInfo &I, raw_ostream &OS) {
70 if (I.Kind == "FullComment") {
71 for (const auto &Child : I.Children)
72 writeDescription(*Child, OS);
73 } else if (I.Kind == "ParagraphComment") {
74 for (const auto &Child : I.Children)
75 writeDescription(*Child, OS);
76 writeNewLine(OS);
77 } else if (I.Kind == "BlockCommandComment") {
78 OS << genEmphasis(I.Name);
79 for (const auto &Child : I.Children)
80 writeDescription(*Child, OS);
81 } else if (I.Kind == "InlineCommandComment") {
82 OS << genEmphasis(I.Name) << " " << I.Text;
83 } else if (I.Kind == "ParamCommandComment") {
84 std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
85 OS << genEmphasis(I.ParamName) << I.Text << Direction;
86 for (const auto &Child : I.Children)
87 writeDescription(*Child, OS);
88 } else if (I.Kind == "TParamCommandComment") {
89 std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
90 OS << genEmphasis(I.ParamName) << I.Text << Direction;
91 for (const auto &Child : I.Children)
92 writeDescription(*Child, OS);
93 } else if (I.Kind == "VerbatimBlockComment") {
94 for (const auto &Child : I.Children)
95 writeDescription(*Child, OS);
96 } else if (I.Kind == "VerbatimBlockLineComment") {
97 OS << I.Text;
98 writeNewLine(OS);
99 } else if (I.Kind == "VerbatimLineComment") {
100 OS << I.Text;
101 writeNewLine(OS);
102 } else if (I.Kind == "HTMLStartTagComment") {
103 if (I.AttrKeys.size() != I.AttrValues.size())
104 return;
105 std::string Buffer;
106 llvm::raw_string_ostream Attrs(Buffer);
107 for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)
108 Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
110 std::string CloseTag = I.SelfClosing ? "/>" : ">";
111 writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
112 } else if (I.Kind == "HTMLEndTagComment") {
113 writeLine("</" + I.Name + ">", OS);
114 } else if (I.Kind == "TextComment") {
115 OS << I.Text;
116 } else {
117 OS << "Unknown comment kind: " << I.Kind << ".\n\n";
121 static void writeNameLink(const StringRef &CurrentPath, const Reference &R,
122 llvm::raw_ostream &OS) {
123 llvm::SmallString<64> Path = R.getRelativeFilePath(CurrentPath);
124 // Paths in Markdown use POSIX separators.
125 llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
126 llvm::sys::path::append(Path, llvm::sys::path::Style::posix,
127 R.getFileBaseName() + ".md");
128 OS << "[" << R.Name << "](" << Path << ")";
131 static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
132 llvm::raw_ostream &OS) {
133 if (I.Scoped)
134 writeLine("| enum class " + I.Name + " |", OS);
135 else
136 writeLine("| enum " + I.Name + " |", OS);
137 writeLine("--", OS);
139 std::string Buffer;
140 llvm::raw_string_ostream Members(Buffer);
141 if (!I.Members.empty())
142 for (const auto &N : I.Members)
143 Members << "| " << N.Name << " |\n";
144 writeLine(Members.str(), OS);
145 if (I.DefLoc)
146 writeFileDefinition(CDCtx, *I.DefLoc, OS);
148 for (const auto &C : I.Description)
149 writeDescription(C, OS);
152 static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,
153 llvm::raw_ostream &OS) {
154 std::string Buffer;
155 llvm::raw_string_ostream Stream(Buffer);
156 bool First = true;
157 for (const auto &N : I.Params) {
158 if (!First)
159 Stream << ", ";
160 Stream << N.Type.Name + " " + N.Name;
161 First = false;
163 writeHeader(I.Name, 3, OS);
164 std::string Access = getAccessSpelling(I.Access).str();
165 if (Access != "")
166 writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
167 "(" + Stream.str() + ")"),
168 OS);
169 else
170 writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
171 Stream.str() + ")"),
172 OS);
173 if (I.DefLoc)
174 writeFileDefinition(CDCtx, *I.DefLoc, OS);
176 for (const auto &C : I.Description)
177 writeDescription(C, OS);
180 static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
181 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);
188 if (!I.Description.empty()) {
189 for (const auto &C : I.Description)
190 writeDescription(C, OS);
191 writeNewLine(OS);
194 llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
196 if (!I.Children.Namespaces.empty()) {
197 writeHeader("Namespaces", 2, OS);
198 for (const auto &R : I.Children.Namespaces) {
199 OS << "* ";
200 writeNameLink(BasePath, R, OS);
201 OS << "\n";
203 writeNewLine(OS);
206 if (!I.Children.Records.empty()) {
207 writeHeader("Records", 2, OS);
208 for (const auto &R : I.Children.Records) {
209 OS << "* ";
210 writeNameLink(BasePath, R, OS);
211 OS << "\n";
213 writeNewLine(OS);
216 if (!I.Children.Functions.empty()) {
217 writeHeader("Functions", 2, OS);
218 for (const auto &F : I.Children.Functions)
219 genMarkdown(CDCtx, F, OS);
220 writeNewLine(OS);
222 if (!I.Children.Enums.empty()) {
223 writeHeader("Enums", 2, OS);
224 for (const auto &E : I.Children.Enums)
225 genMarkdown(CDCtx, E, OS);
226 writeNewLine(OS);
230 static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,
231 llvm::raw_ostream &OS) {
232 writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
233 if (I.DefLoc)
234 writeFileDefinition(CDCtx, *I.DefLoc, OS);
236 if (!I.Description.empty()) {
237 for (const auto &C : I.Description)
238 writeDescription(C, OS);
239 writeNewLine(OS);
242 std::string Parents = genReferenceList(I.Parents);
243 std::string VParents = genReferenceList(I.VirtualParents);
244 if (!Parents.empty() || !VParents.empty()) {
245 if (Parents.empty())
246 writeLine("Inherits from " + VParents, OS);
247 else if (VParents.empty())
248 writeLine("Inherits from " + Parents, OS);
249 else
250 writeLine("Inherits from " + Parents + ", " + VParents, OS);
251 writeNewLine(OS);
254 if (!I.Members.empty()) {
255 writeHeader("Members", 2, OS);
256 for (const auto &Member : I.Members) {
257 std::string Access = getAccessSpelling(Member.Access).str();
258 if (Access != "")
259 writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
260 else
261 writeLine(Member.Type.Name + " " + Member.Name, OS);
263 writeNewLine(OS);
266 if (!I.Children.Records.empty()) {
267 writeHeader("Records", 2, OS);
268 for (const auto &R : I.Children.Records)
269 writeLine(R.Name, OS);
270 writeNewLine(OS);
272 if (!I.Children.Functions.empty()) {
273 writeHeader("Functions", 2, OS);
274 for (const auto &F : I.Children.Functions)
275 genMarkdown(CDCtx, F, OS);
276 writeNewLine(OS);
278 if (!I.Children.Enums.empty()) {
279 writeHeader("Enums", 2, OS);
280 for (const auto &E : I.Children.Enums)
281 genMarkdown(CDCtx, E, OS);
282 writeNewLine(OS);
286 static void genMarkdown(const ClangDocContext &CDCtx, const TypedefInfo &I,
287 llvm::raw_ostream &OS) {
288 // TODO support typedefs in markdown.
291 static void serializeReference(llvm::raw_fd_ostream &OS, Index &I, int Level) {
292 // Write out the heading level starting at ##
293 OS << "##" << std::string(Level, '#') << " ";
294 writeNameLink("", I, OS);
295 OS << "\n";
298 static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
299 std::error_code FileErr;
300 llvm::SmallString<128> FilePath;
301 llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
302 llvm::sys::path::append(FilePath, "all_files.md");
303 llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
304 if (FileErr)
305 return llvm::createStringError(llvm::inconvertibleErrorCode(),
306 "error creating index file: " +
307 FileErr.message());
309 CDCtx.Idx.sort();
310 OS << "# All Files";
311 if (!CDCtx.ProjectName.empty())
312 OS << " for " << CDCtx.ProjectName;
313 OS << "\n\n";
315 for (auto C : CDCtx.Idx.Children)
316 serializeReference(OS, C, 0);
318 return llvm::Error::success();
321 static llvm::Error genIndex(ClangDocContext &CDCtx) {
322 std::error_code FileErr;
323 llvm::SmallString<128> FilePath;
324 llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
325 llvm::sys::path::append(FilePath, "index.md");
326 llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
327 if (FileErr)
328 return llvm::createStringError(llvm::inconvertibleErrorCode(),
329 "error creating index file: " +
330 FileErr.message());
331 CDCtx.Idx.sort();
332 OS << "# " << CDCtx.ProjectName << " C/C++ Reference\n\n";
333 for (auto C : CDCtx.Idx.Children) {
334 if (!C.Children.empty()) {
335 const char *Type;
336 switch (C.RefType) {
337 case InfoType::IT_namespace:
338 Type = "Namespace";
339 break;
340 case InfoType::IT_record:
341 Type = "Type";
342 break;
343 case InfoType::IT_enum:
344 Type = "Enum";
345 break;
346 case InfoType::IT_function:
347 Type = "Function";
348 break;
349 case InfoType::IT_typedef:
350 Type = "Typedef";
351 break;
352 case InfoType::IT_default:
353 Type = "Other";
355 OS << "* " << Type << ": [" << C.Name << "](";
356 if (!C.Path.empty())
357 OS << C.Path << "/";
358 OS << C.Name << ")\n";
361 return llvm::Error::success();
364 /// Generator for Markdown documentation.
365 class MDGenerator : public Generator {
366 public:
367 static const char *Format;
369 llvm::Error generateDocs(StringRef RootDir,
370 llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
371 const ClangDocContext &CDCtx) override;
372 llvm::Error createResources(ClangDocContext &CDCtx) override;
373 llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
374 const ClangDocContext &CDCtx) override;
377 const char *MDGenerator::Format = "md";
379 llvm::Error
380 MDGenerator::generateDocs(StringRef RootDir,
381 llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
382 const ClangDocContext &CDCtx) {
383 // Track which directories we already tried to create.
384 llvm::StringSet<> CreatedDirs;
386 // Collect all output by file name and create the necessary directories.
387 llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
388 for (const auto &Group : Infos) {
389 doc::Info *Info = Group.getValue().get();
391 llvm::SmallString<128> Path;
392 llvm::sys::path::native(RootDir, Path);
393 llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
394 if (!CreatedDirs.contains(Path)) {
395 if (std::error_code Err = llvm::sys::fs::create_directories(Path);
396 Err != std::error_code()) {
397 return llvm::createStringError(Err, "Failed to create directory '%s'.",
398 Path.c_str());
400 CreatedDirs.insert(Path);
403 llvm::sys::path::append(Path, Info->getFileBaseName() + ".md");
404 FileToInfos[Path].push_back(Info);
407 for (const auto &Group : FileToInfos) {
408 std::error_code FileErr;
409 llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
410 llvm::sys::fs::OF_None);
411 if (FileErr) {
412 return llvm::createStringError(FileErr, "Error opening file '%s'",
413 Group.getKey().str().c_str());
416 for (const auto &Info : Group.getValue()) {
417 if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
418 return Err;
423 return llvm::Error::success();
426 llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
427 const ClangDocContext &CDCtx) {
428 switch (I->IT) {
429 case InfoType::IT_namespace:
430 genMarkdown(CDCtx, *static_cast<clang::doc::NamespaceInfo *>(I), OS);
431 break;
432 case InfoType::IT_record:
433 genMarkdown(CDCtx, *static_cast<clang::doc::RecordInfo *>(I), OS);
434 break;
435 case InfoType::IT_enum:
436 genMarkdown(CDCtx, *static_cast<clang::doc::EnumInfo *>(I), OS);
437 break;
438 case InfoType::IT_function:
439 genMarkdown(CDCtx, *static_cast<clang::doc::FunctionInfo *>(I), OS);
440 break;
441 case InfoType::IT_typedef:
442 genMarkdown(CDCtx, *static_cast<clang::doc::TypedefInfo *>(I), OS);
443 break;
444 case InfoType::IT_default:
445 return createStringError(llvm::inconvertibleErrorCode(),
446 "unexpected InfoType");
448 return llvm::Error::success();
451 llvm::Error MDGenerator::createResources(ClangDocContext &CDCtx) {
452 // Write an all_files.md
453 auto Err = serializeIndex(CDCtx);
454 if (Err)
455 return Err;
457 // Generate the index page.
458 Err = genIndex(CDCtx);
459 if (Err)
460 return Err;
462 return llvm::Error::success();
465 static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,
466 "Generator for MD output.");
468 // This anchor is used to force the linker to link in the generated object
469 // file and thus register the generator.
470 volatile int MDGeneratorAnchorSource = 0;
472 } // namespace doc
473 } // namespace clang