1 //===-- HTMLGenerator.cpp - HTML 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 "clang/Basic/Version.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/JSON.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/raw_ostream.h"
29 // Any other tag can be added if required
51 constexpr HTMLTag(TagType Value
) : Value(Value
) {}
53 operator TagType() const { return Value
; }
54 operator bool() = delete;
56 bool IsSelfClosing() const;
57 llvm::SmallString
<16> ToString() const;
69 HTMLNode(NodeType Type
) : Type(Type
) {}
70 virtual ~HTMLNode() = default;
72 virtual void Render(llvm::raw_ostream
&OS
, int IndentationLevel
) = 0;
73 NodeType Type
; // Type of node
76 struct TextNode
: public HTMLNode
{
77 TextNode(const Twine
&Text
)
78 : HTMLNode(NodeType::NODE_TEXT
), Text(Text
.str()) {}
80 std::string Text
; // Content of node
81 void Render(llvm::raw_ostream
&OS
, int IndentationLevel
) override
;
84 struct TagNode
: public HTMLNode
{
85 TagNode(HTMLTag Tag
) : HTMLNode(NodeType::NODE_TAG
), Tag(Tag
) {}
86 TagNode(HTMLTag Tag
, const Twine
&Text
) : TagNode(Tag
) {
87 Children
.emplace_back(std::make_unique
<TextNode
>(Text
.str()));
90 HTMLTag Tag
; // Name of HTML Tag (p, div, h1)
91 std::vector
<std::unique_ptr
<HTMLNode
>> Children
; // List of child nodes
92 std::vector
<std::pair
<std::string
, std::string
>>
93 Attributes
; // List of key-value attributes for tag
95 void Render(llvm::raw_ostream
&OS
, int IndentationLevel
) override
;
98 constexpr const char *kDoctypeDecl
= "<!DOCTYPE html>";
101 std::vector
<std::unique_ptr
<HTMLNode
>> Children
; // List of child nodes
102 void Render(llvm::raw_ostream
&OS
) {
103 OS
<< kDoctypeDecl
<< "\n";
104 for (const auto &C
: Children
) {
113 bool HTMLTag::IsSelfClosing() const {
115 case HTMLTag::TAG_META
:
116 case HTMLTag::TAG_LINK
:
119 case HTMLTag::TAG_DIV
:
120 case HTMLTag::TAG_FOOTER
:
121 case HTMLTag::TAG_H1
:
122 case HTMLTag::TAG_H2
:
123 case HTMLTag::TAG_H3
:
124 case HTMLTag::TAG_HEADER
:
125 case HTMLTag::TAG_LI
:
126 case HTMLTag::TAG_MAIN
:
127 case HTMLTag::TAG_OL
:
129 case HTMLTag::TAG_SCRIPT
:
130 case HTMLTag::TAG_SPAN
:
131 case HTMLTag::TAG_TITLE
:
132 case HTMLTag::TAG_UL
:
135 llvm_unreachable("Unhandled HTMLTag::TagType");
138 llvm::SmallString
<16> HTMLTag::ToString() const {
141 return llvm::SmallString
<16>("a");
142 case HTMLTag::TAG_DIV
:
143 return llvm::SmallString
<16>("div");
144 case HTMLTag::TAG_FOOTER
:
145 return llvm::SmallString
<16>("footer");
146 case HTMLTag::TAG_H1
:
147 return llvm::SmallString
<16>("h1");
148 case HTMLTag::TAG_H2
:
149 return llvm::SmallString
<16>("h2");
150 case HTMLTag::TAG_H3
:
151 return llvm::SmallString
<16>("h3");
152 case HTMLTag::TAG_HEADER
:
153 return llvm::SmallString
<16>("header");
154 case HTMLTag::TAG_LI
:
155 return llvm::SmallString
<16>("li");
156 case HTMLTag::TAG_LINK
:
157 return llvm::SmallString
<16>("link");
158 case HTMLTag::TAG_MAIN
:
159 return llvm::SmallString
<16>("main");
160 case HTMLTag::TAG_META
:
161 return llvm::SmallString
<16>("meta");
162 case HTMLTag::TAG_OL
:
163 return llvm::SmallString
<16>("ol");
165 return llvm::SmallString
<16>("p");
166 case HTMLTag::TAG_SCRIPT
:
167 return llvm::SmallString
<16>("script");
168 case HTMLTag::TAG_SPAN
:
169 return llvm::SmallString
<16>("span");
170 case HTMLTag::TAG_TITLE
:
171 return llvm::SmallString
<16>("title");
172 case HTMLTag::TAG_UL
:
173 return llvm::SmallString
<16>("ul");
175 llvm_unreachable("Unhandled HTMLTag::TagType");
178 void TextNode::Render(llvm::raw_ostream
&OS
, int IndentationLevel
) {
179 OS
.indent(IndentationLevel
* 2);
180 printHTMLEscaped(Text
, OS
);
183 void TagNode::Render(llvm::raw_ostream
&OS
, int IndentationLevel
) {
184 // Children nodes are rendered in the same line if all of them are text nodes
185 bool InlineChildren
= true;
186 for (const auto &C
: Children
)
187 if (C
->Type
== NodeType::NODE_TAG
) {
188 InlineChildren
= false;
191 OS
.indent(IndentationLevel
* 2);
192 OS
<< "<" << Tag
.ToString();
193 for (const auto &A
: Attributes
)
194 OS
<< " " << A
.first
<< "=\"" << A
.second
<< "\"";
195 if (Tag
.IsSelfClosing()) {
202 bool NewLineRendered
= true;
203 for (const auto &C
: Children
) {
204 int ChildrenIndentation
=
205 InlineChildren
|| !NewLineRendered
? 0 : IndentationLevel
+ 1;
206 C
->Render(OS
, ChildrenIndentation
);
207 if (!InlineChildren
&& (C
== Children
.back() ||
208 (C
->Type
!= NodeType::NODE_TEXT
||
209 (&C
+ 1)->get()->Type
!= NodeType::NODE_TEXT
))) {
211 NewLineRendered
= true;
213 NewLineRendered
= false;
216 OS
.indent(IndentationLevel
* 2);
217 OS
<< "</" << Tag
.ToString() << ">";
220 template <typename Derived
, typename Base
,
221 typename
= std::enable_if
<std::is_base_of
<Derived
, Base
>::value
>>
222 static void AppendVector(std::vector
<Derived
> &&New
,
223 std::vector
<Base
> &Original
) {
224 std::move(New
.begin(), New
.end(), std::back_inserter(Original
));
227 // Compute the relative path from an Origin directory to a Destination directory
228 static SmallString
<128> computeRelativePath(StringRef Destination
,
230 // If Origin is empty, the relative path to the Destination is its complete
235 // The relative path is an empty path if both directories are the same.
236 if (Destination
== Origin
)
239 // These iterators iterate through each of their parent directories
240 llvm::sys::path::const_iterator FileI
= llvm::sys::path::begin(Destination
);
241 llvm::sys::path::const_iterator FileE
= llvm::sys::path::end(Destination
);
242 llvm::sys::path::const_iterator DirI
= llvm::sys::path::begin(Origin
);
243 llvm::sys::path::const_iterator DirE
= llvm::sys::path::end(Origin
);
244 // Advance both iterators until the paths differ. Example:
245 // Destination = A/B/C/D
247 // FileI will point to C and DirI to E. The directories behind them is the
248 // directory they share (A/B).
249 while (FileI
!= FileE
&& DirI
!= DirE
&& *FileI
== *DirI
) {
253 SmallString
<128> Result
; // This will hold the resulting path.
254 // Result has to go up one directory for each of the remaining directories in
256 while (DirI
!= DirE
) {
257 llvm::sys::path::append(Result
, "..");
260 // Result has to append each of the remaining directories in Destination
261 while (FileI
!= FileE
) {
262 llvm::sys::path::append(Result
, *FileI
);
270 static std::vector
<std::unique_ptr
<TagNode
>>
271 genStylesheetsHTML(StringRef InfoPath
, const ClangDocContext
&CDCtx
) {
272 std::vector
<std::unique_ptr
<TagNode
>> Out
;
273 for (const auto &FilePath
: CDCtx
.UserStylesheets
) {
274 auto LinkNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_LINK
);
275 LinkNode
->Attributes
.emplace_back("rel", "stylesheet");
276 SmallString
<128> StylesheetPath
= computeRelativePath("", InfoPath
);
277 llvm::sys::path::append(StylesheetPath
,
278 llvm::sys::path::filename(FilePath
));
279 // Paths in HTML must be in posix-style
280 llvm::sys::path::native(StylesheetPath
, llvm::sys::path::Style::posix
);
281 LinkNode
->Attributes
.emplace_back("href", std::string(StylesheetPath
.str()));
282 Out
.emplace_back(std::move(LinkNode
));
287 static std::vector
<std::unique_ptr
<TagNode
>>
288 genJsScriptsHTML(StringRef InfoPath
, const ClangDocContext
&CDCtx
) {
289 std::vector
<std::unique_ptr
<TagNode
>> Out
;
290 for (const auto &FilePath
: CDCtx
.JsScripts
) {
291 auto ScriptNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_SCRIPT
);
292 SmallString
<128> ScriptPath
= computeRelativePath("", InfoPath
);
293 llvm::sys::path::append(ScriptPath
, llvm::sys::path::filename(FilePath
));
294 // Paths in HTML must be in posix-style
295 llvm::sys::path::native(ScriptPath
, llvm::sys::path::Style::posix
);
296 ScriptNode
->Attributes
.emplace_back("src", std::string(ScriptPath
.str()));
297 Out
.emplace_back(std::move(ScriptNode
));
302 static std::unique_ptr
<TagNode
> genLink(const Twine
&Text
, const Twine
&Link
) {
303 auto LinkNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_A
, Text
);
304 LinkNode
->Attributes
.emplace_back("href", Link
.str());
308 static std::unique_ptr
<HTMLNode
>
309 genReference(const Reference
&Type
, StringRef CurrentDirectory
,
310 llvm::Optional
<StringRef
> JumpToSection
= None
) {
311 if (Type
.Path
.empty() && !Type
.IsInGlobalNamespace
) {
313 return std::make_unique
<TextNode
>(Type
.Name
);
315 return genLink(Type
.Name
, "#" + JumpToSection
.getValue());
317 llvm::SmallString
<64> Path
= Type
.getRelativeFilePath(CurrentDirectory
);
318 llvm::sys::path::append(Path
, Type
.getFileBaseName() + ".html");
320 // Paths in HTML must be in posix-style
321 llvm::sys::path::native(Path
, llvm::sys::path::Style::posix
);
323 Path
+= ("#" + JumpToSection
.getValue()).str();
324 return genLink(Type
.Name
, Path
);
327 static std::vector
<std::unique_ptr
<HTMLNode
>>
328 genReferenceList(const llvm::SmallVectorImpl
<Reference
> &Refs
,
329 const StringRef
&CurrentDirectory
) {
330 std::vector
<std::unique_ptr
<HTMLNode
>> Out
;
331 for (const auto &R
: Refs
) {
332 if (&R
!= Refs
.begin())
333 Out
.emplace_back(std::make_unique
<TextNode
>(", "));
334 Out
.emplace_back(genReference(R
, CurrentDirectory
));
339 static std::vector
<std::unique_ptr
<TagNode
>>
340 genHTML(const EnumInfo
&I
, const ClangDocContext
&CDCtx
);
341 static std::vector
<std::unique_ptr
<TagNode
>>
342 genHTML(const FunctionInfo
&I
, const ClangDocContext
&CDCtx
,
343 StringRef ParentInfoDir
);
345 static std::vector
<std::unique_ptr
<TagNode
>>
346 genEnumsBlock(const std::vector
<EnumInfo
> &Enums
,
347 const ClangDocContext
&CDCtx
) {
351 std::vector
<std::unique_ptr
<TagNode
>> Out
;
352 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_H2
, "Enums"));
353 Out
.back()->Attributes
.emplace_back("id", "Enums");
354 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
));
355 auto &DivBody
= Out
.back();
356 for (const auto &E
: Enums
) {
357 std::vector
<std::unique_ptr
<TagNode
>> Nodes
= genHTML(E
, CDCtx
);
358 AppendVector(std::move(Nodes
), DivBody
->Children
);
363 static std::unique_ptr
<TagNode
>
364 genEnumMembersBlock(const llvm::SmallVector
<SmallString
<16>, 4> &Members
) {
368 auto List
= std::make_unique
<TagNode
>(HTMLTag::TAG_UL
);
369 for (const auto &M
: Members
)
370 List
->Children
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_LI
, M
));
374 static std::vector
<std::unique_ptr
<TagNode
>>
375 genFunctionsBlock(const std::vector
<FunctionInfo
> &Functions
,
376 const ClangDocContext
&CDCtx
, StringRef ParentInfoDir
) {
377 if (Functions
.empty())
380 std::vector
<std::unique_ptr
<TagNode
>> Out
;
381 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_H2
, "Functions"));
382 Out
.back()->Attributes
.emplace_back("id", "Functions");
383 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
));
384 auto &DivBody
= Out
.back();
385 for (const auto &F
: Functions
) {
386 std::vector
<std::unique_ptr
<TagNode
>> Nodes
=
387 genHTML(F
, CDCtx
, ParentInfoDir
);
388 AppendVector(std::move(Nodes
), DivBody
->Children
);
393 static std::vector
<std::unique_ptr
<TagNode
>>
394 genRecordMembersBlock(const llvm::SmallVector
<MemberTypeInfo
, 4> &Members
,
395 StringRef ParentInfoDir
) {
399 std::vector
<std::unique_ptr
<TagNode
>> Out
;
400 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_H2
, "Members"));
401 Out
.back()->Attributes
.emplace_back("id", "Members");
402 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_UL
));
403 auto &ULBody
= Out
.back();
404 for (const auto &M
: Members
) {
405 std::string Access
= getAccessSpelling(M
.Access
).str();
407 Access
= Access
+ " ";
408 auto LIBody
= std::make_unique
<TagNode
>(HTMLTag::TAG_LI
);
409 LIBody
->Children
.emplace_back(std::make_unique
<TextNode
>(Access
));
410 LIBody
->Children
.emplace_back(genReference(M
.Type
, ParentInfoDir
));
411 LIBody
->Children
.emplace_back(std::make_unique
<TextNode
>(" " + M
.Name
));
412 ULBody
->Children
.emplace_back(std::move(LIBody
));
417 static std::vector
<std::unique_ptr
<TagNode
>>
418 genReferencesBlock(const std::vector
<Reference
> &References
,
419 llvm::StringRef Title
, StringRef ParentPath
) {
420 if (References
.empty())
423 std::vector
<std::unique_ptr
<TagNode
>> Out
;
424 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_H2
, Title
));
425 Out
.back()->Attributes
.emplace_back("id", std::string(Title
));
426 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_UL
));
427 auto &ULBody
= Out
.back();
428 for (const auto &R
: References
) {
429 auto LiNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_LI
);
430 LiNode
->Children
.emplace_back(genReference(R
, ParentPath
));
431 ULBody
->Children
.emplace_back(std::move(LiNode
));
436 static std::unique_ptr
<TagNode
>
437 writeFileDefinition(const Location
&L
,
438 llvm::Optional
<StringRef
> RepositoryUrl
= None
) {
439 if (!L
.IsFileInRootDir
|| !RepositoryUrl
)
440 return std::make_unique
<TagNode
>(
441 HTMLTag::TAG_P
, "Defined at line " + std::to_string(L
.LineNumber
) +
442 " of file " + L
.Filename
);
443 SmallString
<128> FileURL(RepositoryUrl
.getValue());
444 llvm::sys::path::append(FileURL
, llvm::sys::path::Style::posix
, L
.Filename
);
445 auto Node
= std::make_unique
<TagNode
>(HTMLTag::TAG_P
);
446 Node
->Children
.emplace_back(std::make_unique
<TextNode
>("Defined at line "));
448 std::make_unique
<TagNode
>(HTMLTag::TAG_A
, std::to_string(L
.LineNumber
));
449 // The links to a specific line in the source code use the github /
450 // googlesource notation so it won't work for all hosting pages.
451 LocNumberNode
->Attributes
.emplace_back(
452 "href", (FileURL
+ "#" + std::to_string(L
.LineNumber
)).str());
453 Node
->Children
.emplace_back(std::move(LocNumberNode
));
454 Node
->Children
.emplace_back(std::make_unique
<TextNode
>(" of file "));
455 auto LocFileNode
= std::make_unique
<TagNode
>(
456 HTMLTag::TAG_A
, llvm::sys::path::filename(FileURL
));
457 LocFileNode
->Attributes
.emplace_back("href", std::string(FileURL
.str()));
458 Node
->Children
.emplace_back(std::move(LocFileNode
));
462 static std::vector
<std::unique_ptr
<TagNode
>>
463 genHTML(const Index
&Index
, StringRef InfoPath
, bool IsOutermostList
);
465 // Generates a list of child nodes for the HTML head tag
466 // It contains a meta node, link nodes to import CSS files, and script nodes to
468 static std::vector
<std::unique_ptr
<TagNode
>>
469 genFileHeadNodes(StringRef Title
, StringRef InfoPath
,
470 const ClangDocContext
&CDCtx
) {
471 std::vector
<std::unique_ptr
<TagNode
>> Out
;
472 auto MetaNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_META
);
473 MetaNode
->Attributes
.emplace_back("charset", "utf-8");
474 Out
.emplace_back(std::move(MetaNode
));
475 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_TITLE
, Title
));
476 std::vector
<std::unique_ptr
<TagNode
>> StylesheetsNodes
=
477 genStylesheetsHTML(InfoPath
, CDCtx
);
478 AppendVector(std::move(StylesheetsNodes
), Out
);
479 std::vector
<std::unique_ptr
<TagNode
>> JsNodes
=
480 genJsScriptsHTML(InfoPath
, CDCtx
);
481 AppendVector(std::move(JsNodes
), Out
);
485 // Generates a header HTML node that can be used for any file
486 // It contains the project name
487 static std::unique_ptr
<TagNode
> genFileHeaderNode(StringRef ProjectName
) {
488 auto HeaderNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_HEADER
, ProjectName
);
489 HeaderNode
->Attributes
.emplace_back("id", "project-title");
493 // Generates a main HTML node that has all the main content of an info file
494 // It contains both indexes and the info's documented information
495 // This function should only be used for the info files (not for the file that
496 // only has the general index)
497 static std::unique_ptr
<TagNode
> genInfoFileMainNode(
499 std::vector
<std::unique_ptr
<TagNode
>> &MainContentInnerNodes
,
500 const Index
&InfoIndex
) {
501 auto MainNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_MAIN
);
503 auto LeftSidebarNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
);
504 LeftSidebarNode
->Attributes
.emplace_back("id", "sidebar-left");
505 LeftSidebarNode
->Attributes
.emplace_back("path", std::string(InfoPath
));
506 LeftSidebarNode
->Attributes
.emplace_back(
507 "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
509 auto MainContentNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
);
510 MainContentNode
->Attributes
.emplace_back("id", "main-content");
511 MainContentNode
->Attributes
.emplace_back(
512 "class", "col-xs-12 col-sm-9 col-md-8 main-content");
513 AppendVector(std::move(MainContentInnerNodes
), MainContentNode
->Children
);
515 auto RightSidebarNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
);
516 RightSidebarNode
->Attributes
.emplace_back("id", "sidebar-right");
517 RightSidebarNode
->Attributes
.emplace_back(
518 "class", "col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right");
519 std::vector
<std::unique_ptr
<TagNode
>> InfoIndexHTML
=
520 genHTML(InfoIndex
, InfoPath
, true);
521 AppendVector(std::move(InfoIndexHTML
), RightSidebarNode
->Children
);
523 MainNode
->Children
.emplace_back(std::move(LeftSidebarNode
));
524 MainNode
->Children
.emplace_back(std::move(MainContentNode
));
525 MainNode
->Children
.emplace_back(std::move(RightSidebarNode
));
530 // Generates a footer HTML node that can be used for any file
531 // It contains clang-doc's version
532 static std::unique_ptr
<TagNode
> genFileFooterNode() {
533 auto FooterNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_FOOTER
);
534 auto SpanNode
= std::make_unique
<TagNode
>(
535 HTMLTag::TAG_SPAN
, clang::getClangToolFullVersion("clang-doc"));
536 SpanNode
->Attributes
.emplace_back("class", "no-break");
537 FooterNode
->Children
.emplace_back(std::move(SpanNode
));
541 // Generates a complete HTMLFile for an Info
543 genInfoFile(StringRef Title
, StringRef InfoPath
,
544 std::vector
<std::unique_ptr
<TagNode
>> &MainContentNodes
,
545 const Index
&InfoIndex
, const ClangDocContext
&CDCtx
) {
548 std::vector
<std::unique_ptr
<TagNode
>> HeadNodes
=
549 genFileHeadNodes(Title
, InfoPath
, CDCtx
);
550 std::unique_ptr
<TagNode
> HeaderNode
= genFileHeaderNode(CDCtx
.ProjectName
);
551 std::unique_ptr
<TagNode
> MainNode
=
552 genInfoFileMainNode(InfoPath
, MainContentNodes
, InfoIndex
);
553 std::unique_ptr
<TagNode
> FooterNode
= genFileFooterNode();
555 AppendVector(std::move(HeadNodes
), F
.Children
);
556 F
.Children
.emplace_back(std::move(HeaderNode
));
557 F
.Children
.emplace_back(std::move(MainNode
));
558 F
.Children
.emplace_back(std::move(FooterNode
));
563 template <typename T
,
564 typename
= std::enable_if
<std::is_base_of
<T
, Info
>::value
>>
565 static Index
genInfoIndexItem(const std::vector
<T
> &Infos
, StringRef Title
) {
566 Index
Idx(Title
, Title
);
567 for (const auto &C
: Infos
)
568 Idx
.Children
.emplace_back(C
.extractName(),
569 llvm::toHex(llvm::toStringRef(C
.USR
)));
573 static std::vector
<std::unique_ptr
<TagNode
>>
574 genHTML(const Index
&Index
, StringRef InfoPath
, bool IsOutermostList
) {
575 std::vector
<std::unique_ptr
<TagNode
>> Out
;
576 if (!Index
.Name
.empty()) {
577 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_SPAN
));
578 auto &SpanBody
= Out
.back();
579 if (!Index
.JumpToSection
)
580 SpanBody
->Children
.emplace_back(genReference(Index
, InfoPath
));
582 SpanBody
->Children
.emplace_back(
583 genReference(Index
, InfoPath
, Index
.JumpToSection
.getValue().str()));
585 if (Index
.Children
.empty())
587 // Only the outermost list should use ol, the others should use ul
588 HTMLTag ListHTMLTag
= IsOutermostList
? HTMLTag::TAG_OL
: HTMLTag::TAG_UL
;
589 Out
.emplace_back(std::make_unique
<TagNode
>(ListHTMLTag
));
590 const auto &UlBody
= Out
.back();
591 for (const auto &C
: Index
.Children
) {
592 auto LiBody
= std::make_unique
<TagNode
>(HTMLTag::TAG_LI
);
593 std::vector
<std::unique_ptr
<TagNode
>> Nodes
= genHTML(C
, InfoPath
, false);
594 AppendVector(std::move(Nodes
), LiBody
->Children
);
595 UlBody
->Children
.emplace_back(std::move(LiBody
));
600 static std::unique_ptr
<HTMLNode
> genHTML(const CommentInfo
&I
) {
601 if (I
.Kind
== "FullComment") {
602 auto FullComment
= std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
);
603 for (const auto &Child
: I
.Children
) {
604 std::unique_ptr
<HTMLNode
> Node
= genHTML(*Child
);
606 FullComment
->Children
.emplace_back(std::move(Node
));
608 return std::move(FullComment
);
609 } else if (I
.Kind
== "ParagraphComment") {
610 auto ParagraphComment
= std::make_unique
<TagNode
>(HTMLTag::TAG_P
);
611 for (const auto &Child
: I
.Children
) {
612 std::unique_ptr
<HTMLNode
> Node
= genHTML(*Child
);
614 ParagraphComment
->Children
.emplace_back(std::move(Node
));
616 if (ParagraphComment
->Children
.empty())
618 return std::move(ParagraphComment
);
619 } else if (I
.Kind
== "TextComment") {
622 return std::make_unique
<TextNode
>(I
.Text
);
627 static std::unique_ptr
<TagNode
> genHTML(const std::vector
<CommentInfo
> &C
) {
628 auto CommentBlock
= std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
);
629 for (const auto &Child
: C
) {
630 if (std::unique_ptr
<HTMLNode
> Node
= genHTML(Child
))
631 CommentBlock
->Children
.emplace_back(std::move(Node
));
636 static std::vector
<std::unique_ptr
<TagNode
>>
637 genHTML(const EnumInfo
&I
, const ClangDocContext
&CDCtx
) {
638 std::vector
<std::unique_ptr
<TagNode
>> Out
;
639 std::string EnumType
;
641 EnumType
= "enum class ";
646 std::make_unique
<TagNode
>(HTMLTag::TAG_H3
, EnumType
+ I
.Name
));
647 Out
.back()->Attributes
.emplace_back("id",
648 llvm::toHex(llvm::toStringRef(I
.USR
)));
650 std::unique_ptr
<TagNode
> Node
= genEnumMembersBlock(I
.Members
);
652 Out
.emplace_back(std::move(Node
));
655 if (!CDCtx
.RepositoryUrl
)
656 Out
.emplace_back(writeFileDefinition(I
.DefLoc
.getValue()));
658 Out
.emplace_back(writeFileDefinition(
659 I
.DefLoc
.getValue(), StringRef
{CDCtx
.RepositoryUrl
.getValue()}));
662 std::string Description
;
663 if (!I
.Description
.empty())
664 Out
.emplace_back(genHTML(I
.Description
));
669 static std::vector
<std::unique_ptr
<TagNode
>>
670 genHTML(const FunctionInfo
&I
, const ClangDocContext
&CDCtx
,
671 StringRef ParentInfoDir
) {
672 std::vector
<std::unique_ptr
<TagNode
>> Out
;
673 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_H3
, I
.Name
));
674 // USR is used as id for functions instead of name to disambiguate function
676 Out
.back()->Attributes
.emplace_back("id",
677 llvm::toHex(llvm::toStringRef(I
.USR
)));
679 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_P
));
680 auto &FunctionHeader
= Out
.back();
682 std::string Access
= getAccessSpelling(I
.Access
).str();
684 FunctionHeader
->Children
.emplace_back(
685 std::make_unique
<TextNode
>(Access
+ " "));
686 if (I
.ReturnType
.Type
.Name
!= "") {
687 FunctionHeader
->Children
.emplace_back(
688 genReference(I
.ReturnType
.Type
, ParentInfoDir
));
689 FunctionHeader
->Children
.emplace_back(std::make_unique
<TextNode
>(" "));
691 FunctionHeader
->Children
.emplace_back(
692 std::make_unique
<TextNode
>(I
.Name
+ "("));
694 for (const auto &P
: I
.Params
) {
695 if (&P
!= I
.Params
.begin())
696 FunctionHeader
->Children
.emplace_back(std::make_unique
<TextNode
>(", "));
697 FunctionHeader
->Children
.emplace_back(genReference(P
.Type
, ParentInfoDir
));
698 FunctionHeader
->Children
.emplace_back(
699 std::make_unique
<TextNode
>(" " + P
.Name
));
701 FunctionHeader
->Children
.emplace_back(std::make_unique
<TextNode
>(")"));
704 if (!CDCtx
.RepositoryUrl
)
705 Out
.emplace_back(writeFileDefinition(I
.DefLoc
.getValue()));
707 Out
.emplace_back(writeFileDefinition(
708 I
.DefLoc
.getValue(), StringRef
{CDCtx
.RepositoryUrl
.getValue()}));
711 std::string Description
;
712 if (!I
.Description
.empty())
713 Out
.emplace_back(genHTML(I
.Description
));
718 static std::vector
<std::unique_ptr
<TagNode
>>
719 genHTML(const NamespaceInfo
&I
, Index
&InfoIndex
, const ClangDocContext
&CDCtx
,
720 std::string
&InfoTitle
) {
721 std::vector
<std::unique_ptr
<TagNode
>> Out
;
722 if (I
.Name
.str() == "")
723 InfoTitle
= "Global Namespace";
725 InfoTitle
= ("namespace " + I
.Name
).str();
727 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_H1
, InfoTitle
));
729 std::string Description
;
730 if (!I
.Description
.empty())
731 Out
.emplace_back(genHTML(I
.Description
));
733 llvm::SmallString
<64> BasePath
= I
.getRelativeFilePath("");
735 std::vector
<std::unique_ptr
<TagNode
>> ChildNamespaces
=
736 genReferencesBlock(I
.ChildNamespaces
, "Namespaces", BasePath
);
737 AppendVector(std::move(ChildNamespaces
), Out
);
738 std::vector
<std::unique_ptr
<TagNode
>> ChildRecords
=
739 genReferencesBlock(I
.ChildRecords
, "Records", BasePath
);
740 AppendVector(std::move(ChildRecords
), Out
);
742 std::vector
<std::unique_ptr
<TagNode
>> ChildFunctions
=
743 genFunctionsBlock(I
.ChildFunctions
, CDCtx
, BasePath
);
744 AppendVector(std::move(ChildFunctions
), Out
);
745 std::vector
<std::unique_ptr
<TagNode
>> ChildEnums
=
746 genEnumsBlock(I
.ChildEnums
, CDCtx
);
747 AppendVector(std::move(ChildEnums
), Out
);
749 if (!I
.ChildNamespaces
.empty())
750 InfoIndex
.Children
.emplace_back("Namespaces", "Namespaces");
751 if (!I
.ChildRecords
.empty())
752 InfoIndex
.Children
.emplace_back("Records", "Records");
753 if (!I
.ChildFunctions
.empty())
754 InfoIndex
.Children
.emplace_back(
755 genInfoIndexItem(I
.ChildFunctions
, "Functions"));
756 if (!I
.ChildEnums
.empty())
757 InfoIndex
.Children
.emplace_back(genInfoIndexItem(I
.ChildEnums
, "Enums"));
762 static std::vector
<std::unique_ptr
<TagNode
>>
763 genHTML(const RecordInfo
&I
, Index
&InfoIndex
, const ClangDocContext
&CDCtx
,
764 std::string
&InfoTitle
) {
765 std::vector
<std::unique_ptr
<TagNode
>> Out
;
766 InfoTitle
= (getTagType(I
.TagType
) + " " + I
.Name
).str();
767 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_H1
, InfoTitle
));
770 if (!CDCtx
.RepositoryUrl
)
771 Out
.emplace_back(writeFileDefinition(I
.DefLoc
.getValue()));
773 Out
.emplace_back(writeFileDefinition(
774 I
.DefLoc
.getValue(), StringRef
{CDCtx
.RepositoryUrl
.getValue()}));
777 std::string Description
;
778 if (!I
.Description
.empty())
779 Out
.emplace_back(genHTML(I
.Description
));
781 std::vector
<std::unique_ptr
<HTMLNode
>> Parents
=
782 genReferenceList(I
.Parents
, I
.Path
);
783 std::vector
<std::unique_ptr
<HTMLNode
>> VParents
=
784 genReferenceList(I
.VirtualParents
, I
.Path
);
785 if (!Parents
.empty() || !VParents
.empty()) {
786 Out
.emplace_back(std::make_unique
<TagNode
>(HTMLTag::TAG_P
));
787 auto &PBody
= Out
.back();
788 PBody
->Children
.emplace_back(std::make_unique
<TextNode
>("Inherits from "));
790 AppendVector(std::move(VParents
), PBody
->Children
);
791 else if (VParents
.empty())
792 AppendVector(std::move(Parents
), PBody
->Children
);
794 AppendVector(std::move(Parents
), PBody
->Children
);
795 PBody
->Children
.emplace_back(std::make_unique
<TextNode
>(", "));
796 AppendVector(std::move(VParents
), PBody
->Children
);
800 std::vector
<std::unique_ptr
<TagNode
>> Members
=
801 genRecordMembersBlock(I
.Members
, I
.Path
);
802 AppendVector(std::move(Members
), Out
);
803 std::vector
<std::unique_ptr
<TagNode
>> ChildRecords
=
804 genReferencesBlock(I
.ChildRecords
, "Records", I
.Path
);
805 AppendVector(std::move(ChildRecords
), Out
);
807 std::vector
<std::unique_ptr
<TagNode
>> ChildFunctions
=
808 genFunctionsBlock(I
.ChildFunctions
, CDCtx
, I
.Path
);
809 AppendVector(std::move(ChildFunctions
), Out
);
810 std::vector
<std::unique_ptr
<TagNode
>> ChildEnums
=
811 genEnumsBlock(I
.ChildEnums
, CDCtx
);
812 AppendVector(std::move(ChildEnums
), Out
);
814 if (!I
.Members
.empty())
815 InfoIndex
.Children
.emplace_back("Members", "Members");
816 if (!I
.ChildRecords
.empty())
817 InfoIndex
.Children
.emplace_back("Records", "Records");
818 if (!I
.ChildFunctions
.empty())
819 InfoIndex
.Children
.emplace_back(
820 genInfoIndexItem(I
.ChildFunctions
, "Functions"));
821 if (!I
.ChildEnums
.empty())
822 InfoIndex
.Children
.emplace_back(genInfoIndexItem(I
.ChildEnums
, "Enums"));
827 /// Generator for HTML documentation.
828 class HTMLGenerator
: public Generator
{
830 static const char *Format
;
832 llvm::Error
generateDocForInfo(Info
*I
, llvm::raw_ostream
&OS
,
833 const ClangDocContext
&CDCtx
) override
;
834 llvm::Error
createResources(ClangDocContext
&CDCtx
) override
;
837 const char *HTMLGenerator::Format
= "html";
839 llvm::Error
HTMLGenerator::generateDocForInfo(Info
*I
, llvm::raw_ostream
&OS
,
840 const ClangDocContext
&CDCtx
) {
841 std::string InfoTitle
;
842 std::vector
<std::unique_ptr
<TagNode
>> MainContentNodes
;
845 case InfoType::IT_namespace
:
846 MainContentNodes
= genHTML(*static_cast<clang::doc::NamespaceInfo
*>(I
),
847 InfoIndex
, CDCtx
, InfoTitle
);
849 case InfoType::IT_record
:
850 MainContentNodes
= genHTML(*static_cast<clang::doc::RecordInfo
*>(I
),
851 InfoIndex
, CDCtx
, InfoTitle
);
853 case InfoType::IT_enum
:
854 MainContentNodes
= genHTML(*static_cast<clang::doc::EnumInfo
*>(I
), CDCtx
);
856 case InfoType::IT_function
:
858 genHTML(*static_cast<clang::doc::FunctionInfo
*>(I
), CDCtx
, "");
860 case InfoType::IT_default
:
861 return llvm::createStringError(llvm::inconvertibleErrorCode(),
862 "unexpected info type");
865 HTMLFile F
= genInfoFile(InfoTitle
, I
->getRelativeFilePath(""),
866 MainContentNodes
, InfoIndex
, CDCtx
);
869 return llvm::Error::success();
872 static std::string
getRefType(InfoType IT
) {
874 case InfoType::IT_default
:
876 case InfoType::IT_namespace
:
878 case InfoType::IT_record
:
880 case InfoType::IT_function
:
882 case InfoType::IT_enum
:
885 llvm_unreachable("Unknown InfoType");
888 static llvm::Error
SerializeIndex(ClangDocContext
&CDCtx
) {
890 std::error_code FileErr
;
891 llvm::SmallString
<128> FilePath
;
892 llvm::sys::path::native(CDCtx
.OutDirectory
, FilePath
);
893 llvm::sys::path::append(FilePath
, "index_json.js");
894 llvm::raw_fd_ostream
OS(FilePath
, FileErr
, llvm::sys::fs::OF_None
);
896 return llvm::createStringError(llvm::inconvertibleErrorCode(),
897 "error creating index file: " +
901 llvm::json::OStream
J(OS
, 2);
902 std::function
<void(Index
)> IndexToJSON
= [&](const Index
&I
) {
904 J
.attribute("USR", toHex(llvm::toStringRef(I
.USR
)));
905 J
.attribute("Name", I
.Name
);
906 J
.attribute("RefType", getRefType(I
.RefType
));
907 J
.attribute("Path", I
.getRelativeFilePath(""));
908 J
.attributeArray("Children", [&] {
909 for (const Index
&C
: I
.Children
)
914 OS
<< "var JsonIndex = `\n";
915 IndexToJSON(CDCtx
.Idx
);
917 return llvm::Error::success();
920 // Generates a main HTML node that has the main content of the file that shows
921 // only the general index
922 // It contains the general index with links to all the generated files
923 static std::unique_ptr
<TagNode
> genIndexFileMainNode() {
924 auto MainNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_MAIN
);
926 auto LeftSidebarNode
= std::make_unique
<TagNode
>(HTMLTag::TAG_DIV
);
927 LeftSidebarNode
->Attributes
.emplace_back("id", "sidebar-left");
928 LeftSidebarNode
->Attributes
.emplace_back("path", "");
929 LeftSidebarNode
->Attributes
.emplace_back(
930 "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
931 LeftSidebarNode
->Attributes
.emplace_back("style", "flex: 0 100%;");
933 MainNode
->Children
.emplace_back(std::move(LeftSidebarNode
));
938 static llvm::Error
GenIndex(const ClangDocContext
&CDCtx
) {
939 std::error_code FileErr
, OK
;
940 llvm::SmallString
<128> IndexPath
;
941 llvm::sys::path::native(CDCtx
.OutDirectory
, IndexPath
);
942 llvm::sys::path::append(IndexPath
, "index.html");
943 llvm::raw_fd_ostream
IndexOS(IndexPath
, FileErr
, llvm::sys::fs::OF_None
);
945 return llvm::createStringError(llvm::inconvertibleErrorCode(),
946 "error creating main index: " +
952 std::vector
<std::unique_ptr
<TagNode
>> HeadNodes
=
953 genFileHeadNodes("Index", "", CDCtx
);
954 std::unique_ptr
<TagNode
> HeaderNode
= genFileHeaderNode(CDCtx
.ProjectName
);
955 std::unique_ptr
<TagNode
> MainNode
= genIndexFileMainNode();
956 std::unique_ptr
<TagNode
> FooterNode
= genFileFooterNode();
958 AppendVector(std::move(HeadNodes
), F
.Children
);
959 F
.Children
.emplace_back(std::move(HeaderNode
));
960 F
.Children
.emplace_back(std::move(MainNode
));
961 F
.Children
.emplace_back(std::move(FooterNode
));
965 return llvm::Error::success();
968 static llvm::Error
CopyFile(StringRef FilePath
, StringRef OutDirectory
) {
969 llvm::SmallString
<128> PathWrite
;
970 llvm::sys::path::native(OutDirectory
, PathWrite
);
971 llvm::sys::path::append(PathWrite
, llvm::sys::path::filename(FilePath
));
972 llvm::SmallString
<128> PathRead
;
973 llvm::sys::path::native(FilePath
, PathRead
);
975 std::error_code FileErr
= llvm::sys::fs::copy_file(PathRead
, PathWrite
);
977 return llvm::createStringError(llvm::inconvertibleErrorCode(),
978 "error creating file " +
979 llvm::sys::path::filename(FilePath
) +
980 ": " + FileErr
.message() + "\n");
982 return llvm::Error::success();
985 llvm::Error
HTMLGenerator::createResources(ClangDocContext
&CDCtx
) {
986 auto Err
= SerializeIndex(CDCtx
);
989 Err
= GenIndex(CDCtx
);
993 for (const auto &FilePath
: CDCtx
.UserStylesheets
) {
994 Err
= CopyFile(FilePath
, CDCtx
.OutDirectory
);
998 for (const auto &FilePath
: CDCtx
.FilesToCopy
) {
999 Err
= CopyFile(FilePath
, CDCtx
.OutDirectory
);
1003 return llvm::Error::success();
1006 static GeneratorRegistry::Add
<HTMLGenerator
> HTML(HTMLGenerator::Format
,
1007 "Generator for HTML output.");
1009 // This anchor is used to force the linker to link in the generated object
1010 // file and thus register the generator.
1011 volatile int HTMLGeneratorAnchorSource
= 0;
1014 } // namespace clang