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