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