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