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