xref: /llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp (revision c29aba7159473c5fc225d4aea75647939497e3ac)
1 //===-- HTMLGenerator.cpp - HTML 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 //===----------------------------------------------------------------------===//
8 
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/ADT/StringSet.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/JSON.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/raw_ostream.h"
19 #include <algorithm>
20 #include <optional>
21 #include <string>
22 
23 using namespace llvm;
24 
25 namespace clang {
26 namespace doc {
27 
28 namespace {
29 
30 class HTMLTag {
31 public:
32   // Any other tag can be added if required
33   enum TagType {
34     TAG_A,
35     TAG_DIV,
36     TAG_FOOTER,
37     TAG_H1,
38     TAG_H2,
39     TAG_H3,
40     TAG_HEADER,
41     TAG_LI,
42     TAG_LINK,
43     TAG_MAIN,
44     TAG_META,
45     TAG_OL,
46     TAG_P,
47     TAG_SCRIPT,
48     TAG_SPAN,
49     TAG_TITLE,
50     TAG_UL,
51     TAG_TABLE,
52     TAG_THEAD,
53     TAG_TBODY,
54     TAG_TR,
55     TAG_TD,
56     TAG_TH
57   };
58 
59   HTMLTag() = default;
60   constexpr HTMLTag(TagType Value) : Value(Value) {}
61 
62   operator TagType() const { return Value; }
63   operator bool() = delete;
64 
65   bool isSelfClosing() const;
66   StringRef toString() const;
67 
68 private:
69   TagType Value;
70 };
71 
72 enum NodeType {
73   NODE_TEXT,
74   NODE_TAG,
75 };
76 
77 struct HTMLNode {
78   HTMLNode(NodeType Type) : Type(Type) {}
79   virtual ~HTMLNode() = default;
80 
81   virtual void render(llvm::raw_ostream &OS, int IndentationLevel) = 0;
82   NodeType Type; // Type of node
83 };
84 
85 struct TextNode : public HTMLNode {
86   TextNode(const Twine &Text)
87       : HTMLNode(NodeType::NODE_TEXT), Text(Text.str()) {}
88 
89   std::string Text; // Content of node
90   void render(llvm::raw_ostream &OS, int IndentationLevel) override;
91 };
92 
93 struct TagNode : public HTMLNode {
94   TagNode(HTMLTag Tag) : HTMLNode(NodeType::NODE_TAG), Tag(Tag) {}
95   TagNode(HTMLTag Tag, const Twine &Text) : TagNode(Tag) {
96     Children.emplace_back(std::make_unique<TextNode>(Text.str()));
97   }
98 
99   HTMLTag Tag; // Name of HTML Tag (p, div, h1)
100   std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes
101   std::vector<std::pair<std::string, std::string>>
102       Attributes; // List of key-value attributes for tag
103 
104   void render(llvm::raw_ostream &OS, int IndentationLevel) override;
105 };
106 
107 constexpr const char *kDoctypeDecl = "<!DOCTYPE html>";
108 
109 struct HTMLFile {
110   std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes
111   void render(llvm::raw_ostream &OS) {
112     OS << kDoctypeDecl << "\n";
113     for (const auto &C : Children) {
114       C->render(OS, 0);
115       OS << "\n";
116     }
117   }
118 };
119 
120 } // namespace
121 
122 bool HTMLTag::isSelfClosing() const {
123   switch (Value) {
124   case HTMLTag::TAG_META:
125   case HTMLTag::TAG_LINK:
126     return true;
127   case HTMLTag::TAG_A:
128   case HTMLTag::TAG_DIV:
129   case HTMLTag::TAG_FOOTER:
130   case HTMLTag::TAG_H1:
131   case HTMLTag::TAG_H2:
132   case HTMLTag::TAG_H3:
133   case HTMLTag::TAG_HEADER:
134   case HTMLTag::TAG_LI:
135   case HTMLTag::TAG_MAIN:
136   case HTMLTag::TAG_OL:
137   case HTMLTag::TAG_P:
138   case HTMLTag::TAG_SCRIPT:
139   case HTMLTag::TAG_SPAN:
140   case HTMLTag::TAG_TITLE:
141   case HTMLTag::TAG_UL:
142   case HTMLTag::TAG_TABLE:
143   case HTMLTag::TAG_THEAD:
144   case HTMLTag::TAG_TBODY:
145   case HTMLTag::TAG_TR:
146   case HTMLTag::TAG_TD:
147   case HTMLTag::TAG_TH:
148     return false;
149   }
150   llvm_unreachable("Unhandled HTMLTag::TagType");
151 }
152 
153 StringRef HTMLTag::toString() const {
154   switch (Value) {
155   case HTMLTag::TAG_A:
156     return "a";
157   case HTMLTag::TAG_DIV:
158     return "div";
159   case HTMLTag::TAG_FOOTER:
160     return "footer";
161   case HTMLTag::TAG_H1:
162     return "h1";
163   case HTMLTag::TAG_H2:
164     return "h2";
165   case HTMLTag::TAG_H3:
166     return "h3";
167   case HTMLTag::TAG_HEADER:
168     return "header";
169   case HTMLTag::TAG_LI:
170     return "li";
171   case HTMLTag::TAG_LINK:
172     return "link";
173   case HTMLTag::TAG_MAIN:
174     return "main";
175   case HTMLTag::TAG_META:
176     return "meta";
177   case HTMLTag::TAG_OL:
178     return "ol";
179   case HTMLTag::TAG_P:
180     return "p";
181   case HTMLTag::TAG_SCRIPT:
182     return "script";
183   case HTMLTag::TAG_SPAN:
184     return "span";
185   case HTMLTag::TAG_TITLE:
186     return "title";
187   case HTMLTag::TAG_UL:
188     return "ul";
189   case HTMLTag::TAG_TABLE:
190     return "table";
191   case HTMLTag::TAG_THEAD:
192     return "thead";
193   case HTMLTag::TAG_TBODY:
194     return "tbody";
195   case HTMLTag::TAG_TR:
196     return "tr";
197   case HTMLTag::TAG_TD:
198     return "td";
199   case HTMLTag::TAG_TH:
200     return "th";
201   }
202   llvm_unreachable("Unhandled HTMLTag::TagType");
203 }
204 
205 void TextNode::render(llvm::raw_ostream &OS, int IndentationLevel) {
206   OS.indent(IndentationLevel * 2);
207   printHTMLEscaped(Text, OS);
208 }
209 
210 void TagNode::render(llvm::raw_ostream &OS, int IndentationLevel) {
211   // Children nodes are rendered in the same line if all of them are text nodes
212   bool InlineChildren = true;
213   for (const auto &C : Children)
214     if (C->Type == NodeType::NODE_TAG) {
215       InlineChildren = false;
216       break;
217     }
218   OS.indent(IndentationLevel * 2);
219   OS << "<" << Tag.toString();
220   for (const auto &A : Attributes)
221     OS << " " << A.first << "=\"" << A.second << "\"";
222   if (Tag.isSelfClosing()) {
223     OS << "/>";
224     return;
225   }
226   OS << ">";
227   if (!InlineChildren)
228     OS << "\n";
229   bool NewLineRendered = true;
230   for (const auto &C : Children) {
231     int ChildrenIndentation =
232         InlineChildren || !NewLineRendered ? 0 : IndentationLevel + 1;
233     C->render(OS, ChildrenIndentation);
234     if (!InlineChildren && (C == Children.back() ||
235                             (C->Type != NodeType::NODE_TEXT ||
236                              (&C + 1)->get()->Type != NodeType::NODE_TEXT))) {
237       OS << "\n";
238       NewLineRendered = true;
239     } else
240       NewLineRendered = false;
241   }
242   if (!InlineChildren)
243     OS.indent(IndentationLevel * 2);
244   OS << "</" << Tag.toString() << ">";
245 }
246 
247 template <typename Derived, typename Base,
248           typename = std::enable_if<std::is_base_of<Derived, Base>::value>>
249 static void appendVector(std::vector<Derived> &&New,
250                          std::vector<Base> &Original) {
251   std::move(New.begin(), New.end(), std::back_inserter(Original));
252 }
253 
254 // Compute the relative path from an Origin directory to a Destination directory
255 static SmallString<128> computeRelativePath(StringRef Destination,
256                                             StringRef Origin) {
257   // If Origin is empty, the relative path to the Destination is its complete
258   // path.
259   if (Origin.empty())
260     return Destination;
261 
262   // The relative path is an empty path if both directories are the same.
263   if (Destination == Origin)
264     return {};
265 
266   // These iterators iterate through each of their parent directories
267   llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
268   llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
269   llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
270   llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
271   // Advance both iterators until the paths differ. Example:
272   //    Destination = A/B/C/D
273   //    Origin      = A/B/E/F
274   // FileI will point to C and DirI to E. The directories behind them is the
275   // directory they share (A/B).
276   while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
277     ++FileI;
278     ++DirI;
279   }
280   SmallString<128> Result; // This will hold the resulting path.
281   // Result has to go up one directory for each of the remaining directories in
282   // Origin
283   while (DirI != DirE) {
284     llvm::sys::path::append(Result, "..");
285     ++DirI;
286   }
287   // Result has to append each of the remaining directories in Destination
288   while (FileI != FileE) {
289     llvm::sys::path::append(Result, *FileI);
290     ++FileI;
291   }
292   return Result;
293 }
294 
295 // HTML generation
296 
297 static std::vector<std::unique_ptr<TagNode>>
298 genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
299   std::vector<std::unique_ptr<TagNode>> Out;
300   for (const auto &FilePath : CDCtx.UserStylesheets) {
301     auto LinkNode = std::make_unique<TagNode>(HTMLTag::TAG_LINK);
302     LinkNode->Attributes.emplace_back("rel", "stylesheet");
303     SmallString<128> StylesheetPath = computeRelativePath("", InfoPath);
304     llvm::sys::path::append(StylesheetPath,
305                             llvm::sys::path::filename(FilePath));
306     // Paths in HTML must be in posix-style
307     llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
308     LinkNode->Attributes.emplace_back("href", std::string(StylesheetPath));
309     Out.emplace_back(std::move(LinkNode));
310   }
311   return Out;
312 }
313 
314 static std::vector<std::unique_ptr<TagNode>>
315 genJsScriptsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
316   std::vector<std::unique_ptr<TagNode>> Out;
317 
318   // index_json.js is part of every generated HTML file
319   SmallString<128> IndexJSONPath = computeRelativePath("", InfoPath);
320   auto IndexJSONNode = std::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
321   llvm::sys::path::append(IndexJSONPath, "index_json.js");
322   llvm::sys::path::native(IndexJSONPath, llvm::sys::path::Style::posix);
323   IndexJSONNode->Attributes.emplace_back("src", std::string(IndexJSONPath));
324   Out.emplace_back(std::move(IndexJSONNode));
325 
326   for (const auto &FilePath : CDCtx.JsScripts) {
327     SmallString<128> ScriptPath = computeRelativePath("", InfoPath);
328     auto ScriptNode = std::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
329     llvm::sys::path::append(ScriptPath, llvm::sys::path::filename(FilePath));
330     // Paths in HTML must be in posix-style
331     llvm::sys::path::native(ScriptPath, llvm::sys::path::Style::posix);
332     ScriptNode->Attributes.emplace_back("src", std::string(ScriptPath));
333     Out.emplace_back(std::move(ScriptNode));
334   }
335   return Out;
336 }
337 
338 static std::unique_ptr<TagNode> genLink(const Twine &Text, const Twine &Link) {
339   auto LinkNode = std::make_unique<TagNode>(HTMLTag::TAG_A, Text);
340   LinkNode->Attributes.emplace_back("href", Link.str());
341   return LinkNode;
342 }
343 
344 static std::unique_ptr<HTMLNode>
345 genReference(const Reference &Type, StringRef CurrentDirectory,
346              std::optional<StringRef> JumpToSection = std::nullopt) {
347   if (Type.Path.empty()) {
348     if (!JumpToSection)
349       return std::make_unique<TextNode>(Type.Name);
350     return genLink(Type.Name, "#" + *JumpToSection);
351   }
352   llvm::SmallString<64> Path = Type.getRelativeFilePath(CurrentDirectory);
353   llvm::sys::path::append(Path, Type.getFileBaseName() + ".html");
354 
355   // Paths in HTML must be in posix-style
356   llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
357   if (JumpToSection)
358     Path += ("#" + *JumpToSection).str();
359   return genLink(Type.Name, Path);
360 }
361 
362 static std::vector<std::unique_ptr<HTMLNode>>
363 genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs,
364                  const StringRef &CurrentDirectory) {
365   std::vector<std::unique_ptr<HTMLNode>> Out;
366   for (const auto &R : Refs) {
367     if (&R != Refs.begin())
368       Out.emplace_back(std::make_unique<TextNode>(", "));
369     Out.emplace_back(genReference(R, CurrentDirectory));
370   }
371   return Out;
372 }
373 
374 static std::vector<std::unique_ptr<TagNode>>
375 genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
376 static std::vector<std::unique_ptr<TagNode>>
377 genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
378         StringRef ParentInfoDir);
379 static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C);
380 
381 static std::vector<std::unique_ptr<TagNode>>
382 genEnumsBlock(const std::vector<EnumInfo> &Enums,
383               const ClangDocContext &CDCtx) {
384   if (Enums.empty())
385     return {};
386 
387   std::vector<std::unique_ptr<TagNode>> Out;
388   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Enums"));
389   Out.back()->Attributes.emplace_back("id", "Enums");
390   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
391   auto &DivBody = Out.back();
392   for (const auto &E : Enums) {
393     std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E, CDCtx);
394     appendVector(std::move(Nodes), DivBody->Children);
395   }
396   return Out;
397 }
398 
399 static std::unique_ptr<TagNode>
400 genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
401   if (Members.empty())
402     return nullptr;
403 
404   auto List = std::make_unique<TagNode>(HTMLTag::TAG_TBODY);
405 
406   for (const auto &M : Members) {
407     auto TRNode = std::make_unique<TagNode>(HTMLTag::TAG_TR);
408     TRNode->Children.emplace_back(
409         std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Name));
410     // Use user supplied value if it exists, otherwise use the value
411     if (!M.ValueExpr.empty()) {
412       TRNode->Children.emplace_back(
413           std::make_unique<TagNode>(HTMLTag::TAG_TD, M.ValueExpr));
414     } else {
415       TRNode->Children.emplace_back(
416           std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
417     }
418     if (!M.Description.empty()) {
419       auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
420       TD->Children.emplace_back(genHTML(M.Description));
421       TRNode->Children.emplace_back(std::move(TD));
422     }
423     List->Children.emplace_back(std::move(TRNode));
424   }
425   return List;
426 }
427 
428 static std::vector<std::unique_ptr<TagNode>>
429 genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
430                   const ClangDocContext &CDCtx, StringRef ParentInfoDir) {
431   if (Functions.empty())
432     return {};
433 
434   std::vector<std::unique_ptr<TagNode>> Out;
435   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions"));
436   Out.back()->Attributes.emplace_back("id", "Functions");
437   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
438   auto &DivBody = Out.back();
439   for (const auto &F : Functions) {
440     std::vector<std::unique_ptr<TagNode>> Nodes =
441         genHTML(F, CDCtx, ParentInfoDir);
442     appendVector(std::move(Nodes), DivBody->Children);
443   }
444   return Out;
445 }
446 
447 static std::vector<std::unique_ptr<TagNode>>
448 genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
449                       StringRef ParentInfoDir) {
450   if (Members.empty())
451     return {};
452 
453   std::vector<std::unique_ptr<TagNode>> Out;
454   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Members"));
455   Out.back()->Attributes.emplace_back("id", "Members");
456   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
457   auto &ULBody = Out.back();
458   for (const auto &M : Members) {
459     std::string Access = getAccessSpelling(M.Access).str();
460     if (Access != "")
461       Access = Access + " ";
462     auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
463     auto MemberDecl = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
464     MemberDecl->Children.emplace_back(std::make_unique<TextNode>(Access));
465     MemberDecl->Children.emplace_back(genReference(M.Type, ParentInfoDir));
466     MemberDecl->Children.emplace_back(std::make_unique<TextNode>(" " + M.Name));
467     if (!M.Description.empty())
468       LIBody->Children.emplace_back(genHTML(M.Description));
469     LIBody->Children.emplace_back(std::move(MemberDecl));
470     ULBody->Children.emplace_back(std::move(LIBody));
471   }
472   return Out;
473 }
474 
475 static std::vector<std::unique_ptr<TagNode>>
476 genReferencesBlock(const std::vector<Reference> &References,
477                    llvm::StringRef Title, StringRef ParentPath) {
478   if (References.empty())
479     return {};
480 
481   std::vector<std::unique_ptr<TagNode>> Out;
482   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, Title));
483   Out.back()->Attributes.emplace_back("id", std::string(Title));
484   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
485   auto &ULBody = Out.back();
486   for (const auto &R : References) {
487     auto LiNode = std::make_unique<TagNode>(HTMLTag::TAG_LI);
488     LiNode->Children.emplace_back(genReference(R, ParentPath));
489     ULBody->Children.emplace_back(std::move(LiNode));
490   }
491   return Out;
492 }
493 
494 static std::unique_ptr<TagNode>
495 writeFileDefinition(const Location &L,
496                     std::optional<StringRef> RepositoryUrl = std::nullopt) {
497   if (!L.IsFileInRootDir || !RepositoryUrl)
498     return std::make_unique<TagNode>(
499         HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
500                             " of file " + L.Filename);
501   SmallString<128> FileURL(*RepositoryUrl);
502   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
503   auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
504   Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
505   auto LocNumberNode =
506       std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
507   // The links to a specific line in the source code use the github /
508   // googlesource notation so it won't work for all hosting pages.
509   LocNumberNode->Attributes.emplace_back(
510       "href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
511   Node->Children.emplace_back(std::move(LocNumberNode));
512   Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
513   auto LocFileNode = std::make_unique<TagNode>(
514       HTMLTag::TAG_A, llvm::sys::path::filename(FileURL));
515   LocFileNode->Attributes.emplace_back("href", std::string(FileURL));
516   Node->Children.emplace_back(std::move(LocFileNode));
517   return Node;
518 }
519 
520 static std::vector<std::unique_ptr<TagNode>>
521 genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList);
522 
523 // Generates a list of child nodes for the HTML head tag
524 // It contains a meta node, link nodes to import CSS files, and script nodes to
525 // import JS files
526 static std::vector<std::unique_ptr<TagNode>>
527 genFileHeadNodes(StringRef Title, StringRef InfoPath,
528                  const ClangDocContext &CDCtx) {
529   std::vector<std::unique_ptr<TagNode>> Out;
530   auto MetaNode = std::make_unique<TagNode>(HTMLTag::TAG_META);
531   MetaNode->Attributes.emplace_back("charset", "utf-8");
532   Out.emplace_back(std::move(MetaNode));
533   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_TITLE, Title));
534   std::vector<std::unique_ptr<TagNode>> StylesheetsNodes =
535       genStylesheetsHTML(InfoPath, CDCtx);
536   appendVector(std::move(StylesheetsNodes), Out);
537   std::vector<std::unique_ptr<TagNode>> JsNodes =
538       genJsScriptsHTML(InfoPath, CDCtx);
539   appendVector(std::move(JsNodes), Out);
540   return Out;
541 }
542 
543 // Generates a header HTML node that can be used for any file
544 // It contains the project name
545 static std::unique_ptr<TagNode> genFileHeaderNode(StringRef ProjectName) {
546   auto HeaderNode = std::make_unique<TagNode>(HTMLTag::TAG_HEADER, ProjectName);
547   HeaderNode->Attributes.emplace_back("id", "project-title");
548   return HeaderNode;
549 }
550 
551 // Generates a main HTML node that has all the main content of an info file
552 // It contains both indexes and the info's documented information
553 // This function should only be used for the info files (not for the file that
554 // only has the general index)
555 static std::unique_ptr<TagNode> genInfoFileMainNode(
556     StringRef InfoPath,
557     std::vector<std::unique_ptr<TagNode>> &MainContentInnerNodes,
558     const Index &InfoIndex) {
559   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
560 
561   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
562   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
563   LeftSidebarNode->Attributes.emplace_back("path", std::string(InfoPath));
564   LeftSidebarNode->Attributes.emplace_back(
565       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
566 
567   auto MainContentNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
568   MainContentNode->Attributes.emplace_back("id", "main-content");
569   MainContentNode->Attributes.emplace_back(
570       "class", "col-xs-12 col-sm-9 col-md-8 main-content");
571   appendVector(std::move(MainContentInnerNodes), MainContentNode->Children);
572 
573   auto RightSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
574   RightSidebarNode->Attributes.emplace_back("id", "sidebar-right");
575   RightSidebarNode->Attributes.emplace_back(
576       "class", "col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right");
577   std::vector<std::unique_ptr<TagNode>> InfoIndexHTML =
578       genHTML(InfoIndex, InfoPath, true);
579   appendVector(std::move(InfoIndexHTML), RightSidebarNode->Children);
580 
581   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
582   MainNode->Children.emplace_back(std::move(MainContentNode));
583   MainNode->Children.emplace_back(std::move(RightSidebarNode));
584 
585   return MainNode;
586 }
587 
588 // Generates a footer HTML node that can be used for any file
589 // It contains clang-doc's version
590 static std::unique_ptr<TagNode> genFileFooterNode() {
591   auto FooterNode = std::make_unique<TagNode>(HTMLTag::TAG_FOOTER);
592   auto SpanNode = std::make_unique<TagNode>(
593       HTMLTag::TAG_SPAN, clang::getClangToolFullVersion("clang-doc"));
594   SpanNode->Attributes.emplace_back("class", "no-break");
595   FooterNode->Children.emplace_back(std::move(SpanNode));
596   return FooterNode;
597 }
598 
599 // Generates a complete HTMLFile for an Info
600 static HTMLFile
601 genInfoFile(StringRef Title, StringRef InfoPath,
602             std::vector<std::unique_ptr<TagNode>> &MainContentNodes,
603             const Index &InfoIndex, const ClangDocContext &CDCtx) {
604   HTMLFile F;
605 
606   std::vector<std::unique_ptr<TagNode>> HeadNodes =
607       genFileHeadNodes(Title, InfoPath, CDCtx);
608   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
609   std::unique_ptr<TagNode> MainNode =
610       genInfoFileMainNode(InfoPath, MainContentNodes, InfoIndex);
611   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
612 
613   appendVector(std::move(HeadNodes), F.Children);
614   F.Children.emplace_back(std::move(HeaderNode));
615   F.Children.emplace_back(std::move(MainNode));
616   F.Children.emplace_back(std::move(FooterNode));
617 
618   return F;
619 }
620 
621 template <typename T,
622           typename = std::enable_if<std::is_base_of<T, Info>::value>>
623 static Index genInfoIndexItem(const std::vector<T> &Infos, StringRef Title) {
624   Index Idx(Title, Title);
625   for (const auto &C : Infos)
626     Idx.Children.emplace_back(C.extractName(),
627                               llvm::toHex(llvm::toStringRef(C.USR)));
628   return Idx;
629 }
630 
631 static std::vector<std::unique_ptr<TagNode>>
632 genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList) {
633   std::vector<std::unique_ptr<TagNode>> Out;
634   if (!Index.Name.empty()) {
635     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_SPAN));
636     auto &SpanBody = Out.back();
637     if (!Index.JumpToSection)
638       SpanBody->Children.emplace_back(genReference(Index, InfoPath));
639     else
640       SpanBody->Children.emplace_back(
641           genReference(Index, InfoPath, Index.JumpToSection->str()));
642   }
643   if (Index.Children.empty())
644     return Out;
645   // Only the outermost list should use ol, the others should use ul
646   HTMLTag ListHTMLTag = IsOutermostList ? HTMLTag::TAG_OL : HTMLTag::TAG_UL;
647   Out.emplace_back(std::make_unique<TagNode>(ListHTMLTag));
648   const auto &UlBody = Out.back();
649   for (const auto &C : Index.Children) {
650     auto LiBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
651     std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(C, InfoPath, false);
652     appendVector(std::move(Nodes), LiBody->Children);
653     UlBody->Children.emplace_back(std::move(LiBody));
654   }
655   return Out;
656 }
657 
658 static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
659   if (I.Kind == "FullComment") {
660     auto FullComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
661     for (const auto &Child : I.Children) {
662       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
663       if (Node)
664         FullComment->Children.emplace_back(std::move(Node));
665     }
666     return std::move(FullComment);
667   }
668 
669   if (I.Kind == "ParagraphComment") {
670     auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
671     for (const auto &Child : I.Children) {
672       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
673       if (Node)
674         ParagraphComment->Children.emplace_back(std::move(Node));
675     }
676     if (ParagraphComment->Children.empty())
677       return nullptr;
678     return std::move(ParagraphComment);
679   }
680 
681   if (I.Kind == "BlockCommandComment") {
682     auto BlockComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
683     BlockComment->Children.emplace_back(
684         std::make_unique<TagNode>(HTMLTag::TAG_DIV, I.Name));
685     for (const auto &Child : I.Children) {
686       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
687       if (Node)
688         BlockComment->Children.emplace_back(std::move(Node));
689     }
690     if (BlockComment->Children.empty())
691       return nullptr;
692     return std::move(BlockComment);
693   }
694   if (I.Kind == "TextComment") {
695     if (I.Text == "")
696       return nullptr;
697     return std::make_unique<TextNode>(I.Text);
698   }
699   return nullptr;
700 }
701 
702 static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) {
703   auto CommentBlock = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
704   for (const auto &Child : C) {
705     if (std::unique_ptr<HTMLNode> Node = genHTML(Child))
706       CommentBlock->Children.emplace_back(std::move(Node));
707   }
708   return CommentBlock;
709 }
710 
711 static std::vector<std::unique_ptr<TagNode>>
712 genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
713   std::vector<std::unique_ptr<TagNode>> Out;
714   std::string EnumType = I.Scoped ? "enum class " : "enum ";
715   // Determine if enum members have comments attached
716   bool HasComments = std::any_of(
717       I.Members.begin(), I.Members.end(),
718       [](const EnumValueInfo &M) { return !M.Description.empty(); });
719   std::unique_ptr<TagNode> Table =
720       std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
721   std::unique_ptr<TagNode> THead =
722       std::make_unique<TagNode>(HTMLTag::TAG_THEAD);
723   std::unique_ptr<TagNode> TRow = std::make_unique<TagNode>(HTMLTag::TAG_TR);
724   std::unique_ptr<TagNode> TD =
725       std::make_unique<TagNode>(HTMLTag::TAG_TH, EnumType + I.Name);
726   // Span 3 columns if enum has comments
727   TD->Attributes.emplace_back("colspan", HasComments ? "3" : "2");
728 
729   Table->Attributes.emplace_back("id", llvm::toHex(llvm::toStringRef(I.USR)));
730   TRow->Children.emplace_back(std::move(TD));
731   THead->Children.emplace_back(std::move(TRow));
732   Table->Children.emplace_back(std::move(THead));
733 
734   if (std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members))
735     Table->Children.emplace_back(std::move(Node));
736 
737   Out.emplace_back(std::move(Table));
738 
739   if (I.DefLoc) {
740     if (!CDCtx.RepositoryUrl)
741       Out.emplace_back(writeFileDefinition(*I.DefLoc));
742     else
743       Out.emplace_back(
744           writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
745   }
746 
747   std::string Description;
748   if (!I.Description.empty())
749     Out.emplace_back(genHTML(I.Description));
750 
751   return Out;
752 }
753 
754 static std::vector<std::unique_ptr<TagNode>>
755 genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
756         StringRef ParentInfoDir) {
757   std::vector<std::unique_ptr<TagNode>> Out;
758   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name));
759   // USR is used as id for functions instead of name to disambiguate function
760   // overloads.
761   Out.back()->Attributes.emplace_back("id",
762                                       llvm::toHex(llvm::toStringRef(I.USR)));
763 
764   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
765   auto &FunctionHeader = Out.back();
766 
767   std::string Access = getAccessSpelling(I.Access).str();
768   if (Access != "")
769     FunctionHeader->Children.emplace_back(
770         std::make_unique<TextNode>(Access + " "));
771   if (I.ReturnType.Type.Name != "") {
772     FunctionHeader->Children.emplace_back(
773         genReference(I.ReturnType.Type, ParentInfoDir));
774     FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(" "));
775   }
776   FunctionHeader->Children.emplace_back(
777       std::make_unique<TextNode>(I.Name + "("));
778 
779   for (const auto &P : I.Params) {
780     if (&P != I.Params.begin())
781       FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(", "));
782     FunctionHeader->Children.emplace_back(genReference(P.Type, ParentInfoDir));
783     FunctionHeader->Children.emplace_back(
784         std::make_unique<TextNode>(" " + P.Name));
785   }
786   FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(")"));
787 
788   if (I.DefLoc) {
789     if (!CDCtx.RepositoryUrl)
790       Out.emplace_back(writeFileDefinition(*I.DefLoc));
791     else
792       Out.emplace_back(writeFileDefinition(
793           *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
794   }
795 
796   std::string Description;
797   if (!I.Description.empty())
798     Out.emplace_back(genHTML(I.Description));
799 
800   return Out;
801 }
802 
803 static std::vector<std::unique_ptr<TagNode>>
804 genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
805         std::string &InfoTitle) {
806   std::vector<std::unique_ptr<TagNode>> Out;
807   if (I.Name.str() == "")
808     InfoTitle = "Global Namespace";
809   else
810     InfoTitle = ("namespace " + I.Name).str();
811 
812   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
813 
814   std::string Description;
815   if (!I.Description.empty())
816     Out.emplace_back(genHTML(I.Description));
817 
818   llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
819 
820   std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
821       genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath);
822   appendVector(std::move(ChildNamespaces), Out);
823   std::vector<std::unique_ptr<TagNode>> ChildRecords =
824       genReferencesBlock(I.Children.Records, "Records", BasePath);
825   appendVector(std::move(ChildRecords), Out);
826 
827   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
828       genFunctionsBlock(I.Children.Functions, CDCtx, BasePath);
829   appendVector(std::move(ChildFunctions), Out);
830   std::vector<std::unique_ptr<TagNode>> ChildEnums =
831       genEnumsBlock(I.Children.Enums, CDCtx);
832   appendVector(std::move(ChildEnums), Out);
833 
834   if (!I.Children.Namespaces.empty())
835     InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
836   if (!I.Children.Records.empty())
837     InfoIndex.Children.emplace_back("Records", "Records");
838   if (!I.Children.Functions.empty())
839     InfoIndex.Children.emplace_back(
840         genInfoIndexItem(I.Children.Functions, "Functions"));
841   if (!I.Children.Enums.empty())
842     InfoIndex.Children.emplace_back(
843         genInfoIndexItem(I.Children.Enums, "Enums"));
844 
845   return Out;
846 }
847 
848 static std::vector<std::unique_ptr<TagNode>>
849 genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
850         std::string &InfoTitle) {
851   std::vector<std::unique_ptr<TagNode>> Out;
852   InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
853   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
854 
855   if (I.DefLoc) {
856     if (!CDCtx.RepositoryUrl)
857       Out.emplace_back(writeFileDefinition(*I.DefLoc));
858     else
859       Out.emplace_back(writeFileDefinition(
860           *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
861   }
862 
863   std::string Description;
864   if (!I.Description.empty())
865     Out.emplace_back(genHTML(I.Description));
866 
867   std::vector<std::unique_ptr<HTMLNode>> Parents =
868       genReferenceList(I.Parents, I.Path);
869   std::vector<std::unique_ptr<HTMLNode>> VParents =
870       genReferenceList(I.VirtualParents, I.Path);
871   if (!Parents.empty() || !VParents.empty()) {
872     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
873     auto &PBody = Out.back();
874     PBody->Children.emplace_back(std::make_unique<TextNode>("Inherits from "));
875     if (Parents.empty())
876       appendVector(std::move(VParents), PBody->Children);
877     else if (VParents.empty())
878       appendVector(std::move(Parents), PBody->Children);
879     else {
880       appendVector(std::move(Parents), PBody->Children);
881       PBody->Children.emplace_back(std::make_unique<TextNode>(", "));
882       appendVector(std::move(VParents), PBody->Children);
883     }
884   }
885 
886   std::vector<std::unique_ptr<TagNode>> Members =
887       genRecordMembersBlock(I.Members, I.Path);
888   appendVector(std::move(Members), Out);
889   std::vector<std::unique_ptr<TagNode>> ChildRecords =
890       genReferencesBlock(I.Children.Records, "Records", I.Path);
891   appendVector(std::move(ChildRecords), Out);
892 
893   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
894       genFunctionsBlock(I.Children.Functions, CDCtx, I.Path);
895   appendVector(std::move(ChildFunctions), Out);
896   std::vector<std::unique_ptr<TagNode>> ChildEnums =
897       genEnumsBlock(I.Children.Enums, CDCtx);
898   appendVector(std::move(ChildEnums), Out);
899 
900   if (!I.Members.empty())
901     InfoIndex.Children.emplace_back("Members", "Members");
902   if (!I.Children.Records.empty())
903     InfoIndex.Children.emplace_back("Records", "Records");
904   if (!I.Children.Functions.empty())
905     InfoIndex.Children.emplace_back(
906         genInfoIndexItem(I.Children.Functions, "Functions"));
907   if (!I.Children.Enums.empty())
908     InfoIndex.Children.emplace_back(
909         genInfoIndexItem(I.Children.Enums, "Enums"));
910 
911   return Out;
912 }
913 
914 static std::vector<std::unique_ptr<TagNode>>
915 genHTML(const TypedefInfo &I, const ClangDocContext &CDCtx,
916         std::string &InfoTitle) {
917   // TODO support typedefs in HTML.
918   return {};
919 }
920 
921 /// Generator for HTML documentation.
922 class HTMLGenerator : public Generator {
923 public:
924   static const char *Format;
925 
926   llvm::Error generateDocs(StringRef RootDir,
927                            llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
928                            const ClangDocContext &CDCtx) override;
929   llvm::Error createResources(ClangDocContext &CDCtx) override;
930   llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
931                                  const ClangDocContext &CDCtx) override;
932 };
933 
934 const char *HTMLGenerator::Format = "html";
935 
936 llvm::Error
937 HTMLGenerator::generateDocs(StringRef RootDir,
938                             llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
939                             const ClangDocContext &CDCtx) {
940   // Track which directories we already tried to create.
941   llvm::StringSet<> CreatedDirs;
942 
943   // Collect all output by file name and create the nexessary directories.
944   llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
945   for (const auto &Group : Infos) {
946     doc::Info *Info = Group.getValue().get();
947 
948     llvm::SmallString<128> Path;
949     llvm::sys::path::native(RootDir, Path);
950     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
951     if (!CreatedDirs.contains(Path)) {
952       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
953           Err != std::error_code()) {
954         return llvm::createStringError(Err, "Failed to create directory '%s'.",
955                                        Path.c_str());
956       }
957       CreatedDirs.insert(Path);
958     }
959 
960     llvm::sys::path::append(Path, Info->getFileBaseName() + ".html");
961     FileToInfos[Path].push_back(Info);
962   }
963 
964   for (const auto &Group : FileToInfos) {
965     std::error_code FileErr;
966     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
967                                 llvm::sys::fs::OF_None);
968     if (FileErr) {
969       return llvm::createStringError(FileErr, "Error opening file '%s'",
970                                      Group.getKey().str().c_str());
971     }
972 
973     // TODO: https://github.com/llvm/llvm-project/issues/59073
974     // If there are multiple Infos for this file name (for example, template
975     // specializations), this will generate multiple complete web pages (with
976     // <DOCTYPE> and <title>, etc.) concatenated together. This generator needs
977     // some refactoring to be able to output the headers separately from the
978     // contents.
979     for (const auto &Info : Group.getValue()) {
980       if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
981         return Err;
982       }
983     }
984   }
985 
986   return llvm::Error::success();
987 }
988 
989 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
990                                               const ClangDocContext &CDCtx) {
991   std::string InfoTitle;
992   std::vector<std::unique_ptr<TagNode>> MainContentNodes;
993   Index InfoIndex;
994   switch (I->IT) {
995   case InfoType::IT_namespace:
996     MainContentNodes = genHTML(*static_cast<clang::doc::NamespaceInfo *>(I),
997                                InfoIndex, CDCtx, InfoTitle);
998     break;
999   case InfoType::IT_record:
1000     MainContentNodes = genHTML(*static_cast<clang::doc::RecordInfo *>(I),
1001                                InfoIndex, CDCtx, InfoTitle);
1002     break;
1003   case InfoType::IT_enum:
1004     MainContentNodes = genHTML(*static_cast<clang::doc::EnumInfo *>(I), CDCtx);
1005     break;
1006   case InfoType::IT_function:
1007     MainContentNodes =
1008         genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
1009     break;
1010   case InfoType::IT_typedef:
1011     MainContentNodes =
1012         genHTML(*static_cast<clang::doc::TypedefInfo *>(I), CDCtx, InfoTitle);
1013     break;
1014   case InfoType::IT_default:
1015     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1016                                    "unexpected info type");
1017   }
1018 
1019   HTMLFile F = genInfoFile(InfoTitle, I->getRelativeFilePath(""),
1020                            MainContentNodes, InfoIndex, CDCtx);
1021   F.render(OS);
1022 
1023   return llvm::Error::success();
1024 }
1025 
1026 static std::string getRefType(InfoType IT) {
1027   switch (IT) {
1028   case InfoType::IT_default:
1029     return "default";
1030   case InfoType::IT_namespace:
1031     return "namespace";
1032   case InfoType::IT_record:
1033     return "record";
1034   case InfoType::IT_function:
1035     return "function";
1036   case InfoType::IT_enum:
1037     return "enum";
1038   case InfoType::IT_typedef:
1039     return "typedef";
1040   }
1041   llvm_unreachable("Unknown InfoType");
1042 }
1043 
1044 static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
1045   std::error_code OK;
1046   std::error_code FileErr;
1047   llvm::SmallString<128> FilePath;
1048   llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
1049   llvm::sys::path::append(FilePath, "index_json.js");
1050   llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
1051   if (FileErr != OK) {
1052     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1053                                    "error creating index file: " +
1054                                        FileErr.message());
1055   }
1056   llvm::SmallString<128> RootPath(CDCtx.OutDirectory);
1057   if (llvm::sys::path::is_relative(RootPath)) {
1058     llvm::sys::fs::make_absolute(RootPath);
1059   }
1060   // Replace the escaped characters with a forward slash. It shouldn't matter
1061   // when rendering the webpage in a web browser. This helps to prevent the
1062   // JavaScript from escaping characters incorrectly, and introducing  bad paths
1063   // in the URLs.
1064   std::string RootPathEscaped = RootPath.str().str();
1065   std::replace(RootPathEscaped.begin(), RootPathEscaped.end(), '\\', '/');
1066   OS << "var RootPath = \"" << RootPathEscaped << "\";\n";
1067 
1068   CDCtx.Idx.sort();
1069   llvm::json::OStream J(OS, 2);
1070   std::function<void(Index)> IndexToJSON = [&](const Index &I) {
1071     J.object([&] {
1072       J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
1073       J.attribute("Name", I.Name);
1074       J.attribute("RefType", getRefType(I.RefType));
1075       J.attribute("Path", I.getRelativeFilePath(""));
1076       J.attributeArray("Children", [&] {
1077         for (const Index &C : I.Children)
1078           IndexToJSON(C);
1079       });
1080     });
1081   };
1082   OS << "async function LoadIndex() {\nreturn";
1083   IndexToJSON(CDCtx.Idx);
1084   OS << ";\n}";
1085   return llvm::Error::success();
1086 }
1087 
1088 // Generates a main HTML node that has the main content of the file that shows
1089 // only the general index
1090 // It contains the general index with links to all the generated files
1091 static std::unique_ptr<TagNode> genIndexFileMainNode() {
1092   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
1093 
1094   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
1095   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
1096   LeftSidebarNode->Attributes.emplace_back("path", "");
1097   LeftSidebarNode->Attributes.emplace_back(
1098       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
1099   LeftSidebarNode->Attributes.emplace_back("style", "flex: 0 100%;");
1100 
1101   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
1102 
1103   return MainNode;
1104 }
1105 
1106 static llvm::Error genIndex(const ClangDocContext &CDCtx) {
1107   std::error_code FileErr, OK;
1108   llvm::SmallString<128> IndexPath;
1109   llvm::sys::path::native(CDCtx.OutDirectory, IndexPath);
1110   llvm::sys::path::append(IndexPath, "index.html");
1111   llvm::raw_fd_ostream IndexOS(IndexPath, FileErr, llvm::sys::fs::OF_None);
1112   if (FileErr != OK) {
1113     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1114                                    "error creating main index: " +
1115                                        FileErr.message());
1116   }
1117 
1118   HTMLFile F;
1119 
1120   std::vector<std::unique_ptr<TagNode>> HeadNodes =
1121       genFileHeadNodes("Index", "", CDCtx);
1122   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
1123   std::unique_ptr<TagNode> MainNode = genIndexFileMainNode();
1124   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
1125 
1126   appendVector(std::move(HeadNodes), F.Children);
1127   F.Children.emplace_back(std::move(HeaderNode));
1128   F.Children.emplace_back(std::move(MainNode));
1129   F.Children.emplace_back(std::move(FooterNode));
1130 
1131   F.render(IndexOS);
1132 
1133   return llvm::Error::success();
1134 }
1135 
1136 static llvm::Error copyFile(StringRef FilePath, StringRef OutDirectory) {
1137   llvm::SmallString<128> PathWrite;
1138   llvm::sys::path::native(OutDirectory, PathWrite);
1139   llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
1140   llvm::SmallString<128> PathRead;
1141   llvm::sys::path::native(FilePath, PathRead);
1142   std::error_code OK;
1143   std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
1144   if (FileErr != OK) {
1145     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1146                                    "error creating file " +
1147                                        llvm::sys::path::filename(FilePath) +
1148                                        ": " + FileErr.message() + "\n");
1149   }
1150   return llvm::Error::success();
1151 }
1152 
1153 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
1154   auto Err = serializeIndex(CDCtx);
1155   if (Err)
1156     return Err;
1157   Err = genIndex(CDCtx);
1158   if (Err)
1159     return Err;
1160 
1161   for (const auto &FilePath : CDCtx.UserStylesheets) {
1162     Err = copyFile(FilePath, CDCtx.OutDirectory);
1163     if (Err)
1164       return Err;
1165   }
1166   for (const auto &FilePath : CDCtx.JsScripts) {
1167     Err = copyFile(FilePath, CDCtx.OutDirectory);
1168     if (Err)
1169       return Err;
1170   }
1171   return llvm::Error::success();
1172 }
1173 
1174 static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format,
1175                                                   "Generator for HTML output.");
1176 
1177 // This anchor is used to force the linker to link in the generated object
1178 // file and thus register the generator.
1179 volatile int HTMLGeneratorAnchorSource = 0;
1180 
1181 } // namespace doc
1182 } // namespace clang
1183