1 //===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===//
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
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"
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() + "**";
32 genReferenceList(const llvm::SmallVectorImpl
<Reference
> &Refs
) {
34 llvm::raw_string_ostream
Stream(Buffer
);
35 for (const auto &R
: Refs
) {
36 if (&R
!= Refs
.begin())
43 static void writeLine(const Twine
&Text
, raw_ostream
&OS
) {
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
,
56 if (!CDCtx
.RepositoryUrl
) {
57 OS
<< "*Defined at " << L
.Filename
<< "#" << std::to_string(L
.LineNumber
)
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
) << ")"
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
);
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
<< "\n\n";
86 } else if (I
.Kind
== "TParamCommandComment") {
87 std::string Direction
= I
.Explicit
? (" " + I
.Direction
).str() : "";
88 OS
<< genEmphasis(I
.ParamName
) << I
.Text
<< Direction
<< "\n\n";
89 } else if (I
.Kind
== "VerbatimBlockComment") {
90 for (const auto &Child
: I
.Children
)
91 writeDescription(*Child
, OS
);
92 } else if (I
.Kind
== "VerbatimBlockLineComment") {
95 } else if (I
.Kind
== "VerbatimLineComment") {
98 } else if (I
.Kind
== "HTMLStartTagComment") {
99 if (I
.AttrKeys
.size() != I
.AttrValues
.size())
102 llvm::raw_string_ostream
Attrs(Buffer
);
103 for (unsigned Idx
= 0; Idx
< I
.AttrKeys
.size(); ++Idx
)
104 Attrs
<< " \"" << I
.AttrKeys
[Idx
] << "=" << I
.AttrValues
[Idx
] << "\"";
106 std::string CloseTag
= I
.SelfClosing
? "/>" : ">";
107 writeLine("<" + I
.Name
+ Attrs
.str() + CloseTag
, OS
);
108 } else if (I
.Kind
== "HTMLEndTagComment") {
109 writeLine("</" + I
.Name
+ ">", OS
);
110 } else if (I
.Kind
== "TextComment") {
113 OS
<< "Unknown comment kind: " << I
.Kind
<< ".\n\n";
117 static void writeNameLink(const StringRef
&CurrentPath
, const Reference
&R
,
118 llvm::raw_ostream
&OS
) {
119 llvm::SmallString
<64> Path
= R
.getRelativeFilePath(CurrentPath
);
120 // Paths in Markdown use POSIX separators.
121 llvm::sys::path::native(Path
, llvm::sys::path::Style::posix
);
122 llvm::sys::path::append(Path
, llvm::sys::path::Style::posix
,
123 R
.getFileBaseName() + ".md");
124 OS
<< "[" << R
.Name
<< "](" << Path
<< ")";
127 static void genMarkdown(const ClangDocContext
&CDCtx
, const EnumInfo
&I
,
128 llvm::raw_ostream
&OS
) {
130 writeLine("| enum class " + I
.Name
+ " |", OS
);
132 writeLine("| enum " + I
.Name
+ " |", OS
);
136 llvm::raw_string_ostream
Members(Buffer
);
137 if (!I
.Members
.empty())
138 for (const auto &N
: I
.Members
)
139 Members
<< "| " << N
<< " |\n";
140 writeLine(Members
.str(), OS
);
142 writeFileDefinition(CDCtx
, *I
.DefLoc
, OS
);
144 for (const auto &C
: I
.Description
)
145 writeDescription(C
, OS
);
148 static void genMarkdown(const ClangDocContext
&CDCtx
, const FunctionInfo
&I
,
149 llvm::raw_ostream
&OS
) {
151 llvm::raw_string_ostream
Stream(Buffer
);
153 for (const auto &N
: I
.Params
) {
156 Stream
<< N
.Type
.Name
+ " " + N
.Name
;
159 writeHeader(I
.Name
, 3, OS
);
160 std::string Access
= getAccessSpelling(I
.Access
).str();
162 writeLine(genItalic(Access
+ " " + I
.ReturnType
.Type
.Name
+ " " + I
.Name
+
163 "(" + Stream
.str() + ")"),
166 writeLine(genItalic(I
.ReturnType
.Type
.Name
+ " " + I
.Name
+ "(" +
170 writeFileDefinition(CDCtx
, *I
.DefLoc
, OS
);
172 for (const auto &C
: I
.Description
)
173 writeDescription(C
, OS
);
176 static void genMarkdown(const ClangDocContext
&CDCtx
, const NamespaceInfo
&I
,
177 llvm::raw_ostream
&OS
) {
179 writeHeader("Global Namespace", 1, OS
);
181 writeHeader("namespace " + I
.Name
, 1, OS
);
184 if (!I
.Description
.empty()) {
185 for (const auto &C
: I
.Description
)
186 writeDescription(C
, OS
);
190 llvm::SmallString
<64> BasePath
= I
.getRelativeFilePath("");
192 if (!I
.ChildNamespaces
.empty()) {
193 writeHeader("Namespaces", 2, OS
);
194 for (const auto &R
: I
.ChildNamespaces
) {
196 writeNameLink(BasePath
, R
, OS
);
202 if (!I
.ChildRecords
.empty()) {
203 writeHeader("Records", 2, OS
);
204 for (const auto &R
: I
.ChildRecords
) {
206 writeNameLink(BasePath
, R
, OS
);
212 if (!I
.ChildFunctions
.empty()) {
213 writeHeader("Functions", 2, OS
);
214 for (const auto &F
: I
.ChildFunctions
)
215 genMarkdown(CDCtx
, F
, OS
);
218 if (!I
.ChildEnums
.empty()) {
219 writeHeader("Enums", 2, OS
);
220 for (const auto &E
: I
.ChildEnums
)
221 genMarkdown(CDCtx
, E
, OS
);
226 static void genMarkdown(const ClangDocContext
&CDCtx
, const RecordInfo
&I
,
227 llvm::raw_ostream
&OS
) {
228 writeHeader(getTagType(I
.TagType
) + " " + I
.Name
, 1, OS
);
230 writeFileDefinition(CDCtx
, *I
.DefLoc
, OS
);
232 if (!I
.Description
.empty()) {
233 for (const auto &C
: I
.Description
)
234 writeDescription(C
, OS
);
238 std::string Parents
= genReferenceList(I
.Parents
);
239 std::string VParents
= genReferenceList(I
.VirtualParents
);
240 if (!Parents
.empty() || !VParents
.empty()) {
242 writeLine("Inherits from " + VParents
, OS
);
243 else if (VParents
.empty())
244 writeLine("Inherits from " + Parents
, OS
);
246 writeLine("Inherits from " + Parents
+ ", " + VParents
, OS
);
250 if (!I
.Members
.empty()) {
251 writeHeader("Members", 2, OS
);
252 for (const auto &Member
: I
.Members
) {
253 std::string Access
= getAccessSpelling(Member
.Access
).str();
255 writeLine(Access
+ " " + Member
.Type
.Name
+ " " + Member
.Name
, OS
);
257 writeLine(Member
.Type
.Name
+ " " + Member
.Name
, OS
);
262 if (!I
.ChildRecords
.empty()) {
263 writeHeader("Records", 2, OS
);
264 for (const auto &R
: I
.ChildRecords
)
265 writeLine(R
.Name
, OS
);
268 if (!I
.ChildFunctions
.empty()) {
269 writeHeader("Functions", 2, OS
);
270 for (const auto &F
: I
.ChildFunctions
)
271 genMarkdown(CDCtx
, F
, OS
);
274 if (!I
.ChildEnums
.empty()) {
275 writeHeader("Enums", 2, OS
);
276 for (const auto &E
: I
.ChildEnums
)
277 genMarkdown(CDCtx
, E
, OS
);
282 static void serializeReference(llvm::raw_fd_ostream
&OS
, Index
&I
, int Level
) {
283 // Write out the heading level starting at ##
284 OS
<< "##" << std::string(Level
, '#') << " ";
285 writeNameLink("", I
, OS
);
289 static llvm::Error
serializeIndex(ClangDocContext
&CDCtx
) {
290 std::error_code FileErr
;
291 llvm::SmallString
<128> FilePath
;
292 llvm::sys::path::native(CDCtx
.OutDirectory
, FilePath
);
293 llvm::sys::path::append(FilePath
, "all_files.md");
294 llvm::raw_fd_ostream
OS(FilePath
, FileErr
, llvm::sys::fs::OF_None
);
296 return llvm::createStringError(llvm::inconvertibleErrorCode(),
297 "error creating index file: " +
302 if (!CDCtx
.ProjectName
.empty())
303 OS
<< " for " << CDCtx
.ProjectName
;
306 for (auto C
: CDCtx
.Idx
.Children
)
307 serializeReference(OS
, C
, 0);
309 return llvm::Error::success();
312 static llvm::Error
genIndex(ClangDocContext
&CDCtx
) {
313 std::error_code FileErr
;
314 llvm::SmallString
<128> FilePath
;
315 llvm::sys::path::native(CDCtx
.OutDirectory
, FilePath
);
316 llvm::sys::path::append(FilePath
, "index.md");
317 llvm::raw_fd_ostream
OS(FilePath
, FileErr
, llvm::sys::fs::OF_None
);
319 return llvm::createStringError(llvm::inconvertibleErrorCode(),
320 "error creating index file: " +
323 OS
<< "# " << CDCtx
.ProjectName
<< " C/C++ Reference\n\n";
324 for (auto C
: CDCtx
.Idx
.Children
) {
325 if (!C
.Children
.empty()) {
328 case InfoType::IT_namespace
:
331 case InfoType::IT_record
:
334 case InfoType::IT_enum
:
337 case InfoType::IT_function
:
340 case InfoType::IT_default
:
343 OS
<< "* " << Type
<< ": [" << C
.Name
<< "](";
346 OS
<< C
.Name
<< ")\n";
349 return llvm::Error::success();
351 /// Generator for Markdown documentation.
352 class MDGenerator
: public Generator
{
354 static const char *Format
;
356 llvm::Error
generateDocForInfo(Info
*I
, llvm::raw_ostream
&OS
,
357 const ClangDocContext
&CDCtx
) override
;
358 llvm::Error
createResources(ClangDocContext
&CDCtx
) override
;
361 const char *MDGenerator::Format
= "md";
363 llvm::Error
MDGenerator::generateDocForInfo(Info
*I
, llvm::raw_ostream
&OS
,
364 const ClangDocContext
&CDCtx
) {
366 case InfoType::IT_namespace
:
367 genMarkdown(CDCtx
, *static_cast<clang::doc::NamespaceInfo
*>(I
), OS
);
369 case InfoType::IT_record
:
370 genMarkdown(CDCtx
, *static_cast<clang::doc::RecordInfo
*>(I
), OS
);
372 case InfoType::IT_enum
:
373 genMarkdown(CDCtx
, *static_cast<clang::doc::EnumInfo
*>(I
), OS
);
375 case InfoType::IT_function
:
376 genMarkdown(CDCtx
, *static_cast<clang::doc::FunctionInfo
*>(I
), OS
);
378 case InfoType::IT_default
:
379 return createStringError(llvm::inconvertibleErrorCode(),
380 "unexpected InfoType");
382 return llvm::Error::success();
385 llvm::Error
MDGenerator::createResources(ClangDocContext
&CDCtx
) {
386 // Write an all_files.md
387 auto Err
= serializeIndex(CDCtx
);
391 // Generate the index page.
392 Err
= genIndex(CDCtx
);
396 return llvm::Error::success();
399 static GeneratorRegistry::Add
<MDGenerator
> MD(MDGenerator::Format
,
400 "Generator for MD output.");
402 // This anchor is used to force the linker to link in the generated object
403 // file and thus register the generator.
404 volatile int MDGeneratorAnchorSource
= 0;