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