xref: /llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp (revision c29aba7159473c5fc225d4aea75647939497e3ac)
1671bac74SJulie Hockett //===-- HTMLGenerator.cpp - HTML Generator ----------------------*- C++ -*-===//
2671bac74SJulie Hockett //
3671bac74SJulie Hockett // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4671bac74SJulie Hockett // See https://llvm.org/LICENSE.txt for license information.
5671bac74SJulie Hockett // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6671bac74SJulie Hockett //
7671bac74SJulie Hockett //===----------------------------------------------------------------------===//
8671bac74SJulie Hockett 
9671bac74SJulie Hockett #include "Generators.h"
10671bac74SJulie Hockett #include "Representation.h"
113550da79SDiego Astiazaran #include "clang/Basic/Version.h"
12597b3fd3SDiego Astiazaran #include "llvm/ADT/StringExtras.h"
13671bac74SJulie Hockett #include "llvm/ADT/StringRef.h"
147b8c7e02SBrett Wilson #include "llvm/ADT/StringSet.h"
15671bac74SJulie Hockett #include "llvm/Support/FileSystem.h"
167dfe0bc3SDiego Astiazaran #include "llvm/Support/JSON.h"
17671bac74SJulie Hockett #include "llvm/Support/Path.h"
187dfe0bc3SDiego Astiazaran #include "llvm/Support/raw_ostream.h"
1991450f1bSPeterChou1 #include <algorithm>
2071f55735SKazu Hirata #include <optional>
21671bac74SJulie Hockett #include <string>
22671bac74SJulie Hockett 
23671bac74SJulie Hockett using namespace llvm;
24671bac74SJulie Hockett 
25671bac74SJulie Hockett namespace clang {
26671bac74SJulie Hockett namespace doc {
27671bac74SJulie Hockett 
28671bac74SJulie Hockett namespace {
29671bac74SJulie Hockett 
30671bac74SJulie Hockett class HTMLTag {
31671bac74SJulie Hockett public:
32671bac74SJulie Hockett   // Any other tag can be added if required
33671bac74SJulie Hockett   enum TagType {
347dfe0bc3SDiego Astiazaran     TAG_A,
35671bac74SJulie Hockett     TAG_DIV,
363550da79SDiego Astiazaran     TAG_FOOTER,
37671bac74SJulie Hockett     TAG_H1,
38671bac74SJulie Hockett     TAG_H2,
39671bac74SJulie Hockett     TAG_H3,
403550da79SDiego Astiazaran     TAG_HEADER,
41671bac74SJulie Hockett     TAG_LI,
42db5d8e3dSDiego Astiazaran     TAG_LINK,
433550da79SDiego Astiazaran     TAG_MAIN,
447dfe0bc3SDiego Astiazaran     TAG_META,
453550da79SDiego Astiazaran     TAG_OL,
467dfe0bc3SDiego Astiazaran     TAG_P,
477dfe0bc3SDiego Astiazaran     TAG_SCRIPT,
487dfe0bc3SDiego Astiazaran     TAG_SPAN,
497dfe0bc3SDiego Astiazaran     TAG_TITLE,
507dfe0bc3SDiego Astiazaran     TAG_UL,
515ef2456aSPeterChou1     TAG_TABLE,
525ef2456aSPeterChou1     TAG_THEAD,
535ef2456aSPeterChou1     TAG_TBODY,
545ef2456aSPeterChou1     TAG_TR,
555ef2456aSPeterChou1     TAG_TD,
565ef2456aSPeterChou1     TAG_TH
57671bac74SJulie Hockett   };
58671bac74SJulie Hockett 
59671bac74SJulie Hockett   HTMLTag() = default;
60671bac74SJulie Hockett   constexpr HTMLTag(TagType Value) : Value(Value) {}
61671bac74SJulie Hockett 
62671bac74SJulie Hockett   operator TagType() const { return Value; }
63671bac74SJulie Hockett   operator bool() = delete;
64671bac74SJulie Hockett 
65ecfbb850SPaul Kirth   bool isSelfClosing() const;
6646a2abb9SPaul Kirth   StringRef toString() const;
67671bac74SJulie Hockett 
68671bac74SJulie Hockett private:
69671bac74SJulie Hockett   TagType Value;
70671bac74SJulie Hockett };
71671bac74SJulie Hockett 
722c1c9a24SJulie Hockett enum NodeType {
732c1c9a24SJulie Hockett   NODE_TEXT,
742c1c9a24SJulie Hockett   NODE_TAG,
752c1c9a24SJulie Hockett };
762c1c9a24SJulie Hockett 
77671bac74SJulie Hockett struct HTMLNode {
782c1c9a24SJulie Hockett   HTMLNode(NodeType Type) : Type(Type) {}
79671bac74SJulie Hockett   virtual ~HTMLNode() = default;
80671bac74SJulie Hockett 
81ecfbb850SPaul Kirth   virtual void render(llvm::raw_ostream &OS, int IndentationLevel) = 0;
822c1c9a24SJulie Hockett   NodeType Type; // Type of node
83671bac74SJulie Hockett };
84671bac74SJulie Hockett 
85671bac74SJulie Hockett struct TextNode : public HTMLNode {
8664ca8570SDiego Astiazaran   TextNode(const Twine &Text)
8764ca8570SDiego Astiazaran       : HTMLNode(NodeType::NODE_TEXT), Text(Text.str()) {}
88671bac74SJulie Hockett 
89671bac74SJulie Hockett   std::string Text; // Content of node
90ecfbb850SPaul Kirth   void render(llvm::raw_ostream &OS, int IndentationLevel) override;
91671bac74SJulie Hockett };
92671bac74SJulie Hockett 
93671bac74SJulie Hockett struct TagNode : public HTMLNode {
9464ca8570SDiego Astiazaran   TagNode(HTMLTag Tag) : HTMLNode(NodeType::NODE_TAG), Tag(Tag) {}
95671bac74SJulie Hockett   TagNode(HTMLTag Tag, const Twine &Text) : TagNode(Tag) {
961c705d9cSJonas Devlieghere     Children.emplace_back(std::make_unique<TextNode>(Text.str()));
97671bac74SJulie Hockett   }
98671bac74SJulie Hockett 
99671bac74SJulie Hockett   HTMLTag Tag; // Name of HTML Tag (p, div, h1)
100671bac74SJulie Hockett   std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes
101ee6700f6SDiego Astiazaran   std::vector<std::pair<std::string, std::string>>
102671bac74SJulie Hockett       Attributes; // List of key-value attributes for tag
103671bac74SJulie Hockett 
104ecfbb850SPaul Kirth   void render(llvm::raw_ostream &OS, int IndentationLevel) override;
105671bac74SJulie Hockett };
106671bac74SJulie Hockett 
107671bac74SJulie Hockett constexpr const char *kDoctypeDecl = "<!DOCTYPE html>";
108671bac74SJulie Hockett 
109671bac74SJulie Hockett struct HTMLFile {
110671bac74SJulie Hockett   std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes
111ecfbb850SPaul Kirth   void render(llvm::raw_ostream &OS) {
112671bac74SJulie Hockett     OS << kDoctypeDecl << "\n";
113671bac74SJulie Hockett     for (const auto &C : Children) {
114ecfbb850SPaul Kirth       C->render(OS, 0);
115671bac74SJulie Hockett       OS << "\n";
116671bac74SJulie Hockett     }
117671bac74SJulie Hockett   }
118671bac74SJulie Hockett };
119671bac74SJulie Hockett 
120671bac74SJulie Hockett } // namespace
121671bac74SJulie Hockett 
122ecfbb850SPaul Kirth bool HTMLTag::isSelfClosing() const {
123671bac74SJulie Hockett   switch (Value) {
124671bac74SJulie Hockett   case HTMLTag::TAG_META:
125db5d8e3dSDiego Astiazaran   case HTMLTag::TAG_LINK:
126671bac74SJulie Hockett     return true;
1277dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_A:
128671bac74SJulie Hockett   case HTMLTag::TAG_DIV:
1293550da79SDiego Astiazaran   case HTMLTag::TAG_FOOTER:
130671bac74SJulie Hockett   case HTMLTag::TAG_H1:
131671bac74SJulie Hockett   case HTMLTag::TAG_H2:
132671bac74SJulie Hockett   case HTMLTag::TAG_H3:
1333550da79SDiego Astiazaran   case HTMLTag::TAG_HEADER:
134671bac74SJulie Hockett   case HTMLTag::TAG_LI:
1353550da79SDiego Astiazaran   case HTMLTag::TAG_MAIN:
1363550da79SDiego Astiazaran   case HTMLTag::TAG_OL:
1377dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_P:
1387dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_SCRIPT:
1397dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_SPAN:
1407dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_TITLE:
1417dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_UL:
1425ef2456aSPeterChou1   case HTMLTag::TAG_TABLE:
1435ef2456aSPeterChou1   case HTMLTag::TAG_THEAD:
1445ef2456aSPeterChou1   case HTMLTag::TAG_TBODY:
1455ef2456aSPeterChou1   case HTMLTag::TAG_TR:
1465ef2456aSPeterChou1   case HTMLTag::TAG_TD:
1475ef2456aSPeterChou1   case HTMLTag::TAG_TH:
148671bac74SJulie Hockett     return false;
149671bac74SJulie Hockett   }
150c593f5e6SMikael Holmen   llvm_unreachable("Unhandled HTMLTag::TagType");
151671bac74SJulie Hockett }
152671bac74SJulie Hockett 
15346a2abb9SPaul Kirth StringRef HTMLTag::toString() const {
154671bac74SJulie Hockett   switch (Value) {
1557dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_A:
15646a2abb9SPaul Kirth     return "a";
157671bac74SJulie Hockett   case HTMLTag::TAG_DIV:
15846a2abb9SPaul Kirth     return "div";
1593550da79SDiego Astiazaran   case HTMLTag::TAG_FOOTER:
16046a2abb9SPaul Kirth     return "footer";
161671bac74SJulie Hockett   case HTMLTag::TAG_H1:
16246a2abb9SPaul Kirth     return "h1";
163671bac74SJulie Hockett   case HTMLTag::TAG_H2:
16446a2abb9SPaul Kirth     return "h2";
165671bac74SJulie Hockett   case HTMLTag::TAG_H3:
16646a2abb9SPaul Kirth     return "h3";
1673550da79SDiego Astiazaran   case HTMLTag::TAG_HEADER:
16846a2abb9SPaul Kirth     return "header";
169671bac74SJulie Hockett   case HTMLTag::TAG_LI:
17046a2abb9SPaul Kirth     return "li";
171db5d8e3dSDiego Astiazaran   case HTMLTag::TAG_LINK:
17246a2abb9SPaul Kirth     return "link";
1733550da79SDiego Astiazaran   case HTMLTag::TAG_MAIN:
17446a2abb9SPaul Kirth     return "main";
1757dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_META:
17646a2abb9SPaul Kirth     return "meta";
1773550da79SDiego Astiazaran   case HTMLTag::TAG_OL:
17846a2abb9SPaul Kirth     return "ol";
1797dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_P:
18046a2abb9SPaul Kirth     return "p";
1817dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_SCRIPT:
18246a2abb9SPaul Kirth     return "script";
1837dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_SPAN:
18446a2abb9SPaul Kirth     return "span";
1857dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_TITLE:
18646a2abb9SPaul Kirth     return "title";
1877dfe0bc3SDiego Astiazaran   case HTMLTag::TAG_UL:
18846a2abb9SPaul Kirth     return "ul";
1895ef2456aSPeterChou1   case HTMLTag::TAG_TABLE:
1905ef2456aSPeterChou1     return "table";
1915ef2456aSPeterChou1   case HTMLTag::TAG_THEAD:
1925ef2456aSPeterChou1     return "thead";
1935ef2456aSPeterChou1   case HTMLTag::TAG_TBODY:
1945ef2456aSPeterChou1     return "tbody";
1955ef2456aSPeterChou1   case HTMLTag::TAG_TR:
1965ef2456aSPeterChou1     return "tr";
1975ef2456aSPeterChou1   case HTMLTag::TAG_TD:
1985ef2456aSPeterChou1     return "td";
1995ef2456aSPeterChou1   case HTMLTag::TAG_TH:
2005ef2456aSPeterChou1     return "th";
201671bac74SJulie Hockett   }
202c593f5e6SMikael Holmen   llvm_unreachable("Unhandled HTMLTag::TagType");
203671bac74SJulie Hockett }
204671bac74SJulie Hockett 
205ecfbb850SPaul Kirth void TextNode::render(llvm::raw_ostream &OS, int IndentationLevel) {
206671bac74SJulie Hockett   OS.indent(IndentationLevel * 2);
207597b3fd3SDiego Astiazaran   printHTMLEscaped(Text, OS);
208671bac74SJulie Hockett }
209671bac74SJulie Hockett 
210ecfbb850SPaul Kirth void TagNode::render(llvm::raw_ostream &OS, int IndentationLevel) {
21164ca8570SDiego Astiazaran   // Children nodes are rendered in the same line if all of them are text nodes
21264ca8570SDiego Astiazaran   bool InlineChildren = true;
21364ca8570SDiego Astiazaran   for (const auto &C : Children)
21464ca8570SDiego Astiazaran     if (C->Type == NodeType::NODE_TAG) {
21564ca8570SDiego Astiazaran       InlineChildren = false;
21664ca8570SDiego Astiazaran       break;
21764ca8570SDiego Astiazaran     }
218671bac74SJulie Hockett   OS.indent(IndentationLevel * 2);
21946a2abb9SPaul Kirth   OS << "<" << Tag.toString();
220671bac74SJulie Hockett   for (const auto &A : Attributes)
2213550da79SDiego Astiazaran     OS << " " << A.first << "=\"" << A.second << "\"";
222ecfbb850SPaul Kirth   if (Tag.isSelfClosing()) {
223671bac74SJulie Hockett     OS << "/>";
224671bac74SJulie Hockett     return;
225671bac74SJulie Hockett   }
226671bac74SJulie Hockett   OS << ">";
227671bac74SJulie Hockett   if (!InlineChildren)
228671bac74SJulie Hockett     OS << "\n";
2292c1c9a24SJulie Hockett   bool NewLineRendered = true;
230671bac74SJulie Hockett   for (const auto &C : Children) {
2312c1c9a24SJulie Hockett     int ChildrenIndentation =
2322c1c9a24SJulie Hockett         InlineChildren || !NewLineRendered ? 0 : IndentationLevel + 1;
233ecfbb850SPaul Kirth     C->render(OS, ChildrenIndentation);
2342c1c9a24SJulie Hockett     if (!InlineChildren && (C == Children.back() ||
2352c1c9a24SJulie Hockett                             (C->Type != NodeType::NODE_TEXT ||
2362c1c9a24SJulie Hockett                              (&C + 1)->get()->Type != NodeType::NODE_TEXT))) {
237671bac74SJulie Hockett       OS << "\n";
2382c1c9a24SJulie Hockett       NewLineRendered = true;
2392c1c9a24SJulie Hockett     } else
2402c1c9a24SJulie Hockett       NewLineRendered = false;
241671bac74SJulie Hockett   }
242671bac74SJulie Hockett   if (!InlineChildren)
243671bac74SJulie Hockett     OS.indent(IndentationLevel * 2);
24446a2abb9SPaul Kirth   OS << "</" << Tag.toString() << ">";
245671bac74SJulie Hockett }
246671bac74SJulie Hockett 
2472c1c9a24SJulie Hockett template <typename Derived, typename Base,
2482c1c9a24SJulie Hockett           typename = std::enable_if<std::is_base_of<Derived, Base>::value>>
249ecfbb850SPaul Kirth static void appendVector(std::vector<Derived> &&New,
2502c1c9a24SJulie Hockett                          std::vector<Base> &Original) {
2512c1c9a24SJulie Hockett   std::move(New.begin(), New.end(), std::back_inserter(Original));
2522c1c9a24SJulie Hockett }
2532c1c9a24SJulie Hockett 
254e27f778aSDiego Astiazaran // Compute the relative path from an Origin directory to a Destination directory
255e27f778aSDiego Astiazaran static SmallString<128> computeRelativePath(StringRef Destination,
256e27f778aSDiego Astiazaran                                             StringRef Origin) {
257e27f778aSDiego Astiazaran   // If Origin is empty, the relative path to the Destination is its complete
258e27f778aSDiego Astiazaran   // path.
259e27f778aSDiego Astiazaran   if (Origin.empty())
260e27f778aSDiego Astiazaran     return Destination;
2612c1c9a24SJulie Hockett 
262e27f778aSDiego Astiazaran   // The relative path is an empty path if both directories are the same.
263e27f778aSDiego Astiazaran   if (Destination == Origin)
264e27f778aSDiego Astiazaran     return {};
265e27f778aSDiego Astiazaran 
266e27f778aSDiego Astiazaran   // These iterators iterate through each of their parent directories
267e27f778aSDiego Astiazaran   llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
268e27f778aSDiego Astiazaran   llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
269e27f778aSDiego Astiazaran   llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
270e27f778aSDiego Astiazaran   llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
271e27f778aSDiego Astiazaran   // Advance both iterators until the paths differ. Example:
272e27f778aSDiego Astiazaran   //    Destination = A/B/C/D
273e27f778aSDiego Astiazaran   //    Origin      = A/B/E/F
274e27f778aSDiego Astiazaran   // FileI will point to C and DirI to E. The directories behind them is the
275e27f778aSDiego Astiazaran   // directory they share (A/B).
276e27f778aSDiego Astiazaran   while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
277e27f778aSDiego Astiazaran     ++FileI;
278e27f778aSDiego Astiazaran     ++DirI;
2792c1c9a24SJulie Hockett   }
280e27f778aSDiego Astiazaran   SmallString<128> Result; // This will hold the resulting path.
281e27f778aSDiego Astiazaran   // Result has to go up one directory for each of the remaining directories in
282e27f778aSDiego Astiazaran   // Origin
283e27f778aSDiego Astiazaran   while (DirI != DirE) {
284e27f778aSDiego Astiazaran     llvm::sys::path::append(Result, "..");
285e27f778aSDiego Astiazaran     ++DirI;
286e27f778aSDiego Astiazaran   }
287e27f778aSDiego Astiazaran   // Result has to append each of the remaining directories in Destination
288e27f778aSDiego Astiazaran   while (FileI != FileE) {
289e27f778aSDiego Astiazaran     llvm::sys::path::append(Result, *FileI);
290e27f778aSDiego Astiazaran     ++FileI;
291e27f778aSDiego Astiazaran   }
2922c1c9a24SJulie Hockett   return Result;
2932c1c9a24SJulie Hockett }
2942c1c9a24SJulie Hockett 
295671bac74SJulie Hockett // HTML generation
296671bac74SJulie Hockett 
2977dfe0bc3SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
298acd35f6cSDiego Astiazaran genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
299acd35f6cSDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> Out;
300acd35f6cSDiego Astiazaran   for (const auto &FilePath : CDCtx.UserStylesheets) {
3011c705d9cSJonas Devlieghere     auto LinkNode = std::make_unique<TagNode>(HTMLTag::TAG_LINK);
3023550da79SDiego Astiazaran     LinkNode->Attributes.emplace_back("rel", "stylesheet");
303acd35f6cSDiego Astiazaran     SmallString<128> StylesheetPath = computeRelativePath("", InfoPath);
304acd35f6cSDiego Astiazaran     llvm::sys::path::append(StylesheetPath,
305acd35f6cSDiego Astiazaran                             llvm::sys::path::filename(FilePath));
306d6cdd98aSDiego Astiazaran     // Paths in HTML must be in posix-style
307d6cdd98aSDiego Astiazaran     llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
3082b00d449SKazu Hirata     LinkNode->Attributes.emplace_back("href", std::string(StylesheetPath));
309acd35f6cSDiego Astiazaran     Out.emplace_back(std::move(LinkNode));
310acd35f6cSDiego Astiazaran   }
311acd35f6cSDiego Astiazaran   return Out;
312acd35f6cSDiego Astiazaran }
313acd35f6cSDiego Astiazaran 
3147dfe0bc3SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
3157dfe0bc3SDiego Astiazaran genJsScriptsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
3167dfe0bc3SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> Out;
317f14ad744SPeterChou1 
318f14ad744SPeterChou1   // index_json.js is part of every generated HTML file
319f14ad744SPeterChou1   SmallString<128> IndexJSONPath = computeRelativePath("", InfoPath);
320f14ad744SPeterChou1   auto IndexJSONNode = std::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
321f14ad744SPeterChou1   llvm::sys::path::append(IndexJSONPath, "index_json.js");
322f14ad744SPeterChou1   llvm::sys::path::native(IndexJSONPath, llvm::sys::path::Style::posix);
323f14ad744SPeterChou1   IndexJSONNode->Attributes.emplace_back("src", std::string(IndexJSONPath));
324f14ad744SPeterChou1   Out.emplace_back(std::move(IndexJSONNode));
325f14ad744SPeterChou1 
3267dfe0bc3SDiego Astiazaran   for (const auto &FilePath : CDCtx.JsScripts) {
3277dfe0bc3SDiego Astiazaran     SmallString<128> ScriptPath = computeRelativePath("", InfoPath);
328f14ad744SPeterChou1     auto ScriptNode = std::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
3297dfe0bc3SDiego Astiazaran     llvm::sys::path::append(ScriptPath, llvm::sys::path::filename(FilePath));
33040fde901SDiego Astiazaran     // Paths in HTML must be in posix-style
33140fde901SDiego Astiazaran     llvm::sys::path::native(ScriptPath, llvm::sys::path::Style::posix);
3322b00d449SKazu Hirata     ScriptNode->Attributes.emplace_back("src", std::string(ScriptPath));
3337dfe0bc3SDiego Astiazaran     Out.emplace_back(std::move(ScriptNode));
3347dfe0bc3SDiego Astiazaran   }
3357dfe0bc3SDiego Astiazaran   return Out;
3367dfe0bc3SDiego Astiazaran }
3377dfe0bc3SDiego Astiazaran 
3382c1c9a24SJulie Hockett static std::unique_ptr<TagNode> genLink(const Twine &Text, const Twine &Link) {
3391c705d9cSJonas Devlieghere   auto LinkNode = std::make_unique<TagNode>(HTMLTag::TAG_A, Text);
3403550da79SDiego Astiazaran   LinkNode->Attributes.emplace_back("href", Link.str());
3412c1c9a24SJulie Hockett   return LinkNode;
3422c1c9a24SJulie Hockett }
3432c1c9a24SJulie Hockett 
344d47be4daSDiego Astiazaran static std::unique_ptr<HTMLNode>
345e27f778aSDiego Astiazaran genReference(const Reference &Type, StringRef CurrentDirectory,
346f71ffd3bSKazu Hirata              std::optional<StringRef> JumpToSection = std::nullopt) {
3470afc6085SBrett Wilson   if (Type.Path.empty()) {
348d47be4daSDiego Astiazaran     if (!JumpToSection)
3491c705d9cSJonas Devlieghere       return std::make_unique<TextNode>(Type.Name);
350ed8fceaaSKazu Hirata     return genLink(Type.Name, "#" + *JumpToSection);
351d47be4daSDiego Astiazaran   }
3527003f64cSPetr Hosek   llvm::SmallString<64> Path = Type.getRelativeFilePath(CurrentDirectory);
3537003f64cSPetr Hosek   llvm::sys::path::append(Path, Type.getFileBaseName() + ".html");
3547003f64cSPetr Hosek 
3556ad2151bSDiego Astiazaran   // Paths in HTML must be in posix-style
3566ad2151bSDiego Astiazaran   llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
357d47be4daSDiego Astiazaran   if (JumpToSection)
358ed8fceaaSKazu Hirata     Path += ("#" + *JumpToSection).str();
3592c1c9a24SJulie Hockett   return genLink(Type.Name, Path);
3602c1c9a24SJulie Hockett }
3612c1c9a24SJulie Hockett 
3622c1c9a24SJulie Hockett static std::vector<std::unique_ptr<HTMLNode>>
3632c1c9a24SJulie Hockett genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs,
3642c1c9a24SJulie Hockett                  const StringRef &CurrentDirectory) {
3652c1c9a24SJulie Hockett   std::vector<std::unique_ptr<HTMLNode>> Out;
3662c1c9a24SJulie Hockett   for (const auto &R : Refs) {
3672c1c9a24SJulie Hockett     if (&R != Refs.begin())
3681c705d9cSJonas Devlieghere       Out.emplace_back(std::make_unique<TextNode>(", "));
369e27f778aSDiego Astiazaran     Out.emplace_back(genReference(R, CurrentDirectory));
3702c1c9a24SJulie Hockett   }
3712c1c9a24SJulie Hockett   return Out;
3722c1c9a24SJulie Hockett }
3732c1c9a24SJulie Hockett 
374665e9676SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
375665e9676SDiego Astiazaran genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
376665e9676SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
377665e9676SDiego Astiazaran genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
3782c1c9a24SJulie Hockett         StringRef ParentInfoDir);
379b4bc7b18SPeterChou1 static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C);
380671bac74SJulie Hockett 
381671bac74SJulie Hockett static std::vector<std::unique_ptr<TagNode>>
382665e9676SDiego Astiazaran genEnumsBlock(const std::vector<EnumInfo> &Enums,
383665e9676SDiego Astiazaran               const ClangDocContext &CDCtx) {
384671bac74SJulie Hockett   if (Enums.empty())
385671bac74SJulie Hockett     return {};
386671bac74SJulie Hockett 
387671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
3881c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Enums"));
3893550da79SDiego Astiazaran   Out.back()->Attributes.emplace_back("id", "Enums");
3901c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
391671bac74SJulie Hockett   auto &DivBody = Out.back();
392671bac74SJulie Hockett   for (const auto &E : Enums) {
393665e9676SDiego Astiazaran     std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E, CDCtx);
394ecfbb850SPaul Kirth     appendVector(std::move(Nodes), DivBody->Children);
395671bac74SJulie Hockett   }
396671bac74SJulie Hockett   return Out;
397671bac74SJulie Hockett }
398671bac74SJulie Hockett 
399671bac74SJulie Hockett static std::unique_ptr<TagNode>
400eaa7b324SBrett Wilson genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
401671bac74SJulie Hockett   if (Members.empty())
402671bac74SJulie Hockett     return nullptr;
403671bac74SJulie Hockett 
4045ef2456aSPeterChou1   auto List = std::make_unique<TagNode>(HTMLTag::TAG_TBODY);
4055ef2456aSPeterChou1 
4065ef2456aSPeterChou1   for (const auto &M : Members) {
4075ef2456aSPeterChou1     auto TRNode = std::make_unique<TagNode>(HTMLTag::TAG_TR);
4085ef2456aSPeterChou1     TRNode->Children.emplace_back(
4095ef2456aSPeterChou1         std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Name));
4105ef2456aSPeterChou1     // Use user supplied value if it exists, otherwise use the value
4115ef2456aSPeterChou1     if (!M.ValueExpr.empty()) {
4125ef2456aSPeterChou1       TRNode->Children.emplace_back(
4135ef2456aSPeterChou1           std::make_unique<TagNode>(HTMLTag::TAG_TD, M.ValueExpr));
4145ef2456aSPeterChou1     } else {
4155ef2456aSPeterChou1       TRNode->Children.emplace_back(
4165ef2456aSPeterChou1           std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
4175ef2456aSPeterChou1     }
4185ef2456aSPeterChou1     if (!M.Description.empty()) {
4195ef2456aSPeterChou1       auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
4205ef2456aSPeterChou1       TD->Children.emplace_back(genHTML(M.Description));
4215ef2456aSPeterChou1       TRNode->Children.emplace_back(std::move(TD));
4225ef2456aSPeterChou1     }
4235ef2456aSPeterChou1     List->Children.emplace_back(std::move(TRNode));
4245ef2456aSPeterChou1   }
425671bac74SJulie Hockett   return List;
426671bac74SJulie Hockett }
427671bac74SJulie Hockett 
428671bac74SJulie Hockett static std::vector<std::unique_ptr<TagNode>>
4292c1c9a24SJulie Hockett genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
430665e9676SDiego Astiazaran                   const ClangDocContext &CDCtx, StringRef ParentInfoDir) {
431671bac74SJulie Hockett   if (Functions.empty())
432671bac74SJulie Hockett     return {};
433671bac74SJulie Hockett 
434671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
4351c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions"));
4363550da79SDiego Astiazaran   Out.back()->Attributes.emplace_back("id", "Functions");
4371c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
438671bac74SJulie Hockett   auto &DivBody = Out.back();
439671bac74SJulie Hockett   for (const auto &F : Functions) {
440665e9676SDiego Astiazaran     std::vector<std::unique_ptr<TagNode>> Nodes =
441665e9676SDiego Astiazaran         genHTML(F, CDCtx, ParentInfoDir);
442ecfbb850SPaul Kirth     appendVector(std::move(Nodes), DivBody->Children);
443671bac74SJulie Hockett   }
444671bac74SJulie Hockett   return Out;
445671bac74SJulie Hockett }
446671bac74SJulie Hockett 
447671bac74SJulie Hockett static std::vector<std::unique_ptr<TagNode>>
4482c1c9a24SJulie Hockett genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
4492c1c9a24SJulie Hockett                       StringRef ParentInfoDir) {
450671bac74SJulie Hockett   if (Members.empty())
451671bac74SJulie Hockett     return {};
452671bac74SJulie Hockett 
453671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
4541c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Members"));
4553550da79SDiego Astiazaran   Out.back()->Attributes.emplace_back("id", "Members");
4561c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
457671bac74SJulie Hockett   auto &ULBody = Out.back();
458671bac74SJulie Hockett   for (const auto &M : Members) {
4596407aa9dSDaniel Martín     std::string Access = getAccessSpelling(M.Access).str();
460671bac74SJulie Hockett     if (Access != "")
461671bac74SJulie Hockett       Access = Access + " ";
4621c705d9cSJonas Devlieghere     auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
463b4bc7b18SPeterChou1     auto MemberDecl = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
464b4bc7b18SPeterChou1     MemberDecl->Children.emplace_back(std::make_unique<TextNode>(Access));
465b4bc7b18SPeterChou1     MemberDecl->Children.emplace_back(genReference(M.Type, ParentInfoDir));
466b4bc7b18SPeterChou1     MemberDecl->Children.emplace_back(std::make_unique<TextNode>(" " + M.Name));
467b4bc7b18SPeterChou1     if (!M.Description.empty())
468b4bc7b18SPeterChou1       LIBody->Children.emplace_back(genHTML(M.Description));
469b4bc7b18SPeterChou1     LIBody->Children.emplace_back(std::move(MemberDecl));
4702c1c9a24SJulie Hockett     ULBody->Children.emplace_back(std::move(LIBody));
471671bac74SJulie Hockett   }
472671bac74SJulie Hockett   return Out;
473671bac74SJulie Hockett }
474671bac74SJulie Hockett 
475671bac74SJulie Hockett static std::vector<std::unique_ptr<TagNode>>
476671bac74SJulie Hockett genReferencesBlock(const std::vector<Reference> &References,
477e27f778aSDiego Astiazaran                    llvm::StringRef Title, StringRef ParentPath) {
478671bac74SJulie Hockett   if (References.empty())
479671bac74SJulie Hockett     return {};
480671bac74SJulie Hockett 
481671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
4821c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, Title));
4832103e08bSMikael Holmén   Out.back()->Attributes.emplace_back("id", std::string(Title));
4841c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
485671bac74SJulie Hockett   auto &ULBody = Out.back();
486e27f778aSDiego Astiazaran   for (const auto &R : References) {
4871c705d9cSJonas Devlieghere     auto LiNode = std::make_unique<TagNode>(HTMLTag::TAG_LI);
488e27f778aSDiego Astiazaran     LiNode->Children.emplace_back(genReference(R, ParentPath));
489e27f778aSDiego Astiazaran     ULBody->Children.emplace_back(std::move(LiNode));
490e27f778aSDiego Astiazaran   }
491671bac74SJulie Hockett   return Out;
492671bac74SJulie Hockett }
493671bac74SJulie Hockett 
494665e9676SDiego Astiazaran static std::unique_ptr<TagNode>
495665e9676SDiego Astiazaran writeFileDefinition(const Location &L,
496f71ffd3bSKazu Hirata                     std::optional<StringRef> RepositoryUrl = std::nullopt) {
497665e9676SDiego Astiazaran   if (!L.IsFileInRootDir || !RepositoryUrl)
4981c705d9cSJonas Devlieghere     return std::make_unique<TagNode>(
499665e9676SDiego Astiazaran         HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
500665e9676SDiego Astiazaran                             " of file " + L.Filename);
501ed8fceaaSKazu Hirata   SmallString<128> FileURL(*RepositoryUrl);
502665e9676SDiego Astiazaran   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
5031c705d9cSJonas Devlieghere   auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
5041c705d9cSJonas Devlieghere   Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
505665e9676SDiego Astiazaran   auto LocNumberNode =
5061c705d9cSJonas Devlieghere       std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
507665e9676SDiego Astiazaran   // The links to a specific line in the source code use the github /
508665e9676SDiego Astiazaran   // googlesource notation so it won't work for all hosting pages.
5093550da79SDiego Astiazaran   LocNumberNode->Attributes.emplace_back(
510665e9676SDiego Astiazaran       "href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
511665e9676SDiego Astiazaran   Node->Children.emplace_back(std::move(LocNumberNode));
5121c705d9cSJonas Devlieghere   Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
5131c705d9cSJonas Devlieghere   auto LocFileNode = std::make_unique<TagNode>(
514665e9676SDiego Astiazaran       HTMLTag::TAG_A, llvm::sys::path::filename(FileURL));
51521edd381SKazu Hirata   LocFileNode->Attributes.emplace_back("href", std::string(FileURL));
516665e9676SDiego Astiazaran   Node->Children.emplace_back(std::move(LocFileNode));
517665e9676SDiego Astiazaran   return Node;
518671bac74SJulie Hockett }
519671bac74SJulie Hockett 
5207dfe0bc3SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
5213550da79SDiego Astiazaran genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList);
5223550da79SDiego Astiazaran 
5233550da79SDiego Astiazaran // Generates a list of child nodes for the HTML head tag
5243550da79SDiego Astiazaran // It contains a meta node, link nodes to import CSS files, and script nodes to
5253550da79SDiego Astiazaran // import JS files
5263550da79SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
5273550da79SDiego Astiazaran genFileHeadNodes(StringRef Title, StringRef InfoPath,
5287dfe0bc3SDiego Astiazaran                  const ClangDocContext &CDCtx) {
5297dfe0bc3SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> Out;
5301c705d9cSJonas Devlieghere   auto MetaNode = std::make_unique<TagNode>(HTMLTag::TAG_META);
5313550da79SDiego Astiazaran   MetaNode->Attributes.emplace_back("charset", "utf-8");
5327dfe0bc3SDiego Astiazaran   Out.emplace_back(std::move(MetaNode));
5331c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_TITLE, Title));
5347dfe0bc3SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> StylesheetsNodes =
5357dfe0bc3SDiego Astiazaran       genStylesheetsHTML(InfoPath, CDCtx);
536ecfbb850SPaul Kirth   appendVector(std::move(StylesheetsNodes), Out);
5377dfe0bc3SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> JsNodes =
5387dfe0bc3SDiego Astiazaran       genJsScriptsHTML(InfoPath, CDCtx);
539ecfbb850SPaul Kirth   appendVector(std::move(JsNodes), Out);
5407dfe0bc3SDiego Astiazaran   return Out;
5417dfe0bc3SDiego Astiazaran }
5427dfe0bc3SDiego Astiazaran 
5433550da79SDiego Astiazaran // Generates a header HTML node that can be used for any file
5443550da79SDiego Astiazaran // It contains the project name
5453550da79SDiego Astiazaran static std::unique_ptr<TagNode> genFileHeaderNode(StringRef ProjectName) {
5463550da79SDiego Astiazaran   auto HeaderNode = std::make_unique<TagNode>(HTMLTag::TAG_HEADER, ProjectName);
5473550da79SDiego Astiazaran   HeaderNode->Attributes.emplace_back("id", "project-title");
5483550da79SDiego Astiazaran   return HeaderNode;
5493550da79SDiego Astiazaran }
5503550da79SDiego Astiazaran 
5513550da79SDiego Astiazaran // Generates a main HTML node that has all the main content of an info file
5523550da79SDiego Astiazaran // It contains both indexes and the info's documented information
5533550da79SDiego Astiazaran // This function should only be used for the info files (not for the file that
5543550da79SDiego Astiazaran // only has the general index)
5553550da79SDiego Astiazaran static std::unique_ptr<TagNode> genInfoFileMainNode(
5563550da79SDiego Astiazaran     StringRef InfoPath,
5573550da79SDiego Astiazaran     std::vector<std::unique_ptr<TagNode>> &MainContentInnerNodes,
5583550da79SDiego Astiazaran     const Index &InfoIndex) {
5593550da79SDiego Astiazaran   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
5603550da79SDiego Astiazaran 
5613550da79SDiego Astiazaran   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
5623550da79SDiego Astiazaran   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
5632103e08bSMikael Holmén   LeftSidebarNode->Attributes.emplace_back("path", std::string(InfoPath));
5643550da79SDiego Astiazaran   LeftSidebarNode->Attributes.emplace_back(
5653550da79SDiego Astiazaran       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
5663550da79SDiego Astiazaran 
5673550da79SDiego Astiazaran   auto MainContentNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
5683550da79SDiego Astiazaran   MainContentNode->Attributes.emplace_back("id", "main-content");
5693550da79SDiego Astiazaran   MainContentNode->Attributes.emplace_back(
5703550da79SDiego Astiazaran       "class", "col-xs-12 col-sm-9 col-md-8 main-content");
571ecfbb850SPaul Kirth   appendVector(std::move(MainContentInnerNodes), MainContentNode->Children);
5723550da79SDiego Astiazaran 
5733550da79SDiego Astiazaran   auto RightSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
5743550da79SDiego Astiazaran   RightSidebarNode->Attributes.emplace_back("id", "sidebar-right");
5753550da79SDiego Astiazaran   RightSidebarNode->Attributes.emplace_back(
5763550da79SDiego Astiazaran       "class", "col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right");
5773550da79SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> InfoIndexHTML =
5783550da79SDiego Astiazaran       genHTML(InfoIndex, InfoPath, true);
579ecfbb850SPaul Kirth   appendVector(std::move(InfoIndexHTML), RightSidebarNode->Children);
5803550da79SDiego Astiazaran 
5813550da79SDiego Astiazaran   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
5823550da79SDiego Astiazaran   MainNode->Children.emplace_back(std::move(MainContentNode));
5833550da79SDiego Astiazaran   MainNode->Children.emplace_back(std::move(RightSidebarNode));
5843550da79SDiego Astiazaran 
5853550da79SDiego Astiazaran   return MainNode;
5863550da79SDiego Astiazaran }
5873550da79SDiego Astiazaran 
5883550da79SDiego Astiazaran // Generates a footer HTML node that can be used for any file
5893550da79SDiego Astiazaran // It contains clang-doc's version
5903550da79SDiego Astiazaran static std::unique_ptr<TagNode> genFileFooterNode() {
5913550da79SDiego Astiazaran   auto FooterNode = std::make_unique<TagNode>(HTMLTag::TAG_FOOTER);
5923550da79SDiego Astiazaran   auto SpanNode = std::make_unique<TagNode>(
5933550da79SDiego Astiazaran       HTMLTag::TAG_SPAN, clang::getClangToolFullVersion("clang-doc"));
5943550da79SDiego Astiazaran   SpanNode->Attributes.emplace_back("class", "no-break");
5953550da79SDiego Astiazaran   FooterNode->Children.emplace_back(std::move(SpanNode));
5963550da79SDiego Astiazaran   return FooterNode;
5973550da79SDiego Astiazaran }
5983550da79SDiego Astiazaran 
5993550da79SDiego Astiazaran // Generates a complete HTMLFile for an Info
6003550da79SDiego Astiazaran static HTMLFile
6013550da79SDiego Astiazaran genInfoFile(StringRef Title, StringRef InfoPath,
6023550da79SDiego Astiazaran             std::vector<std::unique_ptr<TagNode>> &MainContentNodes,
6033550da79SDiego Astiazaran             const Index &InfoIndex, const ClangDocContext &CDCtx) {
6043550da79SDiego Astiazaran   HTMLFile F;
6053550da79SDiego Astiazaran 
6063550da79SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> HeadNodes =
6073550da79SDiego Astiazaran       genFileHeadNodes(Title, InfoPath, CDCtx);
6083550da79SDiego Astiazaran   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
6093550da79SDiego Astiazaran   std::unique_ptr<TagNode> MainNode =
6103550da79SDiego Astiazaran       genInfoFileMainNode(InfoPath, MainContentNodes, InfoIndex);
6113550da79SDiego Astiazaran   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
6123550da79SDiego Astiazaran 
613ecfbb850SPaul Kirth   appendVector(std::move(HeadNodes), F.Children);
6143550da79SDiego Astiazaran   F.Children.emplace_back(std::move(HeaderNode));
6153550da79SDiego Astiazaran   F.Children.emplace_back(std::move(MainNode));
6163550da79SDiego Astiazaran   F.Children.emplace_back(std::move(FooterNode));
6173550da79SDiego Astiazaran 
6183550da79SDiego Astiazaran   return F;
6193550da79SDiego Astiazaran }
6203550da79SDiego Astiazaran 
621d47be4daSDiego Astiazaran template <typename T,
622d47be4daSDiego Astiazaran           typename = std::enable_if<std::is_base_of<T, Info>::value>>
623d47be4daSDiego Astiazaran static Index genInfoIndexItem(const std::vector<T> &Infos, StringRef Title) {
624d47be4daSDiego Astiazaran   Index Idx(Title, Title);
625d47be4daSDiego Astiazaran   for (const auto &C : Infos)
626d47be4daSDiego Astiazaran     Idx.Children.emplace_back(C.extractName(),
627d47be4daSDiego Astiazaran                               llvm::toHex(llvm::toStringRef(C.USR)));
628d47be4daSDiego Astiazaran   return Idx;
629d47be4daSDiego Astiazaran }
630d47be4daSDiego Astiazaran 
6313550da79SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
6323550da79SDiego Astiazaran genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList) {
633d47be4daSDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> Out;
634d47be4daSDiego Astiazaran   if (!Index.Name.empty()) {
6351c705d9cSJonas Devlieghere     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_SPAN));
636d47be4daSDiego Astiazaran     auto &SpanBody = Out.back();
637d47be4daSDiego Astiazaran     if (!Index.JumpToSection)
638e27f778aSDiego Astiazaran       SpanBody->Children.emplace_back(genReference(Index, InfoPath));
639d47be4daSDiego Astiazaran     else
6401def2579SDavid Blaikie       SpanBody->Children.emplace_back(
641ed8fceaaSKazu Hirata           genReference(Index, InfoPath, Index.JumpToSection->str()));
642d47be4daSDiego Astiazaran   }
643d47be4daSDiego Astiazaran   if (Index.Children.empty())
644d47be4daSDiego Astiazaran     return Out;
6453550da79SDiego Astiazaran   // Only the outermost list should use ol, the others should use ul
6463550da79SDiego Astiazaran   HTMLTag ListHTMLTag = IsOutermostList ? HTMLTag::TAG_OL : HTMLTag::TAG_UL;
6473550da79SDiego Astiazaran   Out.emplace_back(std::make_unique<TagNode>(ListHTMLTag));
648d47be4daSDiego Astiazaran   const auto &UlBody = Out.back();
649d47be4daSDiego Astiazaran   for (const auto &C : Index.Children) {
6501c705d9cSJonas Devlieghere     auto LiBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
6513550da79SDiego Astiazaran     std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(C, InfoPath, false);
652ecfbb850SPaul Kirth     appendVector(std::move(Nodes), LiBody->Children);
653d47be4daSDiego Astiazaran     UlBody->Children.emplace_back(std::move(LiBody));
654d47be4daSDiego Astiazaran   }
655d47be4daSDiego Astiazaran   return Out;
656d47be4daSDiego Astiazaran }
657d47be4daSDiego Astiazaran 
658671bac74SJulie Hockett static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
659671bac74SJulie Hockett   if (I.Kind == "FullComment") {
6601c705d9cSJonas Devlieghere     auto FullComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
661671bac74SJulie Hockett     for (const auto &Child : I.Children) {
662671bac74SJulie Hockett       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
663671bac74SJulie Hockett       if (Node)
664671bac74SJulie Hockett         FullComment->Children.emplace_back(std::move(Node));
665671bac74SJulie Hockett     }
666671bac74SJulie Hockett     return std::move(FullComment);
667ecfbb850SPaul Kirth   }
668ecfbb850SPaul Kirth 
669ecfbb850SPaul Kirth   if (I.Kind == "ParagraphComment") {
6701c705d9cSJonas Devlieghere     auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
671671bac74SJulie Hockett     for (const auto &Child : I.Children) {
672671bac74SJulie Hockett       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
673671bac74SJulie Hockett       if (Node)
674671bac74SJulie Hockett         ParagraphComment->Children.emplace_back(std::move(Node));
675671bac74SJulie Hockett     }
676671bac74SJulie Hockett     if (ParagraphComment->Children.empty())
677671bac74SJulie Hockett       return nullptr;
678671bac74SJulie Hockett     return std::move(ParagraphComment);
679ecfbb850SPaul Kirth   }
680ecfbb850SPaul Kirth 
681*c29aba71SPeterChou1   if (I.Kind == "BlockCommandComment") {
682*c29aba71SPeterChou1     auto BlockComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
683*c29aba71SPeterChou1     BlockComment->Children.emplace_back(
684*c29aba71SPeterChou1         std::make_unique<TagNode>(HTMLTag::TAG_DIV, I.Name));
685*c29aba71SPeterChou1     for (const auto &Child : I.Children) {
686*c29aba71SPeterChou1       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
687*c29aba71SPeterChou1       if (Node)
688*c29aba71SPeterChou1         BlockComment->Children.emplace_back(std::move(Node));
689*c29aba71SPeterChou1     }
690*c29aba71SPeterChou1     if (BlockComment->Children.empty())
691*c29aba71SPeterChou1       return nullptr;
692*c29aba71SPeterChou1     return std::move(BlockComment);
693*c29aba71SPeterChou1   }
694ecfbb850SPaul Kirth   if (I.Kind == "TextComment") {
695671bac74SJulie Hockett     if (I.Text == "")
696671bac74SJulie Hockett       return nullptr;
6971c705d9cSJonas Devlieghere     return std::make_unique<TextNode>(I.Text);
698671bac74SJulie Hockett   }
699671bac74SJulie Hockett   return nullptr;
700671bac74SJulie Hockett }
701671bac74SJulie Hockett 
702671bac74SJulie Hockett static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) {
7031c705d9cSJonas Devlieghere   auto CommentBlock = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
704671bac74SJulie Hockett   for (const auto &Child : C) {
705671bac74SJulie Hockett     if (std::unique_ptr<HTMLNode> Node = genHTML(Child))
706671bac74SJulie Hockett       CommentBlock->Children.emplace_back(std::move(Node));
707671bac74SJulie Hockett   }
708671bac74SJulie Hockett   return CommentBlock;
709671bac74SJulie Hockett }
710671bac74SJulie Hockett 
711665e9676SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
712665e9676SDiego Astiazaran genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
713671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
714ecfbb850SPaul Kirth   std::string EnumType = I.Scoped ? "enum class " : "enum ";
7155ef2456aSPeterChou1   // Determine if enum members have comments attached
7165ef2456aSPeterChou1   bool HasComments = std::any_of(
7175ef2456aSPeterChou1       I.Members.begin(), I.Members.end(),
7185ef2456aSPeterChou1       [](const EnumValueInfo &M) { return !M.Description.empty(); });
7195ef2456aSPeterChou1   std::unique_ptr<TagNode> Table =
7205ef2456aSPeterChou1       std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
7215ef2456aSPeterChou1   std::unique_ptr<TagNode> THead =
7225ef2456aSPeterChou1       std::make_unique<TagNode>(HTMLTag::TAG_THEAD);
7235ef2456aSPeterChou1   std::unique_ptr<TagNode> TRow = std::make_unique<TagNode>(HTMLTag::TAG_TR);
7245ef2456aSPeterChou1   std::unique_ptr<TagNode> TD =
7255ef2456aSPeterChou1       std::make_unique<TagNode>(HTMLTag::TAG_TH, EnumType + I.Name);
7265ef2456aSPeterChou1   // Span 3 columns if enum has comments
7275ef2456aSPeterChou1   TD->Attributes.emplace_back("colspan", HasComments ? "3" : "2");
728671bac74SJulie Hockett 
7295ef2456aSPeterChou1   Table->Attributes.emplace_back("id", llvm::toHex(llvm::toStringRef(I.USR)));
7305ef2456aSPeterChou1   TRow->Children.emplace_back(std::move(TD));
7315ef2456aSPeterChou1   THead->Children.emplace_back(std::move(TRow));
7325ef2456aSPeterChou1   Table->Children.emplace_back(std::move(THead));
733671bac74SJulie Hockett 
7345ef2456aSPeterChou1   if (std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members))
7355ef2456aSPeterChou1     Table->Children.emplace_back(std::move(Node));
7365ef2456aSPeterChou1 
7375ef2456aSPeterChou1   Out.emplace_back(std::move(Table));
738671bac74SJulie Hockett 
739665e9676SDiego Astiazaran   if (I.DefLoc) {
740665e9676SDiego Astiazaran     if (!CDCtx.RepositoryUrl)
74153243f2aSFangrui Song       Out.emplace_back(writeFileDefinition(*I.DefLoc));
742665e9676SDiego Astiazaran     else
7435ef2456aSPeterChou1       Out.emplace_back(
7445ef2456aSPeterChou1           writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
745665e9676SDiego Astiazaran   }
746671bac74SJulie Hockett 
747671bac74SJulie Hockett   std::string Description;
748671bac74SJulie Hockett   if (!I.Description.empty())
749671bac74SJulie Hockett     Out.emplace_back(genHTML(I.Description));
750671bac74SJulie Hockett 
751671bac74SJulie Hockett   return Out;
752671bac74SJulie Hockett }
753671bac74SJulie Hockett 
754665e9676SDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
755665e9676SDiego Astiazaran genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
7562c1c9a24SJulie Hockett         StringRef ParentInfoDir) {
757671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
7581c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name));
759d47be4daSDiego Astiazaran   // USR is used as id for functions instead of name to disambiguate function
760d47be4daSDiego Astiazaran   // overloads.
7613550da79SDiego Astiazaran   Out.back()->Attributes.emplace_back("id",
762d47be4daSDiego Astiazaran                                       llvm::toHex(llvm::toStringRef(I.USR)));
763671bac74SJulie Hockett 
7641c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
7652c1c9a24SJulie Hockett   auto &FunctionHeader = Out.back();
766671bac74SJulie Hockett 
7676407aa9dSDaniel Martín   std::string Access = getAccessSpelling(I.Access).str();
768671bac74SJulie Hockett   if (Access != "")
7692c1c9a24SJulie Hockett     FunctionHeader->Children.emplace_back(
7701c705d9cSJonas Devlieghere         std::make_unique<TextNode>(Access + " "));
7712c1c9a24SJulie Hockett   if (I.ReturnType.Type.Name != "") {
7722c1c9a24SJulie Hockett     FunctionHeader->Children.emplace_back(
773e27f778aSDiego Astiazaran         genReference(I.ReturnType.Type, ParentInfoDir));
7741c705d9cSJonas Devlieghere     FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(" "));
7752c1c9a24SJulie Hockett   }
7762c1c9a24SJulie Hockett   FunctionHeader->Children.emplace_back(
7771c705d9cSJonas Devlieghere       std::make_unique<TextNode>(I.Name + "("));
778671bac74SJulie Hockett 
7792c1c9a24SJulie Hockett   for (const auto &P : I.Params) {
7802c1c9a24SJulie Hockett     if (&P != I.Params.begin())
7811c705d9cSJonas Devlieghere       FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(", "));
782e27f778aSDiego Astiazaran     FunctionHeader->Children.emplace_back(genReference(P.Type, ParentInfoDir));
7832c1c9a24SJulie Hockett     FunctionHeader->Children.emplace_back(
7841c705d9cSJonas Devlieghere         std::make_unique<TextNode>(" " + P.Name));
7852c1c9a24SJulie Hockett   }
7861c705d9cSJonas Devlieghere   FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(")"));
787671bac74SJulie Hockett 
788665e9676SDiego Astiazaran   if (I.DefLoc) {
789665e9676SDiego Astiazaran     if (!CDCtx.RepositoryUrl)
79053243f2aSFangrui Song       Out.emplace_back(writeFileDefinition(*I.DefLoc));
791665e9676SDiego Astiazaran     else
7923b7c3a65SKazu Hirata       Out.emplace_back(writeFileDefinition(
79353243f2aSFangrui Song           *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
794665e9676SDiego Astiazaran   }
795671bac74SJulie Hockett 
796671bac74SJulie Hockett   std::string Description;
797671bac74SJulie Hockett   if (!I.Description.empty())
798671bac74SJulie Hockett     Out.emplace_back(genHTML(I.Description));
799671bac74SJulie Hockett 
800671bac74SJulie Hockett   return Out;
801671bac74SJulie Hockett }
802671bac74SJulie Hockett 
803d47be4daSDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
804665e9676SDiego Astiazaran genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
805665e9676SDiego Astiazaran         std::string &InfoTitle) {
806671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
807671bac74SJulie Hockett   if (I.Name.str() == "")
808671bac74SJulie Hockett     InfoTitle = "Global Namespace";
809671bac74SJulie Hockett   else
810671bac74SJulie Hockett     InfoTitle = ("namespace " + I.Name).str();
811671bac74SJulie Hockett 
8121c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
813671bac74SJulie Hockett 
814671bac74SJulie Hockett   std::string Description;
815671bac74SJulie Hockett   if (!I.Description.empty())
816671bac74SJulie Hockett     Out.emplace_back(genHTML(I.Description));
817671bac74SJulie Hockett 
8187003f64cSPetr Hosek   llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
8197003f64cSPetr Hosek 
820671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
82121fb70c6SBrett Wilson       genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath);
822ecfbb850SPaul Kirth   appendVector(std::move(ChildNamespaces), Out);
823671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> ChildRecords =
82421fb70c6SBrett Wilson       genReferencesBlock(I.Children.Records, "Records", BasePath);
825ecfbb850SPaul Kirth   appendVector(std::move(ChildRecords), Out);
826671bac74SJulie Hockett 
827671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
82821fb70c6SBrett Wilson       genFunctionsBlock(I.Children.Functions, CDCtx, BasePath);
829ecfbb850SPaul Kirth   appendVector(std::move(ChildFunctions), Out);
830671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> ChildEnums =
83121fb70c6SBrett Wilson       genEnumsBlock(I.Children.Enums, CDCtx);
832ecfbb850SPaul Kirth   appendVector(std::move(ChildEnums), Out);
833671bac74SJulie Hockett 
83421fb70c6SBrett Wilson   if (!I.Children.Namespaces.empty())
835d47be4daSDiego Astiazaran     InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
83621fb70c6SBrett Wilson   if (!I.Children.Records.empty())
837d47be4daSDiego Astiazaran     InfoIndex.Children.emplace_back("Records", "Records");
83821fb70c6SBrett Wilson   if (!I.Children.Functions.empty())
839d47be4daSDiego Astiazaran     InfoIndex.Children.emplace_back(
84021fb70c6SBrett Wilson         genInfoIndexItem(I.Children.Functions, "Functions"));
84121fb70c6SBrett Wilson   if (!I.Children.Enums.empty())
84221fb70c6SBrett Wilson     InfoIndex.Children.emplace_back(
84321fb70c6SBrett Wilson         genInfoIndexItem(I.Children.Enums, "Enums"));
844d47be4daSDiego Astiazaran 
845671bac74SJulie Hockett   return Out;
846671bac74SJulie Hockett }
847671bac74SJulie Hockett 
848d47be4daSDiego Astiazaran static std::vector<std::unique_ptr<TagNode>>
849665e9676SDiego Astiazaran genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
850665e9676SDiego Astiazaran         std::string &InfoTitle) {
851671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Out;
852671bac74SJulie Hockett   InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
8531c705d9cSJonas Devlieghere   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
854671bac74SJulie Hockett 
855665e9676SDiego Astiazaran   if (I.DefLoc) {
856665e9676SDiego Astiazaran     if (!CDCtx.RepositoryUrl)
85753243f2aSFangrui Song       Out.emplace_back(writeFileDefinition(*I.DefLoc));
858665e9676SDiego Astiazaran     else
8593b7c3a65SKazu Hirata       Out.emplace_back(writeFileDefinition(
86053243f2aSFangrui Song           *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
861665e9676SDiego Astiazaran   }
862671bac74SJulie Hockett 
863671bac74SJulie Hockett   std::string Description;
864671bac74SJulie Hockett   if (!I.Description.empty())
865671bac74SJulie Hockett     Out.emplace_back(genHTML(I.Description));
866671bac74SJulie Hockett 
8672c1c9a24SJulie Hockett   std::vector<std::unique_ptr<HTMLNode>> Parents =
8682c1c9a24SJulie Hockett       genReferenceList(I.Parents, I.Path);
8692c1c9a24SJulie Hockett   std::vector<std::unique_ptr<HTMLNode>> VParents =
8702c1c9a24SJulie Hockett       genReferenceList(I.VirtualParents, I.Path);
871671bac74SJulie Hockett   if (!Parents.empty() || !VParents.empty()) {
8721c705d9cSJonas Devlieghere     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
8732c1c9a24SJulie Hockett     auto &PBody = Out.back();
8741c705d9cSJonas Devlieghere     PBody->Children.emplace_back(std::make_unique<TextNode>("Inherits from "));
875671bac74SJulie Hockett     if (Parents.empty())
876ecfbb850SPaul Kirth       appendVector(std::move(VParents), PBody->Children);
877671bac74SJulie Hockett     else if (VParents.empty())
878ecfbb850SPaul Kirth       appendVector(std::move(Parents), PBody->Children);
8792c1c9a24SJulie Hockett     else {
880ecfbb850SPaul Kirth       appendVector(std::move(Parents), PBody->Children);
8811c705d9cSJonas Devlieghere       PBody->Children.emplace_back(std::make_unique<TextNode>(", "));
882ecfbb850SPaul Kirth       appendVector(std::move(VParents), PBody->Children);
8832c1c9a24SJulie Hockett     }
884671bac74SJulie Hockett   }
885671bac74SJulie Hockett 
886671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> Members =
8872c1c9a24SJulie Hockett       genRecordMembersBlock(I.Members, I.Path);
888ecfbb850SPaul Kirth   appendVector(std::move(Members), Out);
889671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> ChildRecords =
89021fb70c6SBrett Wilson       genReferencesBlock(I.Children.Records, "Records", I.Path);
891ecfbb850SPaul Kirth   appendVector(std::move(ChildRecords), Out);
892671bac74SJulie Hockett 
893671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
89421fb70c6SBrett Wilson       genFunctionsBlock(I.Children.Functions, CDCtx, I.Path);
895ecfbb850SPaul Kirth   appendVector(std::move(ChildFunctions), Out);
896671bac74SJulie Hockett   std::vector<std::unique_ptr<TagNode>> ChildEnums =
89721fb70c6SBrett Wilson       genEnumsBlock(I.Children.Enums, CDCtx);
898ecfbb850SPaul Kirth   appendVector(std::move(ChildEnums), Out);
899671bac74SJulie Hockett 
900d47be4daSDiego Astiazaran   if (!I.Members.empty())
901d47be4daSDiego Astiazaran     InfoIndex.Children.emplace_back("Members", "Members");
90221fb70c6SBrett Wilson   if (!I.Children.Records.empty())
903d47be4daSDiego Astiazaran     InfoIndex.Children.emplace_back("Records", "Records");
90421fb70c6SBrett Wilson   if (!I.Children.Functions.empty())
905d47be4daSDiego Astiazaran     InfoIndex.Children.emplace_back(
90621fb70c6SBrett Wilson         genInfoIndexItem(I.Children.Functions, "Functions"));
90721fb70c6SBrett Wilson   if (!I.Children.Enums.empty())
90821fb70c6SBrett Wilson     InfoIndex.Children.emplace_back(
90921fb70c6SBrett Wilson         genInfoIndexItem(I.Children.Enums, "Enums"));
910d47be4daSDiego Astiazaran 
911671bac74SJulie Hockett   return Out;
912671bac74SJulie Hockett }
913671bac74SJulie Hockett 
91421fb70c6SBrett Wilson static std::vector<std::unique_ptr<TagNode>>
91521fb70c6SBrett Wilson genHTML(const TypedefInfo &I, const ClangDocContext &CDCtx,
91621fb70c6SBrett Wilson         std::string &InfoTitle) {
91721fb70c6SBrett Wilson   // TODO support typedefs in HTML.
91821fb70c6SBrett Wilson   return {};
91921fb70c6SBrett Wilson }
92021fb70c6SBrett Wilson 
921671bac74SJulie Hockett /// Generator for HTML documentation.
922671bac74SJulie Hockett class HTMLGenerator : public Generator {
923671bac74SJulie Hockett public:
924671bac74SJulie Hockett   static const char *Format;
925671bac74SJulie Hockett 
9267b8c7e02SBrett Wilson   llvm::Error generateDocs(StringRef RootDir,
9277b8c7e02SBrett Wilson                            llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
928f8a469fcSBrett Wilson                            const ClangDocContext &CDCtx) override;
92948bb1471SPaul Kirth   llvm::Error createResources(ClangDocContext &CDCtx) override;
9307b8c7e02SBrett Wilson   llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
9317b8c7e02SBrett Wilson                                  const ClangDocContext &CDCtx) override;
932671bac74SJulie Hockett };
933671bac74SJulie Hockett 
934671bac74SJulie Hockett const char *HTMLGenerator::Format = "html";
935671bac74SJulie Hockett 
9367b8c7e02SBrett Wilson llvm::Error
9377b8c7e02SBrett Wilson HTMLGenerator::generateDocs(StringRef RootDir,
9387b8c7e02SBrett Wilson                             llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
9397b8c7e02SBrett Wilson                             const ClangDocContext &CDCtx) {
9407b8c7e02SBrett Wilson   // Track which directories we already tried to create.
9417b8c7e02SBrett Wilson   llvm::StringSet<> CreatedDirs;
9427b8c7e02SBrett Wilson 
9437b8c7e02SBrett Wilson   // Collect all output by file name and create the nexessary directories.
9447b8c7e02SBrett Wilson   llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
9457b8c7e02SBrett Wilson   for (const auto &Group : Infos) {
9467b8c7e02SBrett Wilson     doc::Info *Info = Group.getValue().get();
9477b8c7e02SBrett Wilson 
9487b8c7e02SBrett Wilson     llvm::SmallString<128> Path;
9497b8c7e02SBrett Wilson     llvm::sys::path::native(RootDir, Path);
9507b8c7e02SBrett Wilson     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
95115aa9653SKazu Hirata     if (!CreatedDirs.contains(Path)) {
9527b8c7e02SBrett Wilson       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
9537b8c7e02SBrett Wilson           Err != std::error_code()) {
9547b8c7e02SBrett Wilson         return llvm::createStringError(Err, "Failed to create directory '%s'.",
9557b8c7e02SBrett Wilson                                        Path.c_str());
9567b8c7e02SBrett Wilson       }
9577b8c7e02SBrett Wilson       CreatedDirs.insert(Path);
9587b8c7e02SBrett Wilson     }
9597b8c7e02SBrett Wilson 
9607b8c7e02SBrett Wilson     llvm::sys::path::append(Path, Info->getFileBaseName() + ".html");
9617b8c7e02SBrett Wilson     FileToInfos[Path].push_back(Info);
9627b8c7e02SBrett Wilson   }
9637b8c7e02SBrett Wilson 
9647b8c7e02SBrett Wilson   for (const auto &Group : FileToInfos) {
9657b8c7e02SBrett Wilson     std::error_code FileErr;
9667b8c7e02SBrett Wilson     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
9677b8c7e02SBrett Wilson                                 llvm::sys::fs::OF_None);
9687b8c7e02SBrett Wilson     if (FileErr) {
9697b8c7e02SBrett Wilson       return llvm::createStringError(FileErr, "Error opening file '%s'",
9707b8c7e02SBrett Wilson                                      Group.getKey().str().c_str());
9717b8c7e02SBrett Wilson     }
9727b8c7e02SBrett Wilson 
9737b8c7e02SBrett Wilson     // TODO: https://github.com/llvm/llvm-project/issues/59073
9747b8c7e02SBrett Wilson     // If there are multiple Infos for this file name (for example, template
9757b8c7e02SBrett Wilson     // specializations), this will generate multiple complete web pages (with
9767b8c7e02SBrett Wilson     // <DOCTYPE> and <title>, etc.) concatenated together. This generator needs
9777b8c7e02SBrett Wilson     // some refactoring to be able to output the headers separately from the
9787b8c7e02SBrett Wilson     // contents.
9797b8c7e02SBrett Wilson     for (const auto &Info : Group.getValue()) {
9807b8c7e02SBrett Wilson       if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
9817b8c7e02SBrett Wilson         return Err;
9827b8c7e02SBrett Wilson       }
9837b8c7e02SBrett Wilson     }
9847b8c7e02SBrett Wilson   }
9857b8c7e02SBrett Wilson 
9867b8c7e02SBrett Wilson   return llvm::Error::success();
9877b8c7e02SBrett Wilson }
9887b8c7e02SBrett Wilson 
989acd35f6cSDiego Astiazaran llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
990acd35f6cSDiego Astiazaran                                               const ClangDocContext &CDCtx) {
991671bac74SJulie Hockett   std::string InfoTitle;
9923550da79SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> MainContentNodes;
993d47be4daSDiego Astiazaran   Index InfoIndex;
994671bac74SJulie Hockett   switch (I->IT) {
9953550da79SDiego Astiazaran   case InfoType::IT_namespace:
9963550da79SDiego Astiazaran     MainContentNodes = genHTML(*static_cast<clang::doc::NamespaceInfo *>(I),
9973550da79SDiego Astiazaran                                InfoIndex, CDCtx, InfoTitle);
998671bac74SJulie Hockett     break;
9993550da79SDiego Astiazaran   case InfoType::IT_record:
10003550da79SDiego Astiazaran     MainContentNodes = genHTML(*static_cast<clang::doc::RecordInfo *>(I),
10013550da79SDiego Astiazaran                                InfoIndex, CDCtx, InfoTitle);
1002671bac74SJulie Hockett     break;
10033550da79SDiego Astiazaran   case InfoType::IT_enum:
10043550da79SDiego Astiazaran     MainContentNodes = genHTML(*static_cast<clang::doc::EnumInfo *>(I), CDCtx);
1005671bac74SJulie Hockett     break;
10063550da79SDiego Astiazaran   case InfoType::IT_function:
10073550da79SDiego Astiazaran     MainContentNodes =
1008665e9676SDiego Astiazaran         genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
1009671bac74SJulie Hockett     break;
101021fb70c6SBrett Wilson   case InfoType::IT_typedef:
101121fb70c6SBrett Wilson     MainContentNodes =
101221fb70c6SBrett Wilson         genHTML(*static_cast<clang::doc::TypedefInfo *>(I), CDCtx, InfoTitle);
101321fb70c6SBrett Wilson     break;
1014671bac74SJulie Hockett   case InfoType::IT_default:
101518038065SFangrui Song     return llvm::createStringError(llvm::inconvertibleErrorCode(),
101618038065SFangrui Song                                    "unexpected info type");
1017671bac74SJulie Hockett   }
1018671bac74SJulie Hockett 
10197003f64cSPetr Hosek   HTMLFile F = genInfoFile(InfoTitle, I->getRelativeFilePath(""),
10207003f64cSPetr Hosek                            MainContentNodes, InfoIndex, CDCtx);
1021ecfbb850SPaul Kirth   F.render(OS);
1022671bac74SJulie Hockett 
1023671bac74SJulie Hockett   return llvm::Error::success();
1024671bac74SJulie Hockett }
1025671bac74SJulie Hockett 
10267dfe0bc3SDiego Astiazaran static std::string getRefType(InfoType IT) {
10277dfe0bc3SDiego Astiazaran   switch (IT) {
10287dfe0bc3SDiego Astiazaran   case InfoType::IT_default:
10297dfe0bc3SDiego Astiazaran     return "default";
10307dfe0bc3SDiego Astiazaran   case InfoType::IT_namespace:
10317dfe0bc3SDiego Astiazaran     return "namespace";
10327dfe0bc3SDiego Astiazaran   case InfoType::IT_record:
10337dfe0bc3SDiego Astiazaran     return "record";
10347dfe0bc3SDiego Astiazaran   case InfoType::IT_function:
10357dfe0bc3SDiego Astiazaran     return "function";
10367dfe0bc3SDiego Astiazaran   case InfoType::IT_enum:
10377dfe0bc3SDiego Astiazaran     return "enum";
103821fb70c6SBrett Wilson   case InfoType::IT_typedef:
103921fb70c6SBrett Wilson     return "typedef";
10407dfe0bc3SDiego Astiazaran   }
10417dfe0bc3SDiego Astiazaran   llvm_unreachable("Unknown InfoType");
10427dfe0bc3SDiego Astiazaran }
10437dfe0bc3SDiego Astiazaran 
1044ecfbb850SPaul Kirth static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
1045db5d8e3dSDiego Astiazaran   std::error_code OK;
10467dfe0bc3SDiego Astiazaran   std::error_code FileErr;
10477dfe0bc3SDiego Astiazaran   llvm::SmallString<128> FilePath;
10487dfe0bc3SDiego Astiazaran   llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
10497dfe0bc3SDiego Astiazaran   llvm::sys::path::append(FilePath, "index_json.js");
1050ff354de2SFangrui Song   llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
1051db5d8e3dSDiego Astiazaran   if (FileErr != OK) {
105218038065SFangrui Song     return llvm::createStringError(llvm::inconvertibleErrorCode(),
105318038065SFangrui Song                                    "error creating index file: " +
105418038065SFangrui Song                                        FileErr.message());
10557dfe0bc3SDiego Astiazaran   }
105691450f1bSPeterChou1   llvm::SmallString<128> RootPath(CDCtx.OutDirectory);
105791450f1bSPeterChou1   if (llvm::sys::path::is_relative(RootPath)) {
105891450f1bSPeterChou1     llvm::sys::fs::make_absolute(RootPath);
105991450f1bSPeterChou1   }
106091450f1bSPeterChou1   // Replace the escaped characters with a forward slash. It shouldn't matter
106191450f1bSPeterChou1   // when rendering the webpage in a web browser. This helps to prevent the
106291450f1bSPeterChou1   // JavaScript from escaping characters incorrectly, and introducing  bad paths
106391450f1bSPeterChou1   // in the URLs.
106491450f1bSPeterChou1   std::string RootPathEscaped = RootPath.str().str();
106591450f1bSPeterChou1   std::replace(RootPathEscaped.begin(), RootPathEscaped.end(), '\\', '/');
106691450f1bSPeterChou1   OS << "var RootPath = \"" << RootPathEscaped << "\";\n";
106791450f1bSPeterChou1 
10687dfe0bc3SDiego Astiazaran   CDCtx.Idx.sort();
10697dfe0bc3SDiego Astiazaran   llvm::json::OStream J(OS, 2);
10701c502c63SSimon Pilgrim   std::function<void(Index)> IndexToJSON = [&](const Index &I) {
10717dfe0bc3SDiego Astiazaran     J.object([&] {
1072fb55db54SPeterChou1       J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
10737dfe0bc3SDiego Astiazaran       J.attribute("Name", I.Name);
10747dfe0bc3SDiego Astiazaran       J.attribute("RefType", getRefType(I.RefType));
10757003f64cSPetr Hosek       J.attribute("Path", I.getRelativeFilePath(""));
10767dfe0bc3SDiego Astiazaran       J.attributeArray("Children", [&] {
10777dfe0bc3SDiego Astiazaran         for (const Index &C : I.Children)
10787dfe0bc3SDiego Astiazaran           IndexToJSON(C);
10797dfe0bc3SDiego Astiazaran       });
10807dfe0bc3SDiego Astiazaran     });
10817dfe0bc3SDiego Astiazaran   };
1082a9b1e80aSPeterChou1   OS << "async function LoadIndex() {\nreturn";
10837dfe0bc3SDiego Astiazaran   IndexToJSON(CDCtx.Idx);
1084a9b1e80aSPeterChou1   OS << ";\n}";
108572e1f7f9SJulie Hockett   return llvm::Error::success();
10867dfe0bc3SDiego Astiazaran }
10877dfe0bc3SDiego Astiazaran 
10883550da79SDiego Astiazaran // Generates a main HTML node that has the main content of the file that shows
10893550da79SDiego Astiazaran // only the general index
10903550da79SDiego Astiazaran // It contains the general index with links to all the generated files
10913550da79SDiego Astiazaran static std::unique_ptr<TagNode> genIndexFileMainNode() {
10923550da79SDiego Astiazaran   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
10933550da79SDiego Astiazaran 
10943550da79SDiego Astiazaran   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
10953550da79SDiego Astiazaran   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
10963550da79SDiego Astiazaran   LeftSidebarNode->Attributes.emplace_back("path", "");
10973550da79SDiego Astiazaran   LeftSidebarNode->Attributes.emplace_back(
10983550da79SDiego Astiazaran       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
10993550da79SDiego Astiazaran   LeftSidebarNode->Attributes.emplace_back("style", "flex: 0 100%;");
11003550da79SDiego Astiazaran 
11013550da79SDiego Astiazaran   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
11023550da79SDiego Astiazaran 
11033550da79SDiego Astiazaran   return MainNode;
11043550da79SDiego Astiazaran }
11053550da79SDiego Astiazaran 
1106ecfbb850SPaul Kirth static llvm::Error genIndex(const ClangDocContext &CDCtx) {
1107dc30049cSDiego Astiazaran   std::error_code FileErr, OK;
1108dc30049cSDiego Astiazaran   llvm::SmallString<128> IndexPath;
1109dc30049cSDiego Astiazaran   llvm::sys::path::native(CDCtx.OutDirectory, IndexPath);
1110dc30049cSDiego Astiazaran   llvm::sys::path::append(IndexPath, "index.html");
1111ff354de2SFangrui Song   llvm::raw_fd_ostream IndexOS(IndexPath, FileErr, llvm::sys::fs::OF_None);
1112dc30049cSDiego Astiazaran   if (FileErr != OK) {
111318038065SFangrui Song     return llvm::createStringError(llvm::inconvertibleErrorCode(),
111418038065SFangrui Song                                    "error creating main index: " +
111518038065SFangrui Song                                        FileErr.message());
1116dc30049cSDiego Astiazaran   }
11173550da79SDiego Astiazaran 
1118dc30049cSDiego Astiazaran   HTMLFile F;
11193550da79SDiego Astiazaran 
11203550da79SDiego Astiazaran   std::vector<std::unique_ptr<TagNode>> HeadNodes =
11213550da79SDiego Astiazaran       genFileHeadNodes("Index", "", CDCtx);
11223550da79SDiego Astiazaran   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
11233550da79SDiego Astiazaran   std::unique_ptr<TagNode> MainNode = genIndexFileMainNode();
11243550da79SDiego Astiazaran   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
11253550da79SDiego Astiazaran 
1126ecfbb850SPaul Kirth   appendVector(std::move(HeadNodes), F.Children);
11273550da79SDiego Astiazaran   F.Children.emplace_back(std::move(HeaderNode));
11283550da79SDiego Astiazaran   F.Children.emplace_back(std::move(MainNode));
11293550da79SDiego Astiazaran   F.Children.emplace_back(std::move(FooterNode));
11303550da79SDiego Astiazaran 
1131ecfbb850SPaul Kirth   F.render(IndexOS);
11323550da79SDiego Astiazaran 
113372e1f7f9SJulie Hockett   return llvm::Error::success();
1134dc30049cSDiego Astiazaran }
1135dc30049cSDiego Astiazaran 
1136ecfbb850SPaul Kirth static llvm::Error copyFile(StringRef FilePath, StringRef OutDirectory) {
11377dfe0bc3SDiego Astiazaran   llvm::SmallString<128> PathWrite;
11387dfe0bc3SDiego Astiazaran   llvm::sys::path::native(OutDirectory, PathWrite);
11397dfe0bc3SDiego Astiazaran   llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
11407dfe0bc3SDiego Astiazaran   llvm::SmallString<128> PathRead;
11417dfe0bc3SDiego Astiazaran   llvm::sys::path::native(FilePath, PathRead);
11427dfe0bc3SDiego Astiazaran   std::error_code OK;
11437dfe0bc3SDiego Astiazaran   std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
11447dfe0bc3SDiego Astiazaran   if (FileErr != OK) {
114518038065SFangrui Song     return llvm::createStringError(llvm::inconvertibleErrorCode(),
114618038065SFangrui Song                                    "error creating file " +
114718038065SFangrui Song                                        llvm::sys::path::filename(FilePath) +
114818038065SFangrui Song                                        ": " + FileErr.message() + "\n");
1149db5d8e3dSDiego Astiazaran   }
115072e1f7f9SJulie Hockett   return llvm::Error::success();
1151acd35f6cSDiego Astiazaran }
11527dfe0bc3SDiego Astiazaran 
115372e1f7f9SJulie Hockett llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
1154ecfbb850SPaul Kirth   auto Err = serializeIndex(CDCtx);
115572e1f7f9SJulie Hockett   if (Err)
115672e1f7f9SJulie Hockett     return Err;
1157ecfbb850SPaul Kirth   Err = genIndex(CDCtx);
115872e1f7f9SJulie Hockett   if (Err)
115972e1f7f9SJulie Hockett     return Err;
116072e1f7f9SJulie Hockett 
116172e1f7f9SJulie Hockett   for (const auto &FilePath : CDCtx.UserStylesheets) {
1162ecfbb850SPaul Kirth     Err = copyFile(FilePath, CDCtx.OutDirectory);
116372e1f7f9SJulie Hockett     if (Err)
116472e1f7f9SJulie Hockett       return Err;
116572e1f7f9SJulie Hockett   }
1166f14ad744SPeterChou1   for (const auto &FilePath : CDCtx.JsScripts) {
1167ecfbb850SPaul Kirth     Err = copyFile(FilePath, CDCtx.OutDirectory);
116872e1f7f9SJulie Hockett     if (Err)
116972e1f7f9SJulie Hockett       return Err;
117072e1f7f9SJulie Hockett   }
117172e1f7f9SJulie Hockett   return llvm::Error::success();
1172db5d8e3dSDiego Astiazaran }
1173db5d8e3dSDiego Astiazaran 
1174671bac74SJulie Hockett static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format,
1175671bac74SJulie Hockett                                                   "Generator for HTML output.");
1176671bac74SJulie Hockett 
1177671bac74SJulie Hockett // This anchor is used to force the linker to link in the generated object
1178671bac74SJulie Hockett // file and thus register the generator.
1179671bac74SJulie Hockett volatile int HTMLGeneratorAnchorSource = 0;
1180671bac74SJulie Hockett 
1181671bac74SJulie Hockett } // namespace doc
1182671bac74SJulie Hockett } // namespace clang
1183