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 "llvm/ADT/StringExtras.h" 12 #include "llvm/ADT/StringRef.h" 13 #include "llvm/Support/FileSystem.h" 14 #include "llvm/Support/Path.h" 15 #include <string> 16 17 using namespace llvm; 18 19 namespace clang { 20 namespace doc { 21 22 namespace { 23 24 class HTMLTag { 25 public: 26 // Any other tag can be added if required 27 enum TagType { 28 TAG_META, 29 TAG_TITLE, 30 TAG_DIV, 31 TAG_H1, 32 TAG_H2, 33 TAG_H3, 34 TAG_P, 35 TAG_UL, 36 TAG_LI, 37 TAG_A, 38 TAG_LINK, 39 }; 40 41 HTMLTag() = default; 42 constexpr HTMLTag(TagType Value) : Value(Value) {} 43 44 operator TagType() const { return Value; } 45 operator bool() = delete; 46 47 bool IsSelfClosing() const; 48 llvm::SmallString<16> ToString() const; 49 50 private: 51 TagType Value; 52 }; 53 54 enum NodeType { 55 NODE_TEXT, 56 NODE_TAG, 57 }; 58 59 struct HTMLNode { 60 HTMLNode(NodeType Type) : Type(Type) {} 61 virtual ~HTMLNode() = default; 62 63 virtual void Render(llvm::raw_ostream &OS, int IndentationLevel) = 0; 64 NodeType Type; // Type of node 65 }; 66 67 struct TextNode : public HTMLNode { 68 TextNode(const Twine &Text) 69 : HTMLNode(NodeType::NODE_TEXT), Text(Text.str()) {} 70 71 std::string Text; // Content of node 72 void Render(llvm::raw_ostream &OS, int IndentationLevel) override; 73 }; 74 75 struct TagNode : public HTMLNode { 76 TagNode(HTMLTag Tag) : HTMLNode(NodeType::NODE_TAG), Tag(Tag) {} 77 TagNode(HTMLTag Tag, const Twine &Text) : TagNode(Tag) { 78 Children.emplace_back(llvm::make_unique<TextNode>(Text.str())); 79 } 80 81 HTMLTag Tag; // Name of HTML Tag (p, div, h1) 82 std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes 83 llvm::StringMap<llvm::SmallString<16>> 84 Attributes; // List of key-value attributes for tag 85 86 void Render(llvm::raw_ostream &OS, int IndentationLevel) override; 87 }; 88 89 constexpr const char *kDoctypeDecl = "<!DOCTYPE html>"; 90 91 struct HTMLFile { 92 std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes 93 void Render(llvm::raw_ostream &OS) { 94 OS << kDoctypeDecl << "\n"; 95 for (const auto &C : Children) { 96 C->Render(OS, 0); 97 OS << "\n"; 98 } 99 } 100 }; 101 102 } // namespace 103 104 bool HTMLTag::IsSelfClosing() const { 105 switch (Value) { 106 case HTMLTag::TAG_META: 107 case HTMLTag::TAG_LINK: 108 return true; 109 case HTMLTag::TAG_TITLE: 110 case HTMLTag::TAG_DIV: 111 case HTMLTag::TAG_H1: 112 case HTMLTag::TAG_H2: 113 case HTMLTag::TAG_H3: 114 case HTMLTag::TAG_P: 115 case HTMLTag::TAG_UL: 116 case HTMLTag::TAG_LI: 117 case HTMLTag::TAG_A: 118 return false; 119 } 120 llvm_unreachable("Unhandled HTMLTag::TagType"); 121 } 122 123 llvm::SmallString<16> HTMLTag::ToString() const { 124 switch (Value) { 125 case HTMLTag::TAG_META: 126 return llvm::SmallString<16>("meta"); 127 case HTMLTag::TAG_TITLE: 128 return llvm::SmallString<16>("title"); 129 case HTMLTag::TAG_DIV: 130 return llvm::SmallString<16>("div"); 131 case HTMLTag::TAG_H1: 132 return llvm::SmallString<16>("h1"); 133 case HTMLTag::TAG_H2: 134 return llvm::SmallString<16>("h2"); 135 case HTMLTag::TAG_H3: 136 return llvm::SmallString<16>("h3"); 137 case HTMLTag::TAG_P: 138 return llvm::SmallString<16>("p"); 139 case HTMLTag::TAG_UL: 140 return llvm::SmallString<16>("ul"); 141 case HTMLTag::TAG_LI: 142 return llvm::SmallString<16>("li"); 143 case HTMLTag::TAG_A: 144 return llvm::SmallString<16>("a"); 145 case HTMLTag::TAG_LINK: 146 return llvm::SmallString<16>("link"); 147 } 148 llvm_unreachable("Unhandled HTMLTag::TagType"); 149 } 150 151 void TextNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { 152 OS.indent(IndentationLevel * 2); 153 printHTMLEscaped(Text, OS); 154 } 155 156 void TagNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { 157 // Children nodes are rendered in the same line if all of them are text nodes 158 bool InlineChildren = true; 159 for (const auto &C : Children) 160 if (C->Type == NodeType::NODE_TAG) { 161 InlineChildren = false; 162 break; 163 } 164 OS.indent(IndentationLevel * 2); 165 OS << "<" << Tag.ToString(); 166 for (const auto &A : Attributes) 167 OS << " " << A.getKey() << "=\"" << A.getValue() << "\""; 168 if (Tag.IsSelfClosing()) { 169 OS << "/>"; 170 return; 171 } 172 OS << ">"; 173 if (!InlineChildren) 174 OS << "\n"; 175 bool NewLineRendered = true; 176 for (const auto &C : Children) { 177 int ChildrenIndentation = 178 InlineChildren || !NewLineRendered ? 0 : IndentationLevel + 1; 179 C->Render(OS, ChildrenIndentation); 180 if (!InlineChildren && (C == Children.back() || 181 (C->Type != NodeType::NODE_TEXT || 182 (&C + 1)->get()->Type != NodeType::NODE_TEXT))) { 183 OS << "\n"; 184 NewLineRendered = true; 185 } else 186 NewLineRendered = false; 187 } 188 if (!InlineChildren) 189 OS.indent(IndentationLevel * 2); 190 OS << "</" << Tag.ToString() << ">"; 191 } 192 193 template <typename Derived, typename Base, 194 typename = std::enable_if<std::is_base_of<Derived, Base>::value>> 195 static void AppendVector(std::vector<Derived> &&New, 196 std::vector<Base> &Original) { 197 std::move(New.begin(), New.end(), std::back_inserter(Original)); 198 } 199 200 // Compute the relative path that names the file path relative to the given 201 // directory. 202 static SmallString<128> computeRelativePath(StringRef FilePath, 203 StringRef Directory) { 204 StringRef Path = FilePath; 205 while (!Path.empty()) { 206 if (Directory == Path) 207 return FilePath.substr(Path.size()); 208 Path = llvm::sys::path::parent_path(Path); 209 } 210 211 StringRef Dir = Directory; 212 SmallString<128> Result; 213 while (!Dir.empty()) { 214 if (Dir == FilePath) 215 break; 216 Dir = llvm::sys::path::parent_path(Dir); 217 llvm::sys::path::append(Result, ".."); 218 } 219 llvm::sys::path::append(Result, FilePath.substr(Dir.size())); 220 return Result; 221 } 222 223 // HTML generation 224 225 std::vector<std::unique_ptr<TagNode>> 226 genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) { 227 std::vector<std::unique_ptr<TagNode>> Out; 228 for (const auto &FilePath : CDCtx.UserStylesheets) { 229 auto LinkNode = llvm::make_unique<TagNode>(HTMLTag::TAG_LINK); 230 LinkNode->Attributes.try_emplace("rel", "stylesheet"); 231 SmallString<128> StylesheetPath = computeRelativePath("", InfoPath); 232 llvm::sys::path::append(StylesheetPath, 233 llvm::sys::path::filename(FilePath)); 234 // Paths in HTML must be in posix-style 235 llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix); 236 LinkNode->Attributes.try_emplace("href", StylesheetPath); 237 Out.emplace_back(std::move(LinkNode)); 238 } 239 return Out; 240 } 241 242 static std::unique_ptr<TagNode> genLink(const Twine &Text, const Twine &Link) { 243 auto LinkNode = llvm::make_unique<TagNode>(HTMLTag::TAG_A, Text); 244 LinkNode->Attributes.try_emplace("href", Link.str()); 245 return LinkNode; 246 } 247 248 static std::unique_ptr<HTMLNode> genTypeReference(const Reference &Type, 249 StringRef CurrentDirectory) { 250 if (Type.Path.empty() && !Type.IsInGlobalNamespace) 251 return llvm::make_unique<TextNode>(Type.Name); 252 llvm::SmallString<128> Path = 253 computeRelativePath(Type.Path, CurrentDirectory); 254 llvm::sys::path::append(Path, Type.Name + ".html"); 255 // Paths in HTML must be in posix-style 256 llvm::sys::path::native(Path, llvm::sys::path::Style::posix); 257 return genLink(Type.Name, Path); 258 } 259 260 static std::vector<std::unique_ptr<HTMLNode>> 261 genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs, 262 const StringRef &CurrentDirectory) { 263 std::vector<std::unique_ptr<HTMLNode>> Out; 264 for (const auto &R : Refs) { 265 if (&R != Refs.begin()) 266 Out.emplace_back(llvm::make_unique<TextNode>(", ")); 267 Out.emplace_back(genTypeReference(R, CurrentDirectory)); 268 } 269 return Out; 270 } 271 272 static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I); 273 static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I, 274 StringRef ParentInfoDir); 275 276 static std::vector<std::unique_ptr<TagNode>> 277 genEnumsBlock(const std::vector<EnumInfo> &Enums) { 278 if (Enums.empty()) 279 return {}; 280 281 std::vector<std::unique_ptr<TagNode>> Out; 282 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Enums")); 283 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV)); 284 auto &DivBody = Out.back(); 285 for (const auto &E : Enums) { 286 std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E); 287 AppendVector(std::move(Nodes), DivBody->Children); 288 } 289 return Out; 290 } 291 292 static std::unique_ptr<TagNode> 293 genEnumMembersBlock(const llvm::SmallVector<SmallString<16>, 4> &Members) { 294 if (Members.empty()) 295 return nullptr; 296 297 auto List = llvm::make_unique<TagNode>(HTMLTag::TAG_UL); 298 for (const auto &M : Members) 299 List->Children.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_LI, M)); 300 return List; 301 } 302 303 static std::vector<std::unique_ptr<TagNode>> 304 genFunctionsBlock(const std::vector<FunctionInfo> &Functions, 305 StringRef ParentInfoDir) { 306 if (Functions.empty()) 307 return {}; 308 309 std::vector<std::unique_ptr<TagNode>> Out; 310 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions")); 311 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV)); 312 auto &DivBody = Out.back(); 313 for (const auto &F : Functions) { 314 std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(F, ParentInfoDir); 315 AppendVector(std::move(Nodes), DivBody->Children); 316 } 317 return Out; 318 } 319 320 static std::vector<std::unique_ptr<TagNode>> 321 genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members, 322 StringRef ParentInfoDir) { 323 if (Members.empty()) 324 return {}; 325 326 std::vector<std::unique_ptr<TagNode>> Out; 327 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Members")); 328 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL)); 329 auto &ULBody = Out.back(); 330 for (const auto &M : Members) { 331 std::string Access = getAccess(M.Access); 332 if (Access != "") 333 Access = Access + " "; 334 auto LIBody = llvm::make_unique<TagNode>(HTMLTag::TAG_LI); 335 LIBody->Children.emplace_back(llvm::make_unique<TextNode>(Access)); 336 LIBody->Children.emplace_back(genTypeReference(M.Type, ParentInfoDir)); 337 LIBody->Children.emplace_back(llvm::make_unique<TextNode>(" " + M.Name)); 338 ULBody->Children.emplace_back(std::move(LIBody)); 339 } 340 return Out; 341 } 342 343 static std::vector<std::unique_ptr<TagNode>> 344 genReferencesBlock(const std::vector<Reference> &References, 345 llvm::StringRef Title) { 346 if (References.empty()) 347 return {}; 348 349 std::vector<std::unique_ptr<TagNode>> Out; 350 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, Title)); 351 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL)); 352 auto &ULBody = Out.back(); 353 for (const auto &R : References) 354 ULBody->Children.emplace_back( 355 llvm::make_unique<TagNode>(HTMLTag::TAG_LI, R.Name)); 356 return Out; 357 } 358 359 static std::unique_ptr<TagNode> writeFileDefinition(const Location &L) { 360 return llvm::make_unique<TagNode>( 361 HTMLTag::TAG_P, 362 "Defined at line " + std::to_string(L.LineNumber) + " of " + L.Filename); 363 } 364 365 static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) { 366 if (I.Kind == "FullComment") { 367 auto FullComment = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); 368 for (const auto &Child : I.Children) { 369 std::unique_ptr<HTMLNode> Node = genHTML(*Child); 370 if (Node) 371 FullComment->Children.emplace_back(std::move(Node)); 372 } 373 return std::move(FullComment); 374 } else if (I.Kind == "ParagraphComment") { 375 auto ParagraphComment = llvm::make_unique<TagNode>(HTMLTag::TAG_P); 376 for (const auto &Child : I.Children) { 377 std::unique_ptr<HTMLNode> Node = genHTML(*Child); 378 if (Node) 379 ParagraphComment->Children.emplace_back(std::move(Node)); 380 } 381 if (ParagraphComment->Children.empty()) 382 return nullptr; 383 return std::move(ParagraphComment); 384 } else if (I.Kind == "TextComment") { 385 if (I.Text == "") 386 return nullptr; 387 return llvm::make_unique<TextNode>(I.Text); 388 } 389 return nullptr; 390 } 391 392 static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) { 393 auto CommentBlock = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); 394 for (const auto &Child : C) { 395 if (std::unique_ptr<HTMLNode> Node = genHTML(Child)) 396 CommentBlock->Children.emplace_back(std::move(Node)); 397 } 398 return CommentBlock; 399 } 400 401 static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I) { 402 std::vector<std::unique_ptr<TagNode>> Out; 403 std::string EnumType; 404 if (I.Scoped) 405 EnumType = "enum class "; 406 else 407 EnumType = "enum "; 408 409 Out.emplace_back( 410 llvm::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name)); 411 412 std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members); 413 if (Node) 414 Out.emplace_back(std::move(Node)); 415 416 if (I.DefLoc) 417 Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); 418 419 std::string Description; 420 if (!I.Description.empty()) 421 Out.emplace_back(genHTML(I.Description)); 422 423 return Out; 424 } 425 426 static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I, 427 StringRef ParentInfoDir) { 428 std::vector<std::unique_ptr<TagNode>> Out; 429 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name)); 430 431 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P)); 432 auto &FunctionHeader = Out.back(); 433 434 std::string Access = getAccess(I.Access); 435 if (Access != "") 436 FunctionHeader->Children.emplace_back( 437 llvm::make_unique<TextNode>(Access + " ")); 438 if (I.ReturnType.Type.Name != "") { 439 FunctionHeader->Children.emplace_back( 440 genTypeReference(I.ReturnType.Type, ParentInfoDir)); 441 FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(" ")); 442 } 443 FunctionHeader->Children.emplace_back( 444 llvm::make_unique<TextNode>(I.Name + "(")); 445 446 for (const auto &P : I.Params) { 447 if (&P != I.Params.begin()) 448 FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(", ")); 449 FunctionHeader->Children.emplace_back( 450 genTypeReference(P.Type, ParentInfoDir)); 451 FunctionHeader->Children.emplace_back( 452 llvm::make_unique<TextNode>(" " + P.Name)); 453 } 454 FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(")")); 455 456 if (I.DefLoc) 457 Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); 458 459 std::string Description; 460 if (!I.Description.empty()) 461 Out.emplace_back(genHTML(I.Description)); 462 463 return Out; 464 } 465 466 static std::vector<std::unique_ptr<TagNode>> genHTML(const NamespaceInfo &I, 467 std::string &InfoTitle) { 468 std::vector<std::unique_ptr<TagNode>> Out; 469 if (I.Name.str() == "") 470 InfoTitle = "Global Namespace"; 471 else 472 InfoTitle = ("namespace " + I.Name).str(); 473 474 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle)); 475 476 std::string Description; 477 if (!I.Description.empty()) 478 Out.emplace_back(genHTML(I.Description)); 479 480 std::vector<std::unique_ptr<TagNode>> ChildNamespaces = 481 genReferencesBlock(I.ChildNamespaces, "Namespaces"); 482 AppendVector(std::move(ChildNamespaces), Out); 483 std::vector<std::unique_ptr<TagNode>> ChildRecords = 484 genReferencesBlock(I.ChildRecords, "Records"); 485 AppendVector(std::move(ChildRecords), Out); 486 487 std::vector<std::unique_ptr<TagNode>> ChildFunctions = 488 genFunctionsBlock(I.ChildFunctions, I.Path); 489 AppendVector(std::move(ChildFunctions), Out); 490 std::vector<std::unique_ptr<TagNode>> ChildEnums = 491 genEnumsBlock(I.ChildEnums); 492 AppendVector(std::move(ChildEnums), Out); 493 494 return Out; 495 } 496 497 static std::vector<std::unique_ptr<TagNode>> genHTML(const RecordInfo &I, 498 std::string &InfoTitle) { 499 std::vector<std::unique_ptr<TagNode>> Out; 500 InfoTitle = (getTagType(I.TagType) + " " + I.Name).str(); 501 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle)); 502 503 if (I.DefLoc) 504 Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); 505 506 std::string Description; 507 if (!I.Description.empty()) 508 Out.emplace_back(genHTML(I.Description)); 509 510 std::vector<std::unique_ptr<HTMLNode>> Parents = 511 genReferenceList(I.Parents, I.Path); 512 std::vector<std::unique_ptr<HTMLNode>> VParents = 513 genReferenceList(I.VirtualParents, I.Path); 514 if (!Parents.empty() || !VParents.empty()) { 515 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P)); 516 auto &PBody = Out.back(); 517 PBody->Children.emplace_back(llvm::make_unique<TextNode>("Inherits from ")); 518 if (Parents.empty()) 519 AppendVector(std::move(VParents), PBody->Children); 520 else if (VParents.empty()) 521 AppendVector(std::move(Parents), PBody->Children); 522 else { 523 AppendVector(std::move(Parents), PBody->Children); 524 PBody->Children.emplace_back(llvm::make_unique<TextNode>(", ")); 525 AppendVector(std::move(VParents), PBody->Children); 526 } 527 } 528 529 std::vector<std::unique_ptr<TagNode>> Members = 530 genRecordMembersBlock(I.Members, I.Path); 531 AppendVector(std::move(Members), Out); 532 std::vector<std::unique_ptr<TagNode>> ChildRecords = 533 genReferencesBlock(I.ChildRecords, "Records"); 534 AppendVector(std::move(ChildRecords), Out); 535 536 std::vector<std::unique_ptr<TagNode>> ChildFunctions = 537 genFunctionsBlock(I.ChildFunctions, I.Path); 538 AppendVector(std::move(ChildFunctions), Out); 539 std::vector<std::unique_ptr<TagNode>> ChildEnums = 540 genEnumsBlock(I.ChildEnums); 541 AppendVector(std::move(ChildEnums), Out); 542 543 return Out; 544 } 545 546 /// Generator for HTML documentation. 547 class HTMLGenerator : public Generator { 548 public: 549 static const char *Format; 550 551 llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS, 552 const ClangDocContext &CDCtx) override; 553 bool createResources(ClangDocContext CDCtx) override; 554 }; 555 556 const char *HTMLGenerator::Format = "html"; 557 558 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS, 559 const ClangDocContext &CDCtx) { 560 HTMLFile F; 561 562 auto MetaNode = llvm::make_unique<TagNode>(HTMLTag::TAG_META); 563 MetaNode->Attributes.try_emplace("charset", "utf-8"); 564 F.Children.emplace_back(std::move(MetaNode)); 565 566 std::string InfoTitle; 567 Info CastedInfo; 568 auto MainContentNode = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); 569 switch (I->IT) { 570 case InfoType::IT_namespace: { 571 std::vector<std::unique_ptr<TagNode>> Nodes = 572 genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), InfoTitle); 573 AppendVector(std::move(Nodes), MainContentNode->Children); 574 break; 575 } 576 case InfoType::IT_record: { 577 std::vector<std::unique_ptr<TagNode>> Nodes = 578 genHTML(*static_cast<clang::doc::RecordInfo *>(I), InfoTitle); 579 AppendVector(std::move(Nodes), MainContentNode->Children); 580 break; 581 } 582 case InfoType::IT_enum: { 583 std::vector<std::unique_ptr<TagNode>> Nodes = 584 genHTML(*static_cast<clang::doc::EnumInfo *>(I)); 585 AppendVector(std::move(Nodes), MainContentNode->Children); 586 break; 587 } 588 case InfoType::IT_function: { 589 std::vector<std::unique_ptr<TagNode>> Nodes = 590 genHTML(*static_cast<clang::doc::FunctionInfo *>(I), ""); 591 AppendVector(std::move(Nodes), MainContentNode->Children); 592 break; 593 } 594 case InfoType::IT_default: 595 return llvm::make_error<llvm::StringError>("Unexpected info type.\n", 596 llvm::inconvertibleErrorCode()); 597 } 598 599 F.Children.emplace_back( 600 llvm::make_unique<TagNode>(HTMLTag::TAG_TITLE, InfoTitle)); 601 std::vector<std::unique_ptr<TagNode>> StylesheetsNodes = 602 genStylesheetsHTML(I->Path, CDCtx); 603 AppendVector(std::move(StylesheetsNodes), F.Children); 604 F.Children.emplace_back(std::move(MainContentNode)); 605 F.Render(OS); 606 607 return llvm::Error::success(); 608 } 609 610 bool HTMLGenerator::createResources(ClangDocContext CDCtx) { 611 llvm::outs() << "Generating stylesheet for docs...\n"; 612 for (const auto &FilePath : CDCtx.UserStylesheets) { 613 llvm::SmallString<128> StylesheetPathWrite; 614 llvm::sys::path::native(CDCtx.OutDirectory, StylesheetPathWrite); 615 llvm::sys::path::append(StylesheetPathWrite, 616 llvm::sys::path::filename(FilePath)); 617 llvm::SmallString<128> StylesheetPathRead; 618 llvm::sys::path::native(FilePath, StylesheetPathRead); 619 std::error_code OK; 620 std::error_code FileErr = 621 llvm::sys::fs::copy_file(StylesheetPathRead, StylesheetPathWrite); 622 if (FileErr != OK) { 623 llvm::errs() << "Error creating stylesheet file " 624 << llvm::sys::path::filename(FilePath) << ": " 625 << FileErr.message() << "\n"; 626 return false; 627 } 628 } 629 return true; 630 } 631 632 static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format, 633 "Generator for HTML output."); 634 635 // This anchor is used to force the linker to link in the generated object 636 // file and thus register the generator. 637 volatile int HTMLGeneratorAnchorSource = 0; 638 639 } // namespace doc 640 } // namespace clang 641