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