• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
HTMLTag(TagType Value)51   constexpr HTMLTag(TagType Value) : Value(Value) {}
52 
operator TagType() const53   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 {
HTMLNodeclang::doc::__anonc28857ed0111::HTMLNode69   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 {
TextNodeclang::doc::__anonc28857ed0111::TextNode77   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 {
TagNodeclang::doc::__anonc28857ed0111::TagNode85   TagNode(HTMLTag Tag) : HTMLNode(NodeType::NODE_TAG), Tag(Tag) {}
TagNodeclang::doc::__anonc28857ed0111::TagNode86   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
Renderclang::doc::__anonc28857ed0111::HTMLFile102   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 
IsSelfClosing() const113 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 
ToString() const138 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 
Render(llvm::raw_ostream & OS,int IndentationLevel)178 void TextNode::Render(llvm::raw_ostream &OS, int IndentationLevel) {
179   OS.indent(IndentationLevel * 2);
180   printHTMLEscaped(Text, OS);
181 }
182 
Render(llvm::raw_ostream & OS,int IndentationLevel)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>>
AppendVector(std::vector<Derived> && New,std::vector<Base> & Original)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
computeRelativePath(StringRef Destination,StringRef Origin)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>>
genStylesheetsHTML(StringRef InfoPath,const ClangDocContext & CDCtx)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>>
genJsScriptsHTML(StringRef InfoPath,const ClangDocContext & CDCtx)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 
genLink(const Twine & Text,const Twine & Link)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>
genReference(const Reference & Type,StringRef CurrentDirectory,llvm::Optional<StringRef> JumpToSection=None)309 genReference(const Reference &Type, StringRef CurrentDirectory,
310              llvm::Optional<StringRef> JumpToSection = None) {
311   if (Type.Path.empty() && !Type.IsInGlobalNamespace) {
312     if (!JumpToSection)
313       return std::make_unique<TextNode>(Type.Name);
314     else
315       return genLink(Type.Name, "#" + JumpToSection.getValue());
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.getValue()).str();
324   return genLink(Type.Name, Path);
325 }
326 
327 static std::vector<std::unique_ptr<HTMLNode>>
genReferenceList(const llvm::SmallVectorImpl<Reference> & Refs,const StringRef & CurrentDirectory)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>>
genEnumsBlock(const std::vector<EnumInfo> & Enums,const ClangDocContext & CDCtx)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>
genEnumMembersBlock(const llvm::SmallVector<SmallString<16>,4> & Members)364 genEnumMembersBlock(const llvm::SmallVector<SmallString<16>, 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(std::make_unique<TagNode>(HTMLTag::TAG_LI, M));
371   return List;
372 }
373 
374 static std::vector<std::unique_ptr<TagNode>>
genFunctionsBlock(const std::vector<FunctionInfo> & Functions,const ClangDocContext & CDCtx,StringRef ParentInfoDir)375 genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
376                   const ClangDocContext &CDCtx, StringRef ParentInfoDir) {
377   if (Functions.empty())
378     return {};
379 
380   std::vector<std::unique_ptr<TagNode>> Out;
381   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions"));
382   Out.back()->Attributes.emplace_back("id", "Functions");
383   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
384   auto &DivBody = Out.back();
385   for (const auto &F : Functions) {
386     std::vector<std::unique_ptr<TagNode>> Nodes =
387         genHTML(F, CDCtx, ParentInfoDir);
388     AppendVector(std::move(Nodes), DivBody->Children);
389   }
390   return Out;
391 }
392 
393 static std::vector<std::unique_ptr<TagNode>>
genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo,4> & Members,StringRef ParentInfoDir)394 genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
395                       StringRef ParentInfoDir) {
396   if (Members.empty())
397     return {};
398 
399   std::vector<std::unique_ptr<TagNode>> Out;
400   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Members"));
401   Out.back()->Attributes.emplace_back("id", "Members");
402   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
403   auto &ULBody = Out.back();
404   for (const auto &M : Members) {
405     std::string Access = getAccessSpelling(M.Access).str();
406     if (Access != "")
407       Access = Access + " ";
408     auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
409     LIBody->Children.emplace_back(std::make_unique<TextNode>(Access));
410     LIBody->Children.emplace_back(genReference(M.Type, ParentInfoDir));
411     LIBody->Children.emplace_back(std::make_unique<TextNode>(" " + M.Name));
412     ULBody->Children.emplace_back(std::move(LIBody));
413   }
414   return Out;
415 }
416 
417 static std::vector<std::unique_ptr<TagNode>>
genReferencesBlock(const std::vector<Reference> & References,llvm::StringRef Title,StringRef ParentPath)418 genReferencesBlock(const std::vector<Reference> &References,
419                    llvm::StringRef Title, StringRef ParentPath) {
420   if (References.empty())
421     return {};
422 
423   std::vector<std::unique_ptr<TagNode>> Out;
424   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, Title));
425   Out.back()->Attributes.emplace_back("id", std::string(Title));
426   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
427   auto &ULBody = Out.back();
428   for (const auto &R : References) {
429     auto LiNode = std::make_unique<TagNode>(HTMLTag::TAG_LI);
430     LiNode->Children.emplace_back(genReference(R, ParentPath));
431     ULBody->Children.emplace_back(std::move(LiNode));
432   }
433   return Out;
434 }
435 
436 static std::unique_ptr<TagNode>
writeFileDefinition(const Location & L,llvm::Optional<StringRef> RepositoryUrl=None)437 writeFileDefinition(const Location &L,
438                     llvm::Optional<StringRef> RepositoryUrl = None) {
439   if (!L.IsFileInRootDir || !RepositoryUrl)
440     return std::make_unique<TagNode>(
441         HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
442                             " of file " + L.Filename);
443   SmallString<128> FileURL(RepositoryUrl.getValue());
444   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
445   auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
446   Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
447   auto LocNumberNode =
448       std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
449   // The links to a specific line in the source code use the github /
450   // googlesource notation so it won't work for all hosting pages.
451   LocNumberNode->Attributes.emplace_back(
452       "href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
453   Node->Children.emplace_back(std::move(LocNumberNode));
454   Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
455   auto LocFileNode = std::make_unique<TagNode>(
456       HTMLTag::TAG_A, llvm::sys::path::filename(FileURL));
457   LocFileNode->Attributes.emplace_back("href", std::string(FileURL.str()));
458   Node->Children.emplace_back(std::move(LocFileNode));
459   return Node;
460 }
461 
462 static std::vector<std::unique_ptr<TagNode>>
463 genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList);
464 
465 // Generates a list of child nodes for the HTML head tag
466 // It contains a meta node, link nodes to import CSS files, and script nodes to
467 // import JS files
468 static std::vector<std::unique_ptr<TagNode>>
genFileHeadNodes(StringRef Title,StringRef InfoPath,const ClangDocContext & CDCtx)469 genFileHeadNodes(StringRef Title, StringRef InfoPath,
470                  const ClangDocContext &CDCtx) {
471   std::vector<std::unique_ptr<TagNode>> Out;
472   auto MetaNode = std::make_unique<TagNode>(HTMLTag::TAG_META);
473   MetaNode->Attributes.emplace_back("charset", "utf-8");
474   Out.emplace_back(std::move(MetaNode));
475   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_TITLE, Title));
476   std::vector<std::unique_ptr<TagNode>> StylesheetsNodes =
477       genStylesheetsHTML(InfoPath, CDCtx);
478   AppendVector(std::move(StylesheetsNodes), Out);
479   std::vector<std::unique_ptr<TagNode>> JsNodes =
480       genJsScriptsHTML(InfoPath, CDCtx);
481   AppendVector(std::move(JsNodes), Out);
482   return Out;
483 }
484 
485 // Generates a header HTML node that can be used for any file
486 // It contains the project name
genFileHeaderNode(StringRef ProjectName)487 static std::unique_ptr<TagNode> genFileHeaderNode(StringRef ProjectName) {
488   auto HeaderNode = std::make_unique<TagNode>(HTMLTag::TAG_HEADER, ProjectName);
489   HeaderNode->Attributes.emplace_back("id", "project-title");
490   return HeaderNode;
491 }
492 
493 // Generates a main HTML node that has all the main content of an info file
494 // It contains both indexes and the info's documented information
495 // This function should only be used for the info files (not for the file that
496 // only has the general index)
genInfoFileMainNode(StringRef InfoPath,std::vector<std::unique_ptr<TagNode>> & MainContentInnerNodes,const Index & InfoIndex)497 static std::unique_ptr<TagNode> genInfoFileMainNode(
498     StringRef InfoPath,
499     std::vector<std::unique_ptr<TagNode>> &MainContentInnerNodes,
500     const Index &InfoIndex) {
501   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
502 
503   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
504   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
505   LeftSidebarNode->Attributes.emplace_back("path", std::string(InfoPath));
506   LeftSidebarNode->Attributes.emplace_back(
507       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
508 
509   auto MainContentNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
510   MainContentNode->Attributes.emplace_back("id", "main-content");
511   MainContentNode->Attributes.emplace_back(
512       "class", "col-xs-12 col-sm-9 col-md-8 main-content");
513   AppendVector(std::move(MainContentInnerNodes), MainContentNode->Children);
514 
515   auto RightSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
516   RightSidebarNode->Attributes.emplace_back("id", "sidebar-right");
517   RightSidebarNode->Attributes.emplace_back(
518       "class", "col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right");
519   std::vector<std::unique_ptr<TagNode>> InfoIndexHTML =
520       genHTML(InfoIndex, InfoPath, true);
521   AppendVector(std::move(InfoIndexHTML), RightSidebarNode->Children);
522 
523   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
524   MainNode->Children.emplace_back(std::move(MainContentNode));
525   MainNode->Children.emplace_back(std::move(RightSidebarNode));
526 
527   return MainNode;
528 }
529 
530 // Generates a footer HTML node that can be used for any file
531 // It contains clang-doc's version
genFileFooterNode()532 static std::unique_ptr<TagNode> genFileFooterNode() {
533   auto FooterNode = std::make_unique<TagNode>(HTMLTag::TAG_FOOTER);
534   auto SpanNode = std::make_unique<TagNode>(
535       HTMLTag::TAG_SPAN, clang::getClangToolFullVersion("clang-doc"));
536   SpanNode->Attributes.emplace_back("class", "no-break");
537   FooterNode->Children.emplace_back(std::move(SpanNode));
538   return FooterNode;
539 }
540 
541 // Generates a complete HTMLFile for an Info
542 static HTMLFile
genInfoFile(StringRef Title,StringRef InfoPath,std::vector<std::unique_ptr<TagNode>> & MainContentNodes,const Index & InfoIndex,const ClangDocContext & CDCtx)543 genInfoFile(StringRef Title, StringRef InfoPath,
544             std::vector<std::unique_ptr<TagNode>> &MainContentNodes,
545             const Index &InfoIndex, const ClangDocContext &CDCtx) {
546   HTMLFile F;
547 
548   std::vector<std::unique_ptr<TagNode>> HeadNodes =
549       genFileHeadNodes(Title, InfoPath, CDCtx);
550   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
551   std::unique_ptr<TagNode> MainNode =
552       genInfoFileMainNode(InfoPath, MainContentNodes, InfoIndex);
553   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
554 
555   AppendVector(std::move(HeadNodes), F.Children);
556   F.Children.emplace_back(std::move(HeaderNode));
557   F.Children.emplace_back(std::move(MainNode));
558   F.Children.emplace_back(std::move(FooterNode));
559 
560   return F;
561 }
562 
563 template <typename T,
564           typename = std::enable_if<std::is_base_of<T, Info>::value>>
genInfoIndexItem(const std::vector<T> & Infos,StringRef Title)565 static Index genInfoIndexItem(const std::vector<T> &Infos, StringRef Title) {
566   Index Idx(Title, Title);
567   for (const auto &C : Infos)
568     Idx.Children.emplace_back(C.extractName(),
569                               llvm::toHex(llvm::toStringRef(C.USR)));
570   return Idx;
571 }
572 
573 static std::vector<std::unique_ptr<TagNode>>
genHTML(const Index & Index,StringRef InfoPath,bool IsOutermostList)574 genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList) {
575   std::vector<std::unique_ptr<TagNode>> Out;
576   if (!Index.Name.empty()) {
577     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_SPAN));
578     auto &SpanBody = Out.back();
579     if (!Index.JumpToSection)
580       SpanBody->Children.emplace_back(genReference(Index, InfoPath));
581     else
582       SpanBody->Children.emplace_back(genReference(
583           Index, InfoPath, StringRef{Index.JumpToSection.getValue()}));
584   }
585   if (Index.Children.empty())
586     return Out;
587   // Only the outermost list should use ol, the others should use ul
588   HTMLTag ListHTMLTag = IsOutermostList ? HTMLTag::TAG_OL : HTMLTag::TAG_UL;
589   Out.emplace_back(std::make_unique<TagNode>(ListHTMLTag));
590   const auto &UlBody = Out.back();
591   for (const auto &C : Index.Children) {
592     auto LiBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
593     std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(C, InfoPath, false);
594     AppendVector(std::move(Nodes), LiBody->Children);
595     UlBody->Children.emplace_back(std::move(LiBody));
596   }
597   return Out;
598 }
599 
genHTML(const CommentInfo & I)600 static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
601   if (I.Kind == "FullComment") {
602     auto FullComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
603     for (const auto &Child : I.Children) {
604       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
605       if (Node)
606         FullComment->Children.emplace_back(std::move(Node));
607     }
608     return std::move(FullComment);
609   } else if (I.Kind == "ParagraphComment") {
610     auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
611     for (const auto &Child : I.Children) {
612       std::unique_ptr<HTMLNode> Node = genHTML(*Child);
613       if (Node)
614         ParagraphComment->Children.emplace_back(std::move(Node));
615     }
616     if (ParagraphComment->Children.empty())
617       return nullptr;
618     return std::move(ParagraphComment);
619   } else if (I.Kind == "TextComment") {
620     if (I.Text == "")
621       return nullptr;
622     return std::make_unique<TextNode>(I.Text);
623   }
624   return nullptr;
625 }
626 
genHTML(const std::vector<CommentInfo> & C)627 static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) {
628   auto CommentBlock = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
629   for (const auto &Child : C) {
630     if (std::unique_ptr<HTMLNode> Node = genHTML(Child))
631       CommentBlock->Children.emplace_back(std::move(Node));
632   }
633   return CommentBlock;
634 }
635 
636 static std::vector<std::unique_ptr<TagNode>>
genHTML(const EnumInfo & I,const ClangDocContext & CDCtx)637 genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
638   std::vector<std::unique_ptr<TagNode>> Out;
639   std::string EnumType;
640   if (I.Scoped)
641     EnumType = "enum class ";
642   else
643     EnumType = "enum ";
644 
645   Out.emplace_back(
646       std::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name));
647   Out.back()->Attributes.emplace_back("id",
648                                       llvm::toHex(llvm::toStringRef(I.USR)));
649 
650   std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
651   if (Node)
652     Out.emplace_back(std::move(Node));
653 
654   if (I.DefLoc) {
655     if (!CDCtx.RepositoryUrl)
656       Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
657     else
658       Out.emplace_back(writeFileDefinition(
659           I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
660   }
661 
662   std::string Description;
663   if (!I.Description.empty())
664     Out.emplace_back(genHTML(I.Description));
665 
666   return Out;
667 }
668 
669 static std::vector<std::unique_ptr<TagNode>>
genHTML(const FunctionInfo & I,const ClangDocContext & CDCtx,StringRef ParentInfoDir)670 genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
671         StringRef ParentInfoDir) {
672   std::vector<std::unique_ptr<TagNode>> Out;
673   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name));
674   // USR is used as id for functions instead of name to disambiguate function
675   // overloads.
676   Out.back()->Attributes.emplace_back("id",
677                                       llvm::toHex(llvm::toStringRef(I.USR)));
678 
679   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
680   auto &FunctionHeader = Out.back();
681 
682   std::string Access = getAccessSpelling(I.Access).str();
683   if (Access != "")
684     FunctionHeader->Children.emplace_back(
685         std::make_unique<TextNode>(Access + " "));
686   if (I.ReturnType.Type.Name != "") {
687     FunctionHeader->Children.emplace_back(
688         genReference(I.ReturnType.Type, ParentInfoDir));
689     FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(" "));
690   }
691   FunctionHeader->Children.emplace_back(
692       std::make_unique<TextNode>(I.Name + "("));
693 
694   for (const auto &P : I.Params) {
695     if (&P != I.Params.begin())
696       FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(", "));
697     FunctionHeader->Children.emplace_back(genReference(P.Type, ParentInfoDir));
698     FunctionHeader->Children.emplace_back(
699         std::make_unique<TextNode>(" " + P.Name));
700   }
701   FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(")"));
702 
703   if (I.DefLoc) {
704     if (!CDCtx.RepositoryUrl)
705       Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
706     else
707       Out.emplace_back(writeFileDefinition(
708           I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
709   }
710 
711   std::string Description;
712   if (!I.Description.empty())
713     Out.emplace_back(genHTML(I.Description));
714 
715   return Out;
716 }
717 
718 static std::vector<std::unique_ptr<TagNode>>
genHTML(const NamespaceInfo & I,Index & InfoIndex,const ClangDocContext & CDCtx,std::string & InfoTitle)719 genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
720         std::string &InfoTitle) {
721   std::vector<std::unique_ptr<TagNode>> Out;
722   if (I.Name.str() == "")
723     InfoTitle = "Global Namespace";
724   else
725     InfoTitle = ("namespace " + I.Name).str();
726 
727   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
728 
729   std::string Description;
730   if (!I.Description.empty())
731     Out.emplace_back(genHTML(I.Description));
732 
733   llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
734 
735   std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
736       genReferencesBlock(I.ChildNamespaces, "Namespaces", BasePath);
737   AppendVector(std::move(ChildNamespaces), Out);
738   std::vector<std::unique_ptr<TagNode>> ChildRecords =
739       genReferencesBlock(I.ChildRecords, "Records", BasePath);
740   AppendVector(std::move(ChildRecords), Out);
741 
742   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
743       genFunctionsBlock(I.ChildFunctions, CDCtx, BasePath);
744   AppendVector(std::move(ChildFunctions), Out);
745   std::vector<std::unique_ptr<TagNode>> ChildEnums =
746       genEnumsBlock(I.ChildEnums, CDCtx);
747   AppendVector(std::move(ChildEnums), Out);
748 
749   if (!I.ChildNamespaces.empty())
750     InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
751   if (!I.ChildRecords.empty())
752     InfoIndex.Children.emplace_back("Records", "Records");
753   if (!I.ChildFunctions.empty())
754     InfoIndex.Children.emplace_back(
755         genInfoIndexItem(I.ChildFunctions, "Functions"));
756   if (!I.ChildEnums.empty())
757     InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
758 
759   return Out;
760 }
761 
762 static std::vector<std::unique_ptr<TagNode>>
genHTML(const RecordInfo & I,Index & InfoIndex,const ClangDocContext & CDCtx,std::string & InfoTitle)763 genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
764         std::string &InfoTitle) {
765   std::vector<std::unique_ptr<TagNode>> Out;
766   InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
767   Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
768 
769   if (I.DefLoc) {
770     if (!CDCtx.RepositoryUrl)
771       Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
772     else
773       Out.emplace_back(writeFileDefinition(
774           I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
775   }
776 
777   std::string Description;
778   if (!I.Description.empty())
779     Out.emplace_back(genHTML(I.Description));
780 
781   std::vector<std::unique_ptr<HTMLNode>> Parents =
782       genReferenceList(I.Parents, I.Path);
783   std::vector<std::unique_ptr<HTMLNode>> VParents =
784       genReferenceList(I.VirtualParents, I.Path);
785   if (!Parents.empty() || !VParents.empty()) {
786     Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
787     auto &PBody = Out.back();
788     PBody->Children.emplace_back(std::make_unique<TextNode>("Inherits from "));
789     if (Parents.empty())
790       AppendVector(std::move(VParents), PBody->Children);
791     else if (VParents.empty())
792       AppendVector(std::move(Parents), PBody->Children);
793     else {
794       AppendVector(std::move(Parents), PBody->Children);
795       PBody->Children.emplace_back(std::make_unique<TextNode>(", "));
796       AppendVector(std::move(VParents), PBody->Children);
797     }
798   }
799 
800   std::vector<std::unique_ptr<TagNode>> Members =
801       genRecordMembersBlock(I.Members, I.Path);
802   AppendVector(std::move(Members), Out);
803   std::vector<std::unique_ptr<TagNode>> ChildRecords =
804       genReferencesBlock(I.ChildRecords, "Records", I.Path);
805   AppendVector(std::move(ChildRecords), Out);
806 
807   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
808       genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path);
809   AppendVector(std::move(ChildFunctions), Out);
810   std::vector<std::unique_ptr<TagNode>> ChildEnums =
811       genEnumsBlock(I.ChildEnums, CDCtx);
812   AppendVector(std::move(ChildEnums), Out);
813 
814   if (!I.Members.empty())
815     InfoIndex.Children.emplace_back("Members", "Members");
816   if (!I.ChildRecords.empty())
817     InfoIndex.Children.emplace_back("Records", "Records");
818   if (!I.ChildFunctions.empty())
819     InfoIndex.Children.emplace_back(
820         genInfoIndexItem(I.ChildFunctions, "Functions"));
821   if (!I.ChildEnums.empty())
822     InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
823 
824   return Out;
825 }
826 
827 /// Generator for HTML documentation.
828 class HTMLGenerator : public Generator {
829 public:
830   static const char *Format;
831 
832   llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
833                                  const ClangDocContext &CDCtx) override;
834   llvm::Error createResources(ClangDocContext &CDCtx) override;
835 };
836 
837 const char *HTMLGenerator::Format = "html";
838 
generateDocForInfo(Info * I,llvm::raw_ostream & OS,const ClangDocContext & CDCtx)839 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
840                                               const ClangDocContext &CDCtx) {
841   std::string InfoTitle;
842   std::vector<std::unique_ptr<TagNode>> MainContentNodes;
843   Index InfoIndex;
844   switch (I->IT) {
845   case InfoType::IT_namespace:
846     MainContentNodes = genHTML(*static_cast<clang::doc::NamespaceInfo *>(I),
847                                InfoIndex, CDCtx, InfoTitle);
848     break;
849   case InfoType::IT_record:
850     MainContentNodes = genHTML(*static_cast<clang::doc::RecordInfo *>(I),
851                                InfoIndex, CDCtx, InfoTitle);
852     break;
853   case InfoType::IT_enum:
854     MainContentNodes = genHTML(*static_cast<clang::doc::EnumInfo *>(I), CDCtx);
855     break;
856   case InfoType::IT_function:
857     MainContentNodes =
858         genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
859     break;
860   case InfoType::IT_default:
861     return llvm::createStringError(llvm::inconvertibleErrorCode(),
862                                    "unexpected info type");
863   }
864 
865   HTMLFile F = genInfoFile(InfoTitle, I->getRelativeFilePath(""),
866                            MainContentNodes, InfoIndex, CDCtx);
867   F.Render(OS);
868 
869   return llvm::Error::success();
870 }
871 
getRefType(InfoType IT)872 static std::string getRefType(InfoType IT) {
873   switch (IT) {
874   case InfoType::IT_default:
875     return "default";
876   case InfoType::IT_namespace:
877     return "namespace";
878   case InfoType::IT_record:
879     return "record";
880   case InfoType::IT_function:
881     return "function";
882   case InfoType::IT_enum:
883     return "enum";
884   }
885   llvm_unreachable("Unknown InfoType");
886 }
887 
SerializeIndex(ClangDocContext & CDCtx)888 static llvm::Error SerializeIndex(ClangDocContext &CDCtx) {
889   std::error_code OK;
890   std::error_code FileErr;
891   llvm::SmallString<128> FilePath;
892   llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
893   llvm::sys::path::append(FilePath, "index_json.js");
894   llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
895   if (FileErr != OK) {
896     return llvm::createStringError(llvm::inconvertibleErrorCode(),
897                                    "error creating index file: " +
898                                        FileErr.message());
899   }
900   CDCtx.Idx.sort();
901   llvm::json::OStream J(OS, 2);
902   std::function<void(Index)> IndexToJSON = [&](Index I) {
903     J.object([&] {
904       J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
905       J.attribute("Name", I.Name);
906       J.attribute("RefType", getRefType(I.RefType));
907       J.attribute("Path", I.getRelativeFilePath(""));
908       J.attributeArray("Children", [&] {
909         for (const Index &C : I.Children)
910           IndexToJSON(C);
911       });
912     });
913   };
914   OS << "var JsonIndex = `\n";
915   IndexToJSON(CDCtx.Idx);
916   OS << "`;\n";
917   return llvm::Error::success();
918 }
919 
920 // Generates a main HTML node that has the main content of the file that shows
921 // only the general index
922 // It contains the general index with links to all the generated files
genIndexFileMainNode()923 static std::unique_ptr<TagNode> genIndexFileMainNode() {
924   auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
925 
926   auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
927   LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
928   LeftSidebarNode->Attributes.emplace_back("path", "");
929   LeftSidebarNode->Attributes.emplace_back(
930       "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
931   LeftSidebarNode->Attributes.emplace_back("style", "flex: 0 100%;");
932 
933   MainNode->Children.emplace_back(std::move(LeftSidebarNode));
934 
935   return MainNode;
936 }
937 
GenIndex(const ClangDocContext & CDCtx)938 static llvm::Error GenIndex(const ClangDocContext &CDCtx) {
939   std::error_code FileErr, OK;
940   llvm::SmallString<128> IndexPath;
941   llvm::sys::path::native(CDCtx.OutDirectory, IndexPath);
942   llvm::sys::path::append(IndexPath, "index.html");
943   llvm::raw_fd_ostream IndexOS(IndexPath, FileErr, llvm::sys::fs::OF_None);
944   if (FileErr != OK) {
945     return llvm::createStringError(llvm::inconvertibleErrorCode(),
946                                    "error creating main index: " +
947                                        FileErr.message());
948   }
949 
950   HTMLFile F;
951 
952   std::vector<std::unique_ptr<TagNode>> HeadNodes =
953       genFileHeadNodes("Index", "", CDCtx);
954   std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
955   std::unique_ptr<TagNode> MainNode = genIndexFileMainNode();
956   std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
957 
958   AppendVector(std::move(HeadNodes), F.Children);
959   F.Children.emplace_back(std::move(HeaderNode));
960   F.Children.emplace_back(std::move(MainNode));
961   F.Children.emplace_back(std::move(FooterNode));
962 
963   F.Render(IndexOS);
964 
965   return llvm::Error::success();
966 }
967 
CopyFile(StringRef FilePath,StringRef OutDirectory)968 static llvm::Error CopyFile(StringRef FilePath, StringRef OutDirectory) {
969   llvm::SmallString<128> PathWrite;
970   llvm::sys::path::native(OutDirectory, PathWrite);
971   llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
972   llvm::SmallString<128> PathRead;
973   llvm::sys::path::native(FilePath, PathRead);
974   std::error_code OK;
975   std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
976   if (FileErr != OK) {
977     return llvm::createStringError(llvm::inconvertibleErrorCode(),
978                                    "error creating file " +
979                                        llvm::sys::path::filename(FilePath) +
980                                        ": " + FileErr.message() + "\n");
981   }
982   return llvm::Error::success();
983 }
984 
createResources(ClangDocContext & CDCtx)985 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
986   auto Err = SerializeIndex(CDCtx);
987   if (Err)
988     return Err;
989   Err = GenIndex(CDCtx);
990   if (Err)
991     return Err;
992 
993   for (const auto &FilePath : CDCtx.UserStylesheets) {
994     Err = CopyFile(FilePath, CDCtx.OutDirectory);
995     if (Err)
996       return Err;
997   }
998   for (const auto &FilePath : CDCtx.FilesToCopy) {
999     Err = CopyFile(FilePath, CDCtx.OutDirectory);
1000     if (Err)
1001       return Err;
1002   }
1003   return llvm::Error::success();
1004 }
1005 
1006 static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format,
1007                                                   "Generator for HTML output.");
1008 
1009 // This anchor is used to force the linker to link in the generated object
1010 // file and thus register the generator.
1011 volatile int HTMLGeneratorAnchorSource = 0;
1012 
1013 } // namespace doc
1014 } // namespace clang
1015