clang-tools  10.0.0svn
HTMLGenerator.cpp
Go to the documentation of this file.
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", 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", 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() && !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<128> Path =
318  computeRelativePath(Type.Path, CurrentDirectory);
319  llvm::sys::path::append(Path, Type.Name + ".html");
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>>
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<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>>
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>>
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 = getAccess(M.Access);
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>>
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", 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>
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", 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>>
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
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)
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", 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
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
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>>
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>>
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 
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 
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>>
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>>
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 = getAccess(I.Access);
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>>
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  std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
734  genReferencesBlock(I.ChildNamespaces, "Namespaces", I.Path);
735  AppendVector(std::move(ChildNamespaces), Out);
736  std::vector<std::unique_ptr<TagNode>> ChildRecords =
737  genReferencesBlock(I.ChildRecords, "Records", I.Path);
738  AppendVector(std::move(ChildRecords), Out);
739 
740  std::vector<std::unique_ptr<TagNode>> ChildFunctions =
742  AppendVector(std::move(ChildFunctions), Out);
743  std::vector<std::unique_ptr<TagNode>> ChildEnums =
744  genEnumsBlock(I.ChildEnums, CDCtx);
745  AppendVector(std::move(ChildEnums), Out);
746 
747  if (!I.ChildNamespaces.empty())
748  InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
749  if (!I.ChildRecords.empty())
750  InfoIndex.Children.emplace_back("Records", "Records");
751  if (!I.ChildFunctions.empty())
752  InfoIndex.Children.emplace_back(
753  genInfoIndexItem(I.ChildFunctions, "Functions"));
754  if (!I.ChildEnums.empty())
755  InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
756 
757  return Out;
758 }
759 
760 static std::vector<std::unique_ptr<TagNode>>
761 genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
762  std::string &InfoTitle) {
763  std::vector<std::unique_ptr<TagNode>> Out;
764  InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
765  Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
766 
767  if (I.DefLoc) {
768  if (!CDCtx.RepositoryUrl)
769  Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
770  else
771  Out.emplace_back(writeFileDefinition(
772  I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
773  }
774 
775  std::string Description;
776  if (!I.Description.empty())
777  Out.emplace_back(genHTML(I.Description));
778 
779  std::vector<std::unique_ptr<HTMLNode>> Parents =
781  std::vector<std::unique_ptr<HTMLNode>> VParents =
783  if (!Parents.empty() || !VParents.empty()) {
784  Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
785  auto &PBody = Out.back();
786  PBody->Children.emplace_back(std::make_unique<TextNode>("Inherits from "));
787  if (Parents.empty())
788  AppendVector(std::move(VParents), PBody->Children);
789  else if (VParents.empty())
790  AppendVector(std::move(Parents), PBody->Children);
791  else {
792  AppendVector(std::move(Parents), PBody->Children);
793  PBody->Children.emplace_back(std::make_unique<TextNode>(", "));
794  AppendVector(std::move(VParents), PBody->Children);
795  }
796  }
797 
798  std::vector<std::unique_ptr<TagNode>> Members =
800  AppendVector(std::move(Members), Out);
801  std::vector<std::unique_ptr<TagNode>> ChildRecords =
802  genReferencesBlock(I.ChildRecords, "Records", I.Path);
803  AppendVector(std::move(ChildRecords), Out);
804 
805  std::vector<std::unique_ptr<TagNode>> ChildFunctions =
807  AppendVector(std::move(ChildFunctions), Out);
808  std::vector<std::unique_ptr<TagNode>> ChildEnums =
809  genEnumsBlock(I.ChildEnums, CDCtx);
810  AppendVector(std::move(ChildEnums), Out);
811 
812  if (!I.Members.empty())
813  InfoIndex.Children.emplace_back("Members", "Members");
814  if (!I.ChildRecords.empty())
815  InfoIndex.Children.emplace_back("Records", "Records");
816  if (!I.ChildFunctions.empty())
817  InfoIndex.Children.emplace_back(
818  genInfoIndexItem(I.ChildFunctions, "Functions"));
819  if (!I.ChildEnums.empty())
820  InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
821 
822  return Out;
823 }
824 
825 /// Generator for HTML documentation.
826 class HTMLGenerator : public Generator {
827 public:
828  static const char *Format;
829 
830  llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
831  const ClangDocContext &CDCtx) override;
832  llvm::Error createResources(ClangDocContext &CDCtx) override;
833 };
834 
835 const char *HTMLGenerator::Format = "html";
836 
837 llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
838  const ClangDocContext &CDCtx) {
839  std::string InfoTitle;
840  std::vector<std::unique_ptr<TagNode>> MainContentNodes;
841  Index InfoIndex;
842  switch (I->IT) {
843  case InfoType::IT_namespace:
844  MainContentNodes = genHTML(*static_cast<clang::doc::NamespaceInfo *>(I),
845  InfoIndex, CDCtx, InfoTitle);
846  break;
847  case InfoType::IT_record:
848  MainContentNodes = genHTML(*static_cast<clang::doc::RecordInfo *>(I),
849  InfoIndex, CDCtx, InfoTitle);
850  break;
851  case InfoType::IT_enum:
852  MainContentNodes = genHTML(*static_cast<clang::doc::EnumInfo *>(I), CDCtx);
853  break;
854  case InfoType::IT_function:
855  MainContentNodes =
856  genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
857  break;
858  case InfoType::IT_default:
859  return llvm::createStringError(llvm::inconvertibleErrorCode(),
860  "unexpected info type");
861  }
862 
863  HTMLFile F =
864  genInfoFile(InfoTitle, I->Path, MainContentNodes, InfoIndex, CDCtx);
865  F.Render(OS);
866 
867  return llvm::Error::success();
868 }
869 
870 static std::string getRefType(InfoType IT) {
871  switch (IT) {
872  case InfoType::IT_default:
873  return "default";
874  case InfoType::IT_namespace:
875  return "namespace";
876  case InfoType::IT_record:
877  return "record";
878  case InfoType::IT_function:
879  return "function";
880  case InfoType::IT_enum:
881  return "enum";
882  }
883  llvm_unreachable("Unknown InfoType");
884 }
885 
886 static llvm::Error SerializeIndex(ClangDocContext &CDCtx) {
887  std::error_code OK;
888  std::error_code FileErr;
889  llvm::SmallString<128> FilePath;
890  llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
891  llvm::sys::path::append(FilePath, "index_json.js");
892  llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_None);
893  if (FileErr != OK) {
894  return llvm::createStringError(llvm::inconvertibleErrorCode(),
895  "error creating index file: " +
896  FileErr.message());
897  }
898  CDCtx.Idx.sort();
899  llvm::json::OStream J(OS, 2);
900  std::function<void(Index)> IndexToJSON = [&](Index I) {
901  J.object([&] {
902  J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
903  J.attribute("Name", I.Name);
904  J.attribute("RefType", getRefType(I.RefType));
905  J.attribute("Path", I.Path);
906  J.attributeArray("Children", [&] {
907  for (const Index &C : I.Children)
908  IndexToJSON(C);
909  });
910  });
911  };
912  OS << "var JsonIndex = `\n";
913  IndexToJSON(CDCtx.Idx);
914  OS << "`;\n";
915  return llvm::Error::success();
916 }
917 
918 // Generates a main HTML node that has the main content of the file that shows
919 // only the general index
920 // It contains the general index with links to all the generated files
921 static std::unique_ptr<TagNode> genIndexFileMainNode() {
922  auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
923 
924  auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
925  LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
926  LeftSidebarNode->Attributes.emplace_back("path", "");
927  LeftSidebarNode->Attributes.emplace_back(
928  "class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
929  LeftSidebarNode->Attributes.emplace_back("style", "flex: 0 100%;");
930 
931  MainNode->Children.emplace_back(std::move(LeftSidebarNode));
932 
933  return MainNode;
934 }
935 
936 static llvm::Error GenIndex(const ClangDocContext &CDCtx) {
937  std::error_code FileErr, OK;
938  llvm::SmallString<128> IndexPath;
939  llvm::sys::path::native(CDCtx.OutDirectory, IndexPath);
940  llvm::sys::path::append(IndexPath, "index.html");
941  llvm::raw_fd_ostream IndexOS(IndexPath, FileErr, llvm::sys::fs::OF_None);
942  if (FileErr != OK) {
943  return llvm::createStringError(llvm::inconvertibleErrorCode(),
944  "error creating main index: " +
945  FileErr.message());
946  }
947 
948  HTMLFile F;
949 
950  std::vector<std::unique_ptr<TagNode>> HeadNodes =
951  genFileHeadNodes("Index", "", CDCtx);
952  std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
953  std::unique_ptr<TagNode> MainNode = genIndexFileMainNode();
954  std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
955 
956  AppendVector(std::move(HeadNodes), F.Children);
957  F.Children.emplace_back(std::move(HeaderNode));
958  F.Children.emplace_back(std::move(MainNode));
959  F.Children.emplace_back(std::move(FooterNode));
960 
961  F.Render(IndexOS);
962 
963  return llvm::Error::success();
964 }
965 
966 static llvm::Error CopyFile(StringRef FilePath, StringRef OutDirectory) {
967  llvm::SmallString<128> PathWrite;
968  llvm::sys::path::native(OutDirectory, PathWrite);
969  llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
970  llvm::SmallString<128> PathRead;
971  llvm::sys::path::native(FilePath, PathRead);
972  std::error_code OK;
973  std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
974  if (FileErr != OK) {
975  return llvm::createStringError(llvm::inconvertibleErrorCode(),
976  "error creating file " +
977  llvm::sys::path::filename(FilePath) +
978  ": " + FileErr.message() + "\n");
979  }
980  return llvm::Error::success();
981 }
982 
983 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
984  auto Err = SerializeIndex(CDCtx);
985  if (Err)
986  return Err;
987  Err = GenIndex(CDCtx);
988  if (Err)
989  return Err;
990 
991  for (const auto &FilePath : CDCtx.UserStylesheets) {
992  Err = CopyFile(FilePath, CDCtx.OutDirectory);
993  if (Err)
994  return Err;
995  }
996  for (const auto &FilePath : CDCtx.FilesToCopy) {
997  Err = CopyFile(FilePath, CDCtx.OutDirectory);
998  if (Err)
999  return Err;
1000  }
1001  return llvm::Error::success();
1002 }
1003 
1004 static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format,
1005  "Generator for HTML output.");
1006 
1007 // This anchor is used to force the linker to link in the generated object
1008 // file and thus register the generator.
1009 volatile int HTMLGeneratorAnchorSource = 0;
1010 
1011 } // namespace doc
1012 } // namespace clang
static std::unique_ptr< TagNode > genFileFooterNode()
std::vector< Index > Children
Some operations such as code completion produce a set of candidates.
std::string Text
static llvm::Error GenIndex(const ClangDocContext &CDCtx)
volatile int HTMLGeneratorAnchorSource
llvm::Optional< std::string > RepositoryUrl
static std::vector< std::unique_ptr< TagNode > > genEnumsBlock(const std::vector< EnumInfo > &Enums, const ClangDocContext &CDCtx)
HTMLTag Tag
static llvm::Error CopyFile(StringRef FilePath, StringRef OutDirectory)
static Index genInfoIndexItem(const std::vector< T > &Infos, StringRef Title)
SmallString< 16 > Name
static std::unique_ptr< HTMLNode > genReference(const Reference &Type, StringRef CurrentDirectory, llvm::Optional< StringRef > JumpToSection=None)
llvm::Optional< Location > DefLoc
std::vector< std::string > JsScripts
std::vector< FunctionInfo > ChildFunctions
static llvm::cl::opt< std::string > RepositoryUrl("repository", llvm::cl::desc(R"( URL of repository that hosts code. Used for links to definition locations.)"), llvm::cl::cat(ClangDocCategory))
static std::string getRefType(InfoType IT)
llvm::Optional< SmallString< 16 > > JumpToSection
std::vector< FunctionInfo > ChildFunctions
static HTMLFile genInfoFile(StringRef Title, StringRef InfoPath, std::vector< std::unique_ptr< TagNode >> &MainContentNodes, const Index &InfoIndex, const ClangDocContext &CDCtx)
std::vector< EnumInfo > ChildEnums
std::vector< HeaderHandle > Path
static std::vector< std::unique_ptr< TagNode > > genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx, std::string &InfoTitle)
std::vector< Reference > ChildRecords
static const char * Format
static void AppendVector(std::vector< Derived > &&New, std::vector< Base > &Original)
static llvm::Error SerializeIndex(ClangDocContext &CDCtx)
llvm::SmallVector< Reference, 4 > VirtualParents
static SmallString< 128 > computeRelativePath(StringRef Destination, StringRef Origin)
static std::vector< std::unique_ptr< TagNode > > genJsScriptsHTML(StringRef InfoPath, const ClangDocContext &CDCtx)
static llvm::cl::opt< std::string > ProjectName("project-name", llvm::cl::desc("Name of project."), llvm::cl::cat(ClangDocCategory))
std::string getAccess(AccessSpecifier AS)
Definition: Generators.cpp:30
llvm::SmallVector< FieldTypeInfo, 4 > Params
static std::vector< std::unique_ptr< TagNode > > genFileHeadNodes(StringRef Title, StringRef InfoPath, const ClangDocContext &CDCtx)
static std::unique_ptr< TagNode > genLink(const Twine &Text, const Twine &Link)
static std::vector< std::unique_ptr< TagNode > > genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx)
static llvm::cl::opt< std::string > OutDirectory("output", llvm::cl::desc("Directory for outputting generated files."), llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory))
llvm::SmallVector< SmallString< 16 >, 4 > Members
std::vector< CommentInfo > Description
std::vector< std::string > UserStylesheets
std::string getTagType(TagTypeKind AS)
Definition: Generators.cpp:44
static std::unique_ptr< TagNode > genFileHeaderNode(StringRef ProjectName)
static std::unique_ptr< TagNode > genInfoFileMainNode(StringRef InfoPath, std::vector< std::unique_ptr< TagNode >> &MainContentInnerNodes, const Index &InfoIndex)
SmallString< 16 > Name
static std::unique_ptr< TagNode > writeFileDefinition(const Location &L, llvm::Optional< StringRef > RepositoryUrl=None)
static std::vector< std::unique_ptr< TagNode > > genReferencesBlock(const std::vector< Reference > &References, llvm::StringRef Title, StringRef ParentPath)
A base struct for Infos.
llvm::SmallString< 128 > Path
llvm::SmallVector< Reference, 4 > Parents
Generator for HTML documentation.
std::vector< std::unique_ptr< HTMLNode > > Children
std::vector< std::pair< std::string, std::string > > Attributes
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
unsigned References
static std::vector< std::unique_ptr< TagNode > > genFunctionsBlock(const std::vector< FunctionInfo > &Functions, const ClangDocContext &CDCtx, StringRef ParentInfoDir)
static std::unique_ptr< TagNode > genIndexFileMainNode()
std::vector< Reference > ChildNamespaces
std::vector< Reference > ChildRecords
SmallString< 32 > Filename
const InfoType IT
std::vector< EnumInfo > ChildEnums
llvm::SmallVector< MemberTypeInfo, 4 > Members
SmallString< 16 > Kind
const char * Description
Definition: Dexp.cpp:257
RefSlab Refs
std::unique_ptr< GlobalCompilationDatabase > Base
static GeneratorRegistry::Add< HTMLGenerator > HTML(HTMLGenerator::Format, "Generator for HTML output.")
std::vector< std::unique_ptr< CommentInfo > > Children
SmallString< 64 > Text
static std::vector< std::unique_ptr< HTMLNode > > genReferenceList(const llvm::SmallVectorImpl< Reference > &Refs, const StringRef &CurrentDirectory)
static std::vector< std::unique_ptr< TagNode > > genRecordMembersBlock(const llvm::SmallVector< MemberTypeInfo, 4 > &Members, StringRef ParentInfoDir)
llvm::SmallString< 128 > Path
NodeType Type
std::vector< std::string > FilesToCopy
static std::unique_ptr< TagNode > genEnumMembersBlock(const llvm::SmallVector< SmallString< 16 >, 4 > &Members)