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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang-tools-extra/clang-doc/HTMLGenerator.cpp