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()) 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 return genLink(Type.Name, Path); 256 } 257 258 static std::vector<std::unique_ptr<HTMLNode>> 259 genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs, 260 const StringRef &CurrentDirectory) { 261 std::vector<std::unique_ptr<HTMLNode>> Out; 262 for (const auto &R : Refs) { 263 if (&R != Refs.begin()) 264 Out.emplace_back(llvm::make_unique<TextNode>(", ")); 265 Out.emplace_back(genTypeReference(R, CurrentDirectory)); 266 } 267 return Out; 268 } 269 270 static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I); 271 static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I, 272 StringRef ParentInfoDir); 273 274 static std::vector<std::unique_ptr<TagNode>> 275 genEnumsBlock(const std::vector<EnumInfo> &Enums) { 276 if (Enums.empty()) 277 return {}; 278 279 std::vector<std::unique_ptr<TagNode>> Out; 280 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Enums")); 281 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV)); 282 auto &DivBody = Out.back(); 283 for (const auto &E : Enums) { 284 std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E); 285 AppendVector(std::move(Nodes), DivBody->Children); 286 } 287 return Out; 288 } 289 290 static std::unique_ptr<TagNode> 291 genEnumMembersBlock(const llvm::SmallVector<SmallString<16>, 4> &Members) { 292 if (Members.empty()) 293 return nullptr; 294 295 auto List = llvm::make_unique<TagNode>(HTMLTag::TAG_UL); 296 for (const auto &M : Members) 297 List->Children.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_LI, M)); 298 return List; 299 } 300 301 static std::vector<std::unique_ptr<TagNode>> 302 genFunctionsBlock(const std::vector<FunctionInfo> &Functions, 303 StringRef ParentInfoDir) { 304 if (Functions.empty()) 305 return {}; 306 307 std::vector<std::unique_ptr<TagNode>> Out; 308 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions")); 309 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV)); 310 auto &DivBody = Out.back(); 311 for (const auto &F : Functions) { 312 std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(F, ParentInfoDir); 313 AppendVector(std::move(Nodes), DivBody->Children); 314 } 315 return Out; 316 } 317 318 static std::vector<std::unique_ptr<TagNode>> 319 genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members, 320 StringRef ParentInfoDir) { 321 if (Members.empty()) 322 return {}; 323 324 std::vector<std::unique_ptr<TagNode>> Out; 325 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, "Members")); 326 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL)); 327 auto &ULBody = Out.back(); 328 for (const auto &M : Members) { 329 std::string Access = getAccess(M.Access); 330 if (Access != "") 331 Access = Access + " "; 332 auto LIBody = llvm::make_unique<TagNode>(HTMLTag::TAG_LI); 333 LIBody->Children.emplace_back(llvm::make_unique<TextNode>(Access)); 334 LIBody->Children.emplace_back(genTypeReference(M.Type, ParentInfoDir)); 335 LIBody->Children.emplace_back(llvm::make_unique<TextNode>(" " + M.Name)); 336 ULBody->Children.emplace_back(std::move(LIBody)); 337 } 338 return Out; 339 } 340 341 static std::vector<std::unique_ptr<TagNode>> 342 genReferencesBlock(const std::vector<Reference> &References, 343 llvm::StringRef Title) { 344 if (References.empty()) 345 return {}; 346 347 std::vector<std::unique_ptr<TagNode>> Out; 348 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H2, Title)); 349 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL)); 350 auto &ULBody = Out.back(); 351 for (const auto &R : References) 352 ULBody->Children.emplace_back( 353 llvm::make_unique<TagNode>(HTMLTag::TAG_LI, R.Name)); 354 return Out; 355 } 356 357 static std::unique_ptr<TagNode> writeFileDefinition(const Location &L) { 358 return llvm::make_unique<TagNode>( 359 HTMLTag::TAG_P, 360 "Defined at line " + std::to_string(L.LineNumber) + " of " + L.Filename); 361 } 362 363 static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) { 364 if (I.Kind == "FullComment") { 365 auto FullComment = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); 366 for (const auto &Child : I.Children) { 367 std::unique_ptr<HTMLNode> Node = genHTML(*Child); 368 if (Node) 369 FullComment->Children.emplace_back(std::move(Node)); 370 } 371 return std::move(FullComment); 372 } else if (I.Kind == "ParagraphComment") { 373 auto ParagraphComment = llvm::make_unique<TagNode>(HTMLTag::TAG_P); 374 for (const auto &Child : I.Children) { 375 std::unique_ptr<HTMLNode> Node = genHTML(*Child); 376 if (Node) 377 ParagraphComment->Children.emplace_back(std::move(Node)); 378 } 379 if (ParagraphComment->Children.empty()) 380 return nullptr; 381 return std::move(ParagraphComment); 382 } else if (I.Kind == "TextComment") { 383 if (I.Text == "") 384 return nullptr; 385 return llvm::make_unique<TextNode>(I.Text); 386 } 387 return nullptr; 388 } 389 390 static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) { 391 auto CommentBlock = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); 392 for (const auto &Child : C) { 393 if (std::unique_ptr<HTMLNode> Node = genHTML(Child)) 394 CommentBlock->Children.emplace_back(std::move(Node)); 395 } 396 return CommentBlock; 397 } 398 399 static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I) { 400 std::vector<std::unique_ptr<TagNode>> Out; 401 std::string EnumType; 402 if (I.Scoped) 403 EnumType = "enum class "; 404 else 405 EnumType = "enum "; 406 407 Out.emplace_back( 408 llvm::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name)); 409 410 std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members); 411 if (Node) 412 Out.emplace_back(std::move(Node)); 413 414 if (I.DefLoc) 415 Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); 416 417 std::string Description; 418 if (!I.Description.empty()) 419 Out.emplace_back(genHTML(I.Description)); 420 421 return Out; 422 } 423 424 static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I, 425 StringRef ParentInfoDir) { 426 std::vector<std::unique_ptr<TagNode>> Out; 427 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name)); 428 429 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P)); 430 auto &FunctionHeader = Out.back(); 431 432 std::string Access = getAccess(I.Access); 433 if (Access != "") 434 FunctionHeader->Children.emplace_back( 435 llvm::make_unique<TextNode>(Access + " ")); 436 if (I.ReturnType.Type.Name != "") { 437 FunctionHeader->Children.emplace_back( 438 genTypeReference(I.ReturnType.Type, ParentInfoDir)); 439 FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(" ")); 440 } 441 FunctionHeader->Children.emplace_back( 442 llvm::make_unique<TextNode>(I.Name + "(")); 443 444 for (const auto &P : I.Params) { 445 if (&P != I.Params.begin()) 446 FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(", ")); 447 FunctionHeader->Children.emplace_back( 448 genTypeReference(P.Type, ParentInfoDir)); 449 FunctionHeader->Children.emplace_back( 450 llvm::make_unique<TextNode>(" " + P.Name)); 451 } 452 FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(")")); 453 454 if (I.DefLoc) 455 Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); 456 457 std::string Description; 458 if (!I.Description.empty()) 459 Out.emplace_back(genHTML(I.Description)); 460 461 return Out; 462 } 463 464 static std::vector<std::unique_ptr<TagNode>> genHTML(const NamespaceInfo &I, 465 std::string &InfoTitle) { 466 std::vector<std::unique_ptr<TagNode>> Out; 467 if (I.Name.str() == "") 468 InfoTitle = "Global Namespace"; 469 else 470 InfoTitle = ("namespace " + I.Name).str(); 471 472 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle)); 473 474 std::string Description; 475 if (!I.Description.empty()) 476 Out.emplace_back(genHTML(I.Description)); 477 478 std::vector<std::unique_ptr<TagNode>> ChildNamespaces = 479 genReferencesBlock(I.ChildNamespaces, "Namespaces"); 480 AppendVector(std::move(ChildNamespaces), Out); 481 std::vector<std::unique_ptr<TagNode>> ChildRecords = 482 genReferencesBlock(I.ChildRecords, "Records"); 483 AppendVector(std::move(ChildRecords), Out); 484 485 std::vector<std::unique_ptr<TagNode>> ChildFunctions = 486 genFunctionsBlock(I.ChildFunctions, I.Path); 487 AppendVector(std::move(ChildFunctions), Out); 488 std::vector<std::unique_ptr<TagNode>> ChildEnums = 489 genEnumsBlock(I.ChildEnums); 490 AppendVector(std::move(ChildEnums), Out); 491 492 return Out; 493 } 494 495 static std::vector<std::unique_ptr<TagNode>> genHTML(const RecordInfo &I, 496 std::string &InfoTitle) { 497 std::vector<std::unique_ptr<TagNode>> Out; 498 InfoTitle = (getTagType(I.TagType) + " " + I.Name).str(); 499 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle)); 500 501 if (I.DefLoc) 502 Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); 503 504 std::string Description; 505 if (!I.Description.empty()) 506 Out.emplace_back(genHTML(I.Description)); 507 508 std::vector<std::unique_ptr<HTMLNode>> Parents = 509 genReferenceList(I.Parents, I.Path); 510 std::vector<std::unique_ptr<HTMLNode>> VParents = 511 genReferenceList(I.VirtualParents, I.Path); 512 if (!Parents.empty() || !VParents.empty()) { 513 Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P)); 514 auto &PBody = Out.back(); 515 PBody->Children.emplace_back(llvm::make_unique<TextNode>("Inherits from ")); 516 if (Parents.empty()) 517 AppendVector(std::move(VParents), PBody->Children); 518 else if (VParents.empty()) 519 AppendVector(std::move(Parents), PBody->Children); 520 else { 521 AppendVector(std::move(Parents), PBody->Children); 522 PBody->Children.emplace_back(llvm::make_unique<TextNode>(", ")); 523 AppendVector(std::move(VParents), PBody->Children); 524 } 525 } 526 527 std::vector<std::unique_ptr<TagNode>> Members = 528 genRecordMembersBlock(I.Members, I.Path); 529 AppendVector(std::move(Members), Out); 530 std::vector<std::unique_ptr<TagNode>> ChildRecords = 531 genReferencesBlock(I.ChildRecords, "Records"); 532 AppendVector(std::move(ChildRecords), Out); 533 534 std::vector<std::unique_ptr<TagNode>> ChildFunctions = 535 genFunctionsBlock(I.ChildFunctions, I.Path); 536 AppendVector(std::move(ChildFunctions), Out); 537 std::vector<std::unique_ptr<TagNode>> ChildEnums = 538 genEnumsBlock(I.ChildEnums); 539 AppendVector(std::move(ChildEnums), Out); 540 541 return Out; 542 } 543 544 /// Generator for HTML documentation. 545 class HTMLGenerator : public Generator { 546 public: 547 static const char *Format; 548 549 llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS, 550 const ClangDocContext &CDCtx) override; 551 bool createResources(ClangDocContext CDCtx) override; 552 }; 553 554 const char *HTMLGenerator::Format = "html"; 555 556 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS, 557 const ClangDocContext &CDCtx) { 558 HTMLFile F; 559 560 auto MetaNode = llvm::make_unique<TagNode>(HTMLTag::TAG_META); 561 MetaNode->Attributes.try_emplace("charset", "utf-8"); 562 F.Children.emplace_back(std::move(MetaNode)); 563 564 std::string InfoTitle; 565 Info CastedInfo; 566 auto MainContentNode = llvm::make_unique<TagNode>(HTMLTag::TAG_DIV); 567 switch (I->IT) { 568 case InfoType::IT_namespace: { 569 std::vector<std::unique_ptr<TagNode>> Nodes = 570 genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), InfoTitle); 571 AppendVector(std::move(Nodes), MainContentNode->Children); 572 break; 573 } 574 case InfoType::IT_record: { 575 std::vector<std::unique_ptr<TagNode>> Nodes = 576 genHTML(*static_cast<clang::doc::RecordInfo *>(I), InfoTitle); 577 AppendVector(std::move(Nodes), MainContentNode->Children); 578 break; 579 } 580 case InfoType::IT_enum: { 581 std::vector<std::unique_ptr<TagNode>> Nodes = 582 genHTML(*static_cast<clang::doc::EnumInfo *>(I)); 583 AppendVector(std::move(Nodes), MainContentNode->Children); 584 break; 585 } 586 case InfoType::IT_function: { 587 std::vector<std::unique_ptr<TagNode>> Nodes = 588 genHTML(*static_cast<clang::doc::FunctionInfo *>(I), ""); 589 AppendVector(std::move(Nodes), MainContentNode->Children); 590 break; 591 } 592 case InfoType::IT_default: 593 return llvm::make_error<llvm::StringError>("Unexpected info type.\n", 594 llvm::inconvertibleErrorCode()); 595 } 596 597 F.Children.emplace_back( 598 llvm::make_unique<TagNode>(HTMLTag::TAG_TITLE, InfoTitle)); 599 std::vector<std::unique_ptr<TagNode>> StylesheetsNodes = 600 genStylesheetsHTML(I->Path, CDCtx); 601 AppendVector(std::move(StylesheetsNodes), F.Children); 602 F.Children.emplace_back(std::move(MainContentNode)); 603 F.Render(OS); 604 605 return llvm::Error::success(); 606 } 607 608 bool HTMLGenerator::createResources(ClangDocContext CDCtx) { 609 llvm::outs() << "Generating stylesheet for docs...\n"; 610 for (const auto &FilePath : CDCtx.UserStylesheets) { 611 llvm::SmallString<128> StylesheetPathWrite; 612 llvm::sys::path::native(CDCtx.OutDirectory, StylesheetPathWrite); 613 llvm::sys::path::append(StylesheetPathWrite, 614 llvm::sys::path::filename(FilePath)); 615 llvm::SmallString<128> StylesheetPathRead; 616 llvm::sys::path::native(FilePath, StylesheetPathRead); 617 std::error_code OK; 618 std::error_code FileErr = 619 llvm::sys::fs::copy_file(StylesheetPathRead, StylesheetPathWrite); 620 if (FileErr != OK) { 621 llvm::errs() << "Error creating stylesheet file " 622 << llvm::sys::path::filename(FilePath) << ": " 623 << FileErr.message() << "\n"; 624 return false; 625 } 626 } 627 return true; 628 } 629 630 static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format, 631 "Generator for HTML output."); 632 633 // This anchor is used to force the linker to link in the generated object 634 // file and thus register the generator. 635 volatile int HTMLGeneratorAnchorSource = 0; 636 637 } // namespace doc 638 } // namespace clang 639