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