xref: /llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp (revision 15aa965363df5cf3a021b3841bcafbced3756ea2)
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   llvm::SmallString<16> 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 llvm::SmallString<16> HTMLTag::ToString() const {
141   switch (Value) {
142   case HTMLTag::TAG_A:
143     return llvm::SmallString<16>("a");
144   case HTMLTag::TAG_DIV:
145     return llvm::SmallString<16>("div");
146   case HTMLTag::TAG_FOOTER:
147     return llvm::SmallString<16>("footer");
148   case HTMLTag::TAG_H1:
149     return llvm::SmallString<16>("h1");
150   case HTMLTag::TAG_H2:
151     return llvm::SmallString<16>("h2");
152   case HTMLTag::TAG_H3:
153     return llvm::SmallString<16>("h3");
154   case HTMLTag::TAG_HEADER:
155     return llvm::SmallString<16>("header");
156   case HTMLTag::TAG_LI:
157     return llvm::SmallString<16>("li");
158   case HTMLTag::TAG_LINK:
159     return llvm::SmallString<16>("link");
160   case HTMLTag::TAG_MAIN:
161     return llvm::SmallString<16>("main");
162   case HTMLTag::TAG_META:
163     return llvm::SmallString<16>("meta");
164   case HTMLTag::TAG_OL:
165     return llvm::SmallString<16>("ol");
166   case HTMLTag::TAG_P:
167     return llvm::SmallString<16>("p");
168   case HTMLTag::TAG_SCRIPT:
169     return llvm::SmallString<16>("script");
170   case HTMLTag::TAG_SPAN:
171     return llvm::SmallString<16>("span");
172   case HTMLTag::TAG_TITLE:
173     return llvm::SmallString<16>("title");
174   case HTMLTag::TAG_UL:
175     return llvm::SmallString<16>("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.str()));
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   for (const auto &FilePath : CDCtx.JsScripts) {
293     auto ScriptNode = std::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
294     SmallString<128> ScriptPath = computeRelativePath("", InfoPath);
295     llvm::sys::path::append(ScriptPath, llvm::sys::path::filename(FilePath));
296     // Paths in HTML must be in posix-style
297     llvm::sys::path::native(ScriptPath, llvm::sys::path::Style::posix);
298     ScriptNode->Attributes.emplace_back("src", std::string(ScriptPath.str()));
299     Out.emplace_back(std::move(ScriptNode));
300   }
301   return Out;
302 }
303 
304 static std::unique_ptr<TagNode> genLink(const Twine &Text, const Twine &Link) {
305   auto LinkNode = std::make_unique<TagNode>(HTMLTag::TAG_A, Text);
306   LinkNode->Attributes.emplace_back("href", Link.str());
307   return LinkNode;
308 }
309 
310 static std::unique_ptr<HTMLNode>
311 genReference(const Reference &Type, StringRef CurrentDirectory,
312              std::optional<StringRef> JumpToSection = std::nullopt) {
313   if (Type.Path.empty()) {
314     if (!JumpToSection)
315       return std::make_unique<TextNode>(Type.Name);
316     else
317       return genLink(Type.Name, "#" + *JumpToSection);
318   }
319   llvm::SmallString<64> Path = Type.getRelativeFilePath(CurrentDirectory);
320   llvm::sys::path::append(Path, Type.getFileBaseName() + ".html");
321 
322   // Paths in HTML must be in posix-style
323   llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
324   if (JumpToSection)
325     Path += ("#" + *JumpToSection).str();
326   return genLink(Type.Name, Path);
327 }
328 
329 static std::vector<std::unique_ptr<HTMLNode>>
330 genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs,
331                  const StringRef &CurrentDirectory) {
332   std::vector<std::unique_ptr<HTMLNode>> Out;
333   for (const auto &R : Refs) {
334     if (&R != Refs.begin())
335       Out.emplace_back(std::make_unique<TextNode>(", "));
336     Out.emplace_back(genReference(R, CurrentDirectory));
337   }
338   return Out;
339 }
340 
341 static std::vector<std::unique_ptr<TagNode>>
342 genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
343 static std::vector<std::unique_ptr<TagNode>>
344 genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
345         StringRef ParentInfoDir);
346 
347 static std::vector<std::unique_ptr<TagNode>>
348 genEnumsBlock(const std::vector<EnumInfo> &Enums,
349               const ClangDocContext &CDCtx) {
350   if (Enums.empty())
351     return {};
352 
353   std::vector<std::unique_ptr<TagNode>> Out;
354   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Enums"));
355   Out.back()->Attributes.emplace_back("id", "Enums");
356   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
357   auto &DivBody = Out.back();
358   for (const auto &E : Enums) {
359     std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E, CDCtx);
360     AppendVector(std::move(Nodes), DivBody->Children);
361   }
362   return Out;
363 }
364 
365 static std::unique_ptr<TagNode>
366 genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
367   if (Members.empty())
368     return nullptr;
369 
370   auto List = std::make_unique<TagNode>(HTMLTag::TAG_UL);
371   for (const auto &M : Members)
372     List->Children.emplace_back(
373         std::make_unique<TagNode>(HTMLTag::TAG_LI, M.Name));
374   return List;
375 }
376 
377 static std::vector<std::unique_ptr<TagNode>>
378 genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
379                   const ClangDocContext &CDCtx, StringRef ParentInfoDir) {
380   if (Functions.empty())
381     return {};
382 
383   std::vector<std::unique_ptr<TagNode>> Out;
384   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions"));
385   Out.back()->Attributes.emplace_back("id", "Functions");
386   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
387   auto &DivBody = Out.back();
388   for (const auto &F : Functions) {
389     std::vector<std::unique_ptr<TagNode>> Nodes =
390         genHTML(F, CDCtx, ParentInfoDir);
391     AppendVector(std::move(Nodes), DivBody->Children);
392   }
393   return Out;
394 }
395 
396 static std::vector<std::unique_ptr<TagNode>>
397 genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
398                       StringRef ParentInfoDir) {
399   if (Members.empty())
400     return {};
401 
402   std::vector<std::unique_ptr<TagNode>> Out;
403   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Members"));
404   Out.back()->Attributes.emplace_back("id", "Members");
405   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
406   auto &ULBody = Out.back();
407   for (const auto &M : Members) {
408     std::string Access = getAccessSpelling(M.Access).str();
409     if (Access != "")
410       Access = Access + " ";
411     auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
412     LIBody->Children.emplace_back(std::make_unique<TextNode>(Access));
413     LIBody->Children.emplace_back(genReference(M.Type, ParentInfoDir));
414     LIBody->Children.emplace_back(std::make_unique<TextNode>(" " + M.Name));
415     ULBody->Children.emplace_back(std::move(LIBody));
416   }
417   return Out;
418 }
419 
420 static std::vector<std::unique_ptr<TagNode>>
421 genReferencesBlock(const std::vector<Reference> &References,
422                    llvm::StringRef Title, StringRef ParentPath) {
423   if (References.empty())
424     return {};
425 
426   std::vector<std::unique_ptr<TagNode>> Out;
427   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, Title));
428   Out.back()->Attributes.emplace_back("id", std::string(Title));
429   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
430   auto &ULBody = Out.back();
431   for (const auto &R : References) {
432     auto LiNode = std::make_unique<TagNode>(HTMLTag::TAG_LI);
433     LiNode->Children.emplace_back(genReference(R, ParentPath));
434     ULBody->Children.emplace_back(std::move(LiNode));
435   }
436   return Out;
437 }
438 
439 static std::unique_ptr<TagNode>
440 writeFileDefinition(const Location &L,
441                     std::optional<StringRef> RepositoryUrl = std::nullopt) {
442   if (!L.IsFileInRootDir || !RepositoryUrl)
443     return std::make_unique<TagNode>(
444         HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
445                             " of file " + L.Filename);
446   SmallString<128> FileURL(*RepositoryUrl);
447   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
448   auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
449   Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
450   auto LocNumberNode =
451       std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
452   // The links to a specific line in the source code use the github /
453   // googlesource notation so it won't work for all hosting pages.
454   LocNumberNode->Attributes.emplace_back(
455       "href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
456   Node->Children.emplace_back(std::move(LocNumberNode));
457   Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
458   auto LocFileNode = std::make_unique<TagNode>(
459       HTMLTag::TAG_A, llvm::sys::path::filename(FileURL));
460   LocFileNode->Attributes.emplace_back("href", std::string(FileURL.str()));
461   Node->Children.emplace_back(std::move(LocFileNode));
462   return Node;
463 }
464 
465 static std::vector<std::unique_ptr<TagNode>>
466 genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList);
467 
468 // Generates a list of child nodes for the HTML head tag
469 // It contains a meta node, link nodes to import CSS files, and script nodes to
470 // import JS files
471 static std::vector<std::unique_ptr<TagNode>>
472 genFileHeadNodes(StringRef Title, StringRef InfoPath,
473                  const ClangDocContext &CDCtx) {
474   std::vector<std::unique_ptr<TagNode>> Out;
475   auto MetaNode = std::make_unique<TagNode>(HTMLTag::TAG_META);
476   MetaNode->Attributes.emplace_back("charset", "utf-8");
477   Out.emplace_back(std::move(MetaNode));
478   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_TITLE, Title));
479   std::vector<std::unique_ptr<TagNode>> StylesheetsNodes =
480       genStylesheetsHTML(InfoPath, CDCtx);
481   AppendVector(std::move(StylesheetsNodes), Out);
482   std::vector<std::unique_ptr<TagNode>> JsNodes =
483       genJsScriptsHTML(InfoPath, CDCtx);
484   AppendVector(std::move(JsNodes), Out);
485   return Out;
486 }
487 
488 // Generates a header HTML node that can be used for any file
489 // It contains the project name
490 static std::unique_ptr<TagNode> genFileHeaderNode(StringRef ProjectName) {
491   auto HeaderNode = std::make_unique<TagNode>(HTMLTag::TAG_HEADER, ProjectName);
492   HeaderNode->Attributes.emplace_back("id", "project-title");
493   return HeaderNode;
494 }
495 
496 // Generates a main HTML node that has all the main content of an info file
497 // It contains both indexes and the info's documented information
498 // This function should only be used for the info files (not for the file that
499 // only has the general index)
500 static std::unique_ptr<TagNode> genInfoFileMainNode(
501     StringRef InfoPath,
502     std::vector<std::unique_ptr<TagNode>> &MainContentInnerNodes,
503     const Index &InfoIndex) {
504   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
505 
506   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
507   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
508   LeftSidebarNode->Attributes.emplace_back("path", std::string(InfoPath));
509   LeftSidebarNode->Attributes.emplace_back(
510       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
511 
512   auto MainContentNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
513   MainContentNode->Attributes.emplace_back("id", "main-content");
514   MainContentNode->Attributes.emplace_back(
515       "class", "col-xs-12 col-sm-9 col-md-8 main-content");
516   AppendVector(std::move(MainContentInnerNodes), MainContentNode->Children);
517 
518   auto RightSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
519   RightSidebarNode->Attributes.emplace_back("id", "sidebar-right");
520   RightSidebarNode->Attributes.emplace_back(
521       "class", "col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right");
522   std::vector<std::unique_ptr<TagNode>> InfoIndexHTML =
523       genHTML(InfoIndex, InfoPath, true);
524   AppendVector(std::move(InfoIndexHTML), RightSidebarNode->Children);
525 
526   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
527   MainNode->Children.emplace_back(std::move(MainContentNode));
528   MainNode->Children.emplace_back(std::move(RightSidebarNode));
529 
530   return MainNode;
531 }
532 
533 // Generates a footer HTML node that can be used for any file
534 // It contains clang-doc's version
535 static std::unique_ptr<TagNode> genFileFooterNode() {
536   auto FooterNode = std::make_unique<TagNode>(HTMLTag::TAG_FOOTER);
537   auto SpanNode = std::make_unique<TagNode>(
538       HTMLTag::TAG_SPAN, clang::getClangToolFullVersion("clang-doc"));
539   SpanNode->Attributes.emplace_back("class", "no-break");
540   FooterNode->Children.emplace_back(std::move(SpanNode));
541   return FooterNode;
542 }
543 
544 // Generates a complete HTMLFile for an Info
545 static HTMLFile
546 genInfoFile(StringRef Title, StringRef InfoPath,
547             std::vector<std::unique_ptr<TagNode>> &MainContentNodes,
548             const Index &InfoIndex, const ClangDocContext &CDCtx) {
549   HTMLFile F;
550 
551   std::vector<std::unique_ptr<TagNode>> HeadNodes =
552       genFileHeadNodes(Title, InfoPath, CDCtx);
553   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
554   std::unique_ptr<TagNode> MainNode =
555       genInfoFileMainNode(InfoPath, MainContentNodes, InfoIndex);
556   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
557 
558   AppendVector(std::move(HeadNodes), F.Children);
559   F.Children.emplace_back(std::move(HeaderNode));
560   F.Children.emplace_back(std::move(MainNode));
561   F.Children.emplace_back(std::move(FooterNode));
562 
563   return F;
564 }
565 
566 template <typename T,
567           typename = std::enable_if<std::is_base_of<T, Info>::value>>
568 static Index genInfoIndexItem(const std::vector<T> &Infos, StringRef Title) {
569   Index Idx(Title, Title);
570   for (const auto &C : Infos)
571     Idx.Children.emplace_back(C.extractName(),
572                               llvm::toHex(llvm::toStringRef(C.USR)));
573   return Idx;
574 }
575 
576 static std::vector<std::unique_ptr<TagNode>>
577 genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList) {
578   std::vector<std::unique_ptr<TagNode>> Out;
579   if (!Index.Name.empty()) {
580     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_SPAN));
581     auto &SpanBody = Out.back();
582     if (!Index.JumpToSection)
583       SpanBody->Children.emplace_back(genReference(Index, InfoPath));
584     else
585       SpanBody->Children.emplace_back(
586           genReference(Index, InfoPath, Index.JumpToSection->str()));
587   }
588   if (Index.Children.empty())
589     return Out;
590   // Only the outermost list should use ol, the others should use ul
591   HTMLTag ListHTMLTag = IsOutermostList ? HTMLTag::TAG_OL : HTMLTag::TAG_UL;
592   Out.emplace_back(std::make_unique<TagNode>(ListHTMLTag));
593   const auto &UlBody = Out.back();
594   for (const auto &C : Index.Children) {
595     auto LiBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
596     std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(C, InfoPath, false);
597     AppendVector(std::move(Nodes), LiBody->Children);
598     UlBody->Children.emplace_back(std::move(LiBody));
599   }
600   return Out;
601 }
602 
603 static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
604   if (I.Kind == "FullComment") {
605     auto FullComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
606     for (const auto &Child : I.Children) {
607       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
608       if (Node)
609         FullComment->Children.emplace_back(std::move(Node));
610     }
611     return std::move(FullComment);
612   } else if (I.Kind == "ParagraphComment") {
613     auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
614     for (const auto &Child : I.Children) {
615       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
616       if (Node)
617         ParagraphComment->Children.emplace_back(std::move(Node));
618     }
619     if (ParagraphComment->Children.empty())
620       return nullptr;
621     return std::move(ParagraphComment);
622   } else if (I.Kind == "TextComment") {
623     if (I.Text == "")
624       return nullptr;
625     return std::make_unique<TextNode>(I.Text);
626   }
627   return nullptr;
628 }
629 
630 static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) {
631   auto CommentBlock = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
632   for (const auto &Child : C) {
633     if (std::unique_ptr<HTMLNode> Node = genHTML(Child))
634       CommentBlock->Children.emplace_back(std::move(Node));
635   }
636   return CommentBlock;
637 }
638 
639 static std::vector<std::unique_ptr<TagNode>>
640 genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
641   std::vector<std::unique_ptr<TagNode>> Out;
642   std::string EnumType;
643   if (I.Scoped)
644     EnumType = "enum class ";
645   else
646     EnumType = "enum ";
647 
648   Out.emplace_back(
649       std::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name));
650   Out.back()->Attributes.emplace_back("id",
651                                       llvm::toHex(llvm::toStringRef(I.USR)));
652 
653   std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
654   if (Node)
655     Out.emplace_back(std::move(Node));
656 
657   if (I.DefLoc) {
658     if (!CDCtx.RepositoryUrl)
659       Out.emplace_back(writeFileDefinition(*I.DefLoc));
660     else
661       Out.emplace_back(writeFileDefinition(
662           *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
663   }
664 
665   std::string Description;
666   if (!I.Description.empty())
667     Out.emplace_back(genHTML(I.Description));
668 
669   return Out;
670 }
671 
672 static std::vector<std::unique_ptr<TagNode>>
673 genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
674         StringRef ParentInfoDir) {
675   std::vector<std::unique_ptr<TagNode>> Out;
676   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name));
677   // USR is used as id for functions instead of name to disambiguate function
678   // overloads.
679   Out.back()->Attributes.emplace_back("id",
680                                       llvm::toHex(llvm::toStringRef(I.USR)));
681 
682   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
683   auto &FunctionHeader = Out.back();
684 
685   std::string Access = getAccessSpelling(I.Access).str();
686   if (Access != "")
687     FunctionHeader->Children.emplace_back(
688         std::make_unique<TextNode>(Access + " "));
689   if (I.ReturnType.Type.Name != "") {
690     FunctionHeader->Children.emplace_back(
691         genReference(I.ReturnType.Type, ParentInfoDir));
692     FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(" "));
693   }
694   FunctionHeader->Children.emplace_back(
695       std::make_unique<TextNode>(I.Name + "("));
696 
697   for (const auto &P : I.Params) {
698     if (&P != I.Params.begin())
699       FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(", "));
700     FunctionHeader->Children.emplace_back(genReference(P.Type, ParentInfoDir));
701     FunctionHeader->Children.emplace_back(
702         std::make_unique<TextNode>(" " + P.Name));
703   }
704   FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(")"));
705 
706   if (I.DefLoc) {
707     if (!CDCtx.RepositoryUrl)
708       Out.emplace_back(writeFileDefinition(*I.DefLoc));
709     else
710       Out.emplace_back(writeFileDefinition(
711           *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
712   }
713 
714   std::string Description;
715   if (!I.Description.empty())
716     Out.emplace_back(genHTML(I.Description));
717 
718   return Out;
719 }
720 
721 static std::vector<std::unique_ptr<TagNode>>
722 genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
723         std::string &InfoTitle) {
724   std::vector<std::unique_ptr<TagNode>> Out;
725   if (I.Name.str() == "")
726     InfoTitle = "Global Namespace";
727   else
728     InfoTitle = ("namespace " + I.Name).str();
729 
730   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
731 
732   std::string Description;
733   if (!I.Description.empty())
734     Out.emplace_back(genHTML(I.Description));
735 
736   llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
737 
738   std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
739       genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath);
740   AppendVector(std::move(ChildNamespaces), Out);
741   std::vector<std::unique_ptr<TagNode>> ChildRecords =
742       genReferencesBlock(I.Children.Records, "Records", BasePath);
743   AppendVector(std::move(ChildRecords), Out);
744 
745   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
746       genFunctionsBlock(I.Children.Functions, CDCtx, BasePath);
747   AppendVector(std::move(ChildFunctions), Out);
748   std::vector<std::unique_ptr<TagNode>> ChildEnums =
749       genEnumsBlock(I.Children.Enums, CDCtx);
750   AppendVector(std::move(ChildEnums), Out);
751 
752   if (!I.Children.Namespaces.empty())
753     InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
754   if (!I.Children.Records.empty())
755     InfoIndex.Children.emplace_back("Records", "Records");
756   if (!I.Children.Functions.empty())
757     InfoIndex.Children.emplace_back(
758         genInfoIndexItem(I.Children.Functions, "Functions"));
759   if (!I.Children.Enums.empty())
760     InfoIndex.Children.emplace_back(
761         genInfoIndexItem(I.Children.Enums, "Enums"));
762 
763   return Out;
764 }
765 
766 static std::vector<std::unique_ptr<TagNode>>
767 genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
768         std::string &InfoTitle) {
769   std::vector<std::unique_ptr<TagNode>> Out;
770   InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
771   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
772 
773   if (I.DefLoc) {
774     if (!CDCtx.RepositoryUrl)
775       Out.emplace_back(writeFileDefinition(*I.DefLoc));
776     else
777       Out.emplace_back(writeFileDefinition(
778           *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
779   }
780 
781   std::string Description;
782   if (!I.Description.empty())
783     Out.emplace_back(genHTML(I.Description));
784 
785   std::vector<std::unique_ptr<HTMLNode>> Parents =
786       genReferenceList(I.Parents, I.Path);
787   std::vector<std::unique_ptr<HTMLNode>> VParents =
788       genReferenceList(I.VirtualParents, I.Path);
789   if (!Parents.empty() || !VParents.empty()) {
790     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
791     auto &PBody = Out.back();
792     PBody->Children.emplace_back(std::make_unique<TextNode>("Inherits from "));
793     if (Parents.empty())
794       AppendVector(std::move(VParents), PBody->Children);
795     else if (VParents.empty())
796       AppendVector(std::move(Parents), PBody->Children);
797     else {
798       AppendVector(std::move(Parents), PBody->Children);
799       PBody->Children.emplace_back(std::make_unique<TextNode>(", "));
800       AppendVector(std::move(VParents), PBody->Children);
801     }
802   }
803 
804   std::vector<std::unique_ptr<TagNode>> Members =
805       genRecordMembersBlock(I.Members, I.Path);
806   AppendVector(std::move(Members), Out);
807   std::vector<std::unique_ptr<TagNode>> ChildRecords =
808       genReferencesBlock(I.Children.Records, "Records", I.Path);
809   AppendVector(std::move(ChildRecords), Out);
810 
811   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
812       genFunctionsBlock(I.Children.Functions, CDCtx, I.Path);
813   AppendVector(std::move(ChildFunctions), Out);
814   std::vector<std::unique_ptr<TagNode>> ChildEnums =
815       genEnumsBlock(I.Children.Enums, CDCtx);
816   AppendVector(std::move(ChildEnums), Out);
817 
818   if (!I.Members.empty())
819     InfoIndex.Children.emplace_back("Members", "Members");
820   if (!I.Children.Records.empty())
821     InfoIndex.Children.emplace_back("Records", "Records");
822   if (!I.Children.Functions.empty())
823     InfoIndex.Children.emplace_back(
824         genInfoIndexItem(I.Children.Functions, "Functions"));
825   if (!I.Children.Enums.empty())
826     InfoIndex.Children.emplace_back(
827         genInfoIndexItem(I.Children.Enums, "Enums"));
828 
829   return Out;
830 }
831 
832 static std::vector<std::unique_ptr<TagNode>>
833 genHTML(const TypedefInfo &I, const ClangDocContext &CDCtx,
834         std::string &InfoTitle) {
835   // TODO support typedefs in HTML.
836   return {};
837 }
838 
839 /// Generator for HTML documentation.
840 class HTMLGenerator : public Generator {
841 public:
842   static const char *Format;
843 
844   llvm::Error generateDocs(StringRef RootDir,
845                            llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
846                            const ClangDocContext &CDCtx) override;
847   llvm::Error createResources(ClangDocContext &CDCtx) override;
848   llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
849                                  const ClangDocContext &CDCtx) override;
850 };
851 
852 const char *HTMLGenerator::Format = "html";
853 
854 llvm::Error
855 HTMLGenerator::generateDocs(StringRef RootDir,
856                             llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
857                             const ClangDocContext &CDCtx) {
858   // Track which directories we already tried to create.
859   llvm::StringSet<> CreatedDirs;
860 
861   // Collect all output by file name and create the nexessary directories.
862   llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
863   for (const auto &Group : Infos) {
864     doc::Info *Info = Group.getValue().get();
865 
866     llvm::SmallString<128> Path;
867     llvm::sys::path::native(RootDir, Path);
868     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
869     if (!CreatedDirs.contains(Path)) {
870       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
871           Err != std::error_code()) {
872         return llvm::createStringError(Err, "Failed to create directory '%s'.",
873                                        Path.c_str());
874       }
875       CreatedDirs.insert(Path);
876     }
877 
878     llvm::sys::path::append(Path, Info->getFileBaseName() + ".html");
879     FileToInfos[Path].push_back(Info);
880   }
881 
882   for (const auto &Group : FileToInfos) {
883     std::error_code FileErr;
884     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
885                                 llvm::sys::fs::OF_None);
886     if (FileErr) {
887       return llvm::createStringError(FileErr, "Error opening file '%s'",
888                                      Group.getKey().str().c_str());
889     }
890 
891     // TODO: https://github.com/llvm/llvm-project/issues/59073
892     // If there are multiple Infos for this file name (for example, template
893     // specializations), this will generate multiple complete web pages (with
894     // <DOCTYPE> and <title>, etc.) concatenated together. This generator needs
895     // some refactoring to be able to output the headers separately from the
896     // contents.
897     for (const auto &Info : Group.getValue()) {
898       if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
899         return Err;
900       }
901     }
902   }
903 
904   return llvm::Error::success();
905 }
906 
907 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
908                                               const ClangDocContext &CDCtx) {
909   std::string InfoTitle;
910   std::vector<std::unique_ptr<TagNode>> MainContentNodes;
911   Index InfoIndex;
912   switch (I->IT) {
913   case InfoType::IT_namespace:
914     MainContentNodes = genHTML(*static_cast<clang::doc::NamespaceInfo *>(I),
915                                InfoIndex, CDCtx, InfoTitle);
916     break;
917   case InfoType::IT_record:
918     MainContentNodes = genHTML(*static_cast<clang::doc::RecordInfo *>(I),
919                                InfoIndex, CDCtx, InfoTitle);
920     break;
921   case InfoType::IT_enum:
922     MainContentNodes = genHTML(*static_cast<clang::doc::EnumInfo *>(I), CDCtx);
923     break;
924   case InfoType::IT_function:
925     MainContentNodes =
926         genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
927     break;
928   case InfoType::IT_typedef:
929     MainContentNodes =
930         genHTML(*static_cast<clang::doc::TypedefInfo *>(I), CDCtx, InfoTitle);
931     break;
932   case InfoType::IT_default:
933     return llvm::createStringError(llvm::inconvertibleErrorCode(),
934                                    "unexpected info type");
935   }
936 
937   HTMLFile F = genInfoFile(InfoTitle, I->getRelativeFilePath(""),
938                            MainContentNodes, InfoIndex, CDCtx);
939   F.Render(OS);
940 
941   return llvm::Error::success();
942 }
943 
944 static std::string getRefType(InfoType IT) {
945   switch (IT) {
946   case InfoType::IT_default:
947     return "default";
948   case InfoType::IT_namespace:
949     return "namespace";
950   case InfoType::IT_record:
951     return "record";
952   case InfoType::IT_function:
953     return "function";
954   case InfoType::IT_enum:
955     return "enum";
956   case InfoType::IT_typedef:
957     return "typedef";
958   }
959   llvm_unreachable("Unknown InfoType");
960 }
961 
962 static llvm::Error SerializeIndex(ClangDocContext &CDCtx) {
963   std::error_code OK;
964   std::error_code FileErr;
965   llvm::SmallString<128> FilePath;
966   llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
967   llvm::sys::path::append(FilePath, "index_json.js");
968   llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
969   if (FileErr != OK) {
970     return llvm::createStringError(llvm::inconvertibleErrorCode(),
971                                    "error creating index file: " +
972                                        FileErr.message());
973   }
974   CDCtx.Idx.sort();
975   llvm::json::OStream J(OS, 2);
976   std::function<void(Index)> IndexToJSON = [&](const Index &I) {
977     J.object([&] {
978       J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
979       J.attribute("Name", I.Name);
980       J.attribute("RefType", getRefType(I.RefType));
981       J.attribute("Path", I.getRelativeFilePath(""));
982       J.attributeArray("Children", [&] {
983         for (const Index &C : I.Children)
984           IndexToJSON(C);
985       });
986     });
987   };
988   OS << "var JsonIndex = `\n";
989   IndexToJSON(CDCtx.Idx);
990   OS << "`;\n";
991   return llvm::Error::success();
992 }
993 
994 // Generates a main HTML node that has the main content of the file that shows
995 // only the general index
996 // It contains the general index with links to all the generated files
997 static std::unique_ptr<TagNode> genIndexFileMainNode() {
998   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
999 
1000   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
1001   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
1002   LeftSidebarNode->Attributes.emplace_back("path", "");
1003   LeftSidebarNode->Attributes.emplace_back(
1004       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
1005   LeftSidebarNode->Attributes.emplace_back("style", "flex: 0 100%;");
1006 
1007   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
1008 
1009   return MainNode;
1010 }
1011 
1012 static llvm::Error GenIndex(const ClangDocContext &CDCtx) {
1013   std::error_code FileErr, OK;
1014   llvm::SmallString<128> IndexPath;
1015   llvm::sys::path::native(CDCtx.OutDirectory, IndexPath);
1016   llvm::sys::path::append(IndexPath, "index.html");
1017   llvm::raw_fd_ostream IndexOS(IndexPath, FileErr, llvm::sys::fs::OF_None);
1018   if (FileErr != OK) {
1019     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1020                                    "error creating main index: " +
1021                                        FileErr.message());
1022   }
1023 
1024   HTMLFile F;
1025 
1026   std::vector<std::unique_ptr<TagNode>> HeadNodes =
1027       genFileHeadNodes("Index", "", CDCtx);
1028   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
1029   std::unique_ptr<TagNode> MainNode = genIndexFileMainNode();
1030   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
1031 
1032   AppendVector(std::move(HeadNodes), F.Children);
1033   F.Children.emplace_back(std::move(HeaderNode));
1034   F.Children.emplace_back(std::move(MainNode));
1035   F.Children.emplace_back(std::move(FooterNode));
1036 
1037   F.Render(IndexOS);
1038 
1039   return llvm::Error::success();
1040 }
1041 
1042 static llvm::Error CopyFile(StringRef FilePath, StringRef OutDirectory) {
1043   llvm::SmallString<128> PathWrite;
1044   llvm::sys::path::native(OutDirectory, PathWrite);
1045   llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
1046   llvm::SmallString<128> PathRead;
1047   llvm::sys::path::native(FilePath, PathRead);
1048   std::error_code OK;
1049   std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
1050   if (FileErr != OK) {
1051     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1052                                    "error creating file " +
1053                                        llvm::sys::path::filename(FilePath) +
1054                                        ": " + FileErr.message() + "\n");
1055   }
1056   return llvm::Error::success();
1057 }
1058 
1059 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
1060   auto Err = SerializeIndex(CDCtx);
1061   if (Err)
1062     return Err;
1063   Err = GenIndex(CDCtx);
1064   if (Err)
1065     return Err;
1066 
1067   for (const auto &FilePath : CDCtx.UserStylesheets) {
1068     Err = CopyFile(FilePath, CDCtx.OutDirectory);
1069     if (Err)
1070       return Err;
1071   }
1072   for (const auto &FilePath : CDCtx.FilesToCopy) {
1073     Err = CopyFile(FilePath, CDCtx.OutDirectory);
1074     if (Err)
1075       return Err;
1076   }
1077   return llvm::Error::success();
1078 }
1079 
1080 static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format,
1081                                                   "Generator for HTML output.");
1082 
1083 // This anchor is used to force the linker to link in the generated object
1084 // file and thus register the generator.
1085 volatile int HTMLGeneratorAnchorSource = 0;
1086 
1087 } // namespace doc
1088 } // namespace clang
1089