2019-07-10 19:03:25 +00:00
|
|
|
//===-- HTMLGenerator.cpp - HTML Generator ----------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Generators.h"
|
|
|
|
#include "Representation.h"
|
2025-04-09 17:21:28 -04:00
|
|
|
#include "support/File.h"
|
2019-08-16 18:38:11 +00:00
|
|
|
#include "clang/Basic/Version.h"
|
2019-07-25 20:14:45 +00:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2019-07-10 19:03:25 +00:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
2022-11-29 12:35:58 -08:00
|
|
|
#include "llvm/ADT/StringSet.h"
|
2019-07-10 19:03:25 +00:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
2019-08-06 18:31:46 +00:00
|
|
|
#include "llvm/Support/JSON.h"
|
2019-07-10 19:03:25 +00:00
|
|
|
#include "llvm/Support/Path.h"
|
2019-08-06 18:31:46 +00:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2024-07-25 03:33:02 -04:00
|
|
|
#include <algorithm>
|
2023-01-07 20:02:20 -08:00
|
|
|
#include <optional>
|
2019-07-10 19:03:25 +00:00
|
|
|
#include <string>
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace doc {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class HTMLTag {
|
|
|
|
public:
|
|
|
|
// Any other tag can be added if required
|
|
|
|
enum TagType {
|
2019-08-06 18:31:46 +00:00
|
|
|
TAG_A,
|
2019-07-10 19:03:25 +00:00
|
|
|
TAG_DIV,
|
2019-08-16 18:38:11 +00:00
|
|
|
TAG_FOOTER,
|
2019-07-10 19:03:25 +00:00
|
|
|
TAG_H1,
|
|
|
|
TAG_H2,
|
|
|
|
TAG_H3,
|
2019-08-16 18:38:11 +00:00
|
|
|
TAG_HEADER,
|
2019-07-10 19:03:25 +00:00
|
|
|
TAG_LI,
|
2019-07-25 21:27:50 +00:00
|
|
|
TAG_LINK,
|
2019-08-16 18:38:11 +00:00
|
|
|
TAG_MAIN,
|
2019-08-06 18:31:46 +00:00
|
|
|
TAG_META,
|
2019-08-16 18:38:11 +00:00
|
|
|
TAG_OL,
|
2019-08-06 18:31:46 +00:00
|
|
|
TAG_P,
|
|
|
|
TAG_SCRIPT,
|
|
|
|
TAG_SPAN,
|
|
|
|
TAG_TITLE,
|
|
|
|
TAG_UL,
|
2024-08-16 18:37:17 -04:00
|
|
|
TAG_TABLE,
|
|
|
|
TAG_THEAD,
|
|
|
|
TAG_TBODY,
|
|
|
|
TAG_TR,
|
|
|
|
TAG_TD,
|
|
|
|
TAG_TH
|
2019-07-10 19:03:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
HTMLTag() = default;
|
|
|
|
constexpr HTMLTag(TagType Value) : Value(Value) {}
|
|
|
|
|
|
|
|
operator TagType() const { return Value; }
|
|
|
|
operator bool() = delete;
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
bool isSelfClosing() const;
|
2024-07-08 16:30:18 -07:00
|
|
|
StringRef toString() const;
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
TagType Value;
|
|
|
|
};
|
|
|
|
|
2019-07-12 18:32:00 +00:00
|
|
|
enum NodeType {
|
|
|
|
NODE_TEXT,
|
|
|
|
NODE_TAG,
|
|
|
|
};
|
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
struct HTMLNode {
|
2019-07-12 18:32:00 +00:00
|
|
|
HTMLNode(NodeType Type) : Type(Type) {}
|
2019-07-10 19:03:25 +00:00
|
|
|
virtual ~HTMLNode() = default;
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
virtual void render(llvm::raw_ostream &OS, int IndentationLevel) = 0;
|
2019-07-12 18:32:00 +00:00
|
|
|
NodeType Type; // Type of node
|
2019-07-10 19:03:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct TextNode : public HTMLNode {
|
2019-07-25 20:49:00 +00:00
|
|
|
TextNode(const Twine &Text)
|
|
|
|
: HTMLNode(NodeType::NODE_TEXT), Text(Text.str()) {}
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
std::string Text; // Content of node
|
2024-06-27 13:46:35 -07:00
|
|
|
void render(llvm::raw_ostream &OS, int IndentationLevel) override;
|
2019-07-10 19:03:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct TagNode : public HTMLNode {
|
2019-07-25 20:49:00 +00:00
|
|
|
TagNode(HTMLTag Tag) : HTMLNode(NodeType::NODE_TAG), Tag(Tag) {}
|
2019-07-10 19:03:25 +00:00
|
|
|
TagNode(HTMLTag Tag, const Twine &Text) : TagNode(Tag) {
|
2019-08-14 23:52:23 +00:00
|
|
|
Children.emplace_back(std::make_unique<TextNode>(Text.str()));
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
2019-07-25 20:49:00 +00:00
|
|
|
HTMLTag Tag; // Name of HTML Tag (p, div, h1)
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes
|
2019-08-17 01:45:03 +00:00
|
|
|
std::vector<std::pair<std::string, std::string>>
|
2019-07-10 19:03:25 +00:00
|
|
|
Attributes; // List of key-value attributes for tag
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
void render(llvm::raw_ostream &OS, int IndentationLevel) override;
|
2019-07-10 19:03:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
constexpr const char *kDoctypeDecl = "<!DOCTYPE html>";
|
|
|
|
|
|
|
|
struct HTMLFile {
|
|
|
|
std::vector<std::unique_ptr<HTMLNode>> Children; // List of child nodes
|
2024-06-27 13:46:35 -07:00
|
|
|
void render(llvm::raw_ostream &OS) {
|
2019-07-10 19:03:25 +00:00
|
|
|
OS << kDoctypeDecl << "\n";
|
|
|
|
for (const auto &C : Children) {
|
2024-06-27 13:46:35 -07:00
|
|
|
C->render(OS, 0);
|
2019-07-10 19:03:25 +00:00
|
|
|
OS << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
bool HTMLTag::isSelfClosing() const {
|
2019-07-10 19:03:25 +00:00
|
|
|
switch (Value) {
|
|
|
|
case HTMLTag::TAG_META:
|
2019-07-25 21:27:50 +00:00
|
|
|
case HTMLTag::TAG_LINK:
|
2019-07-10 19:03:25 +00:00
|
|
|
return true;
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_A:
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_DIV:
|
2019-08-16 18:38:11 +00:00
|
|
|
case HTMLTag::TAG_FOOTER:
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_H1:
|
|
|
|
case HTMLTag::TAG_H2:
|
|
|
|
case HTMLTag::TAG_H3:
|
2019-08-16 18:38:11 +00:00
|
|
|
case HTMLTag::TAG_HEADER:
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_LI:
|
2019-08-16 18:38:11 +00:00
|
|
|
case HTMLTag::TAG_MAIN:
|
|
|
|
case HTMLTag::TAG_OL:
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_P:
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_SCRIPT:
|
|
|
|
case HTMLTag::TAG_SPAN:
|
|
|
|
case HTMLTag::TAG_TITLE:
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_UL:
|
2024-08-16 18:37:17 -04:00
|
|
|
case HTMLTag::TAG_TABLE:
|
|
|
|
case HTMLTag::TAG_THEAD:
|
|
|
|
case HTMLTag::TAG_TBODY:
|
|
|
|
case HTMLTag::TAG_TR:
|
|
|
|
case HTMLTag::TAG_TD:
|
|
|
|
case HTMLTag::TAG_TH:
|
2019-07-10 19:03:25 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-07-11 06:50:10 +00:00
|
|
|
llvm_unreachable("Unhandled HTMLTag::TagType");
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
2024-07-08 16:30:18 -07:00
|
|
|
StringRef HTMLTag::toString() const {
|
2019-07-10 19:03:25 +00:00
|
|
|
switch (Value) {
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_A:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "a";
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_DIV:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "div";
|
2019-08-16 18:38:11 +00:00
|
|
|
case HTMLTag::TAG_FOOTER:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "footer";
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_H1:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "h1";
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_H2:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "h2";
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_H3:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "h3";
|
2019-08-16 18:38:11 +00:00
|
|
|
case HTMLTag::TAG_HEADER:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "header";
|
2019-07-10 19:03:25 +00:00
|
|
|
case HTMLTag::TAG_LI:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "li";
|
2019-07-25 21:27:50 +00:00
|
|
|
case HTMLTag::TAG_LINK:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "link";
|
2019-08-16 18:38:11 +00:00
|
|
|
case HTMLTag::TAG_MAIN:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "main";
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_META:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "meta";
|
2019-08-16 18:38:11 +00:00
|
|
|
case HTMLTag::TAG_OL:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "ol";
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_P:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "p";
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_SCRIPT:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "script";
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_SPAN:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "span";
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_TITLE:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "title";
|
2019-08-06 18:31:46 +00:00
|
|
|
case HTMLTag::TAG_UL:
|
2024-07-08 16:30:18 -07:00
|
|
|
return "ul";
|
2024-08-16 18:37:17 -04:00
|
|
|
case HTMLTag::TAG_TABLE:
|
|
|
|
return "table";
|
|
|
|
case HTMLTag::TAG_THEAD:
|
|
|
|
return "thead";
|
|
|
|
case HTMLTag::TAG_TBODY:
|
|
|
|
return "tbody";
|
|
|
|
case HTMLTag::TAG_TR:
|
|
|
|
return "tr";
|
|
|
|
case HTMLTag::TAG_TD:
|
|
|
|
return "td";
|
|
|
|
case HTMLTag::TAG_TH:
|
|
|
|
return "th";
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
2019-07-11 06:50:10 +00:00
|
|
|
llvm_unreachable("Unhandled HTMLTag::TagType");
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
void TextNode::render(llvm::raw_ostream &OS, int IndentationLevel) {
|
2019-07-25 20:49:00 +00:00
|
|
|
OS.indent(IndentationLevel * 2);
|
2019-07-25 20:14:45 +00:00
|
|
|
printHTMLEscaped(Text, OS);
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
void TagNode::render(llvm::raw_ostream &OS, int IndentationLevel) {
|
2019-07-25 20:49:00 +00:00
|
|
|
// Children nodes are rendered in the same line if all of them are text nodes
|
|
|
|
bool InlineChildren = true;
|
|
|
|
for (const auto &C : Children)
|
|
|
|
if (C->Type == NodeType::NODE_TAG) {
|
|
|
|
InlineChildren = false;
|
|
|
|
break;
|
|
|
|
}
|
2019-07-10 19:03:25 +00:00
|
|
|
OS.indent(IndentationLevel * 2);
|
2024-07-08 16:30:18 -07:00
|
|
|
OS << "<" << Tag.toString();
|
2019-07-10 19:03:25 +00:00
|
|
|
for (const auto &A : Attributes)
|
2019-08-16 18:38:11 +00:00
|
|
|
OS << " " << A.first << "=\"" << A.second << "\"";
|
2024-06-27 13:46:35 -07:00
|
|
|
if (Tag.isSelfClosing()) {
|
2019-07-10 19:03:25 +00:00
|
|
|
OS << "/>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
OS << ">";
|
|
|
|
if (!InlineChildren)
|
|
|
|
OS << "\n";
|
2019-07-12 18:32:00 +00:00
|
|
|
bool NewLineRendered = true;
|
2019-07-10 19:03:25 +00:00
|
|
|
for (const auto &C : Children) {
|
2019-07-12 18:32:00 +00:00
|
|
|
int ChildrenIndentation =
|
|
|
|
InlineChildren || !NewLineRendered ? 0 : IndentationLevel + 1;
|
2024-06-27 13:46:35 -07:00
|
|
|
C->render(OS, ChildrenIndentation);
|
2019-07-12 18:32:00 +00:00
|
|
|
if (!InlineChildren && (C == Children.back() ||
|
|
|
|
(C->Type != NodeType::NODE_TEXT ||
|
|
|
|
(&C + 1)->get()->Type != NodeType::NODE_TEXT))) {
|
2019-07-10 19:03:25 +00:00
|
|
|
OS << "\n";
|
2019-07-12 18:32:00 +00:00
|
|
|
NewLineRendered = true;
|
|
|
|
} else
|
|
|
|
NewLineRendered = false;
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
if (!InlineChildren)
|
|
|
|
OS.indent(IndentationLevel * 2);
|
2024-07-08 16:30:18 -07:00
|
|
|
OS << "</" << Tag.toString() << ">";
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
2019-07-12 18:32:00 +00:00
|
|
|
template <typename Derived, typename Base,
|
|
|
|
typename = std::enable_if<std::is_base_of<Derived, Base>::value>>
|
2024-06-27 13:46:35 -07:00
|
|
|
static void appendVector(std::vector<Derived> &&New,
|
2019-07-12 18:32:00 +00:00
|
|
|
std::vector<Base> &Original) {
|
|
|
|
std::move(New.begin(), New.end(), std::back_inserter(Original));
|
|
|
|
}
|
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
// HTML generation
|
|
|
|
|
2019-08-06 18:31:46 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
2019-07-25 22:46:40 +00:00
|
|
|
genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
|
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
|
|
|
for (const auto &FilePath : CDCtx.UserStylesheets) {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto LinkNode = std::make_unique<TagNode>(HTMLTag::TAG_LINK);
|
2019-08-16 18:38:11 +00:00
|
|
|
LinkNode->Attributes.emplace_back("rel", "stylesheet");
|
2019-07-25 22:46:40 +00:00
|
|
|
SmallString<128> StylesheetPath = computeRelativePath("", InfoPath);
|
|
|
|
llvm::sys::path::append(StylesheetPath,
|
|
|
|
llvm::sys::path::filename(FilePath));
|
2019-07-26 18:02:42 +00:00
|
|
|
// Paths in HTML must be in posix-style
|
|
|
|
llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
|
2024-01-26 23:46:24 -08:00
|
|
|
LinkNode->Attributes.emplace_back("href", std::string(StylesheetPath));
|
2019-07-25 22:46:40 +00:00
|
|
|
Out.emplace_back(std::move(LinkNode));
|
|
|
|
}
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-08-06 18:31:46 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genJsScriptsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) {
|
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2024-06-27 16:22:01 -04:00
|
|
|
|
|
|
|
// index_json.js is part of every generated HTML file
|
|
|
|
SmallString<128> IndexJSONPath = computeRelativePath("", InfoPath);
|
|
|
|
auto IndexJSONNode = std::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
|
|
|
|
llvm::sys::path::append(IndexJSONPath, "index_json.js");
|
|
|
|
llvm::sys::path::native(IndexJSONPath, llvm::sys::path::Style::posix);
|
|
|
|
IndexJSONNode->Attributes.emplace_back("src", std::string(IndexJSONPath));
|
|
|
|
Out.emplace_back(std::move(IndexJSONNode));
|
|
|
|
|
2019-08-06 18:31:46 +00:00
|
|
|
for (const auto &FilePath : CDCtx.JsScripts) {
|
|
|
|
SmallString<128> ScriptPath = computeRelativePath("", InfoPath);
|
2024-06-27 16:22:01 -04:00
|
|
|
auto ScriptNode = std::make_unique<TagNode>(HTMLTag::TAG_SCRIPT);
|
2019-08-06 18:31:46 +00:00
|
|
|
llvm::sys::path::append(ScriptPath, llvm::sys::path::filename(FilePath));
|
2019-08-06 20:59:14 +00:00
|
|
|
// Paths in HTML must be in posix-style
|
|
|
|
llvm::sys::path::native(ScriptPath, llvm::sys::path::Style::posix);
|
2024-01-26 23:46:24 -08:00
|
|
|
ScriptNode->Attributes.emplace_back("src", std::string(ScriptPath));
|
2019-08-06 18:31:46 +00:00
|
|
|
Out.emplace_back(std::move(ScriptNode));
|
|
|
|
}
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-07-12 18:32:00 +00:00
|
|
|
static std::unique_ptr<TagNode> genLink(const Twine &Text, const Twine &Link) {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto LinkNode = std::make_unique<TagNode>(HTMLTag::TAG_A, Text);
|
2019-08-16 18:38:11 +00:00
|
|
|
LinkNode->Attributes.emplace_back("href", Link.str());
|
2019-07-12 18:32:00 +00:00
|
|
|
return LinkNode;
|
|
|
|
}
|
|
|
|
|
2019-08-07 21:04:52 +00:00
|
|
|
static std::unique_ptr<HTMLNode>
|
2019-08-12 18:42:46 +00:00
|
|
|
genReference(const Reference &Type, StringRef CurrentDirectory,
|
2023-01-07 20:19:42 -08:00
|
|
|
std::optional<StringRef> JumpToSection = std::nullopt) {
|
2022-09-27 23:29:04 +00:00
|
|
|
if (Type.Path.empty()) {
|
2019-08-07 21:04:52 +00:00
|
|
|
if (!JumpToSection)
|
2019-08-14 23:52:23 +00:00
|
|
|
return std::make_unique<TextNode>(Type.Name);
|
2024-06-27 13:46:35 -07:00
|
|
|
return genLink(Type.Name, "#" + *JumpToSection);
|
2019-08-07 21:04:52 +00:00
|
|
|
}
|
2020-03-06 17:33:56 -08:00
|
|
|
llvm::SmallString<64> Path = Type.getRelativeFilePath(CurrentDirectory);
|
|
|
|
llvm::sys::path::append(Path, Type.getFileBaseName() + ".html");
|
|
|
|
|
2019-07-29 22:03:39 +00:00
|
|
|
// Paths in HTML must be in posix-style
|
|
|
|
llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
|
2019-08-07 21:04:52 +00:00
|
|
|
if (JumpToSection)
|
2022-06-20 23:35:53 -07:00
|
|
|
Path += ("#" + *JumpToSection).str();
|
2019-07-12 18:32:00 +00:00
|
|
|
return genLink(Type.Name, Path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::unique_ptr<HTMLNode>>
|
|
|
|
genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs,
|
|
|
|
const StringRef &CurrentDirectory) {
|
|
|
|
std::vector<std::unique_ptr<HTMLNode>> Out;
|
|
|
|
for (const auto &R : Refs) {
|
|
|
|
if (&R != Refs.begin())
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TextNode>(", "));
|
2019-08-12 18:42:46 +00:00
|
|
|
Out.emplace_back(genReference(R, CurrentDirectory));
|
2019-07-12 18:32:00 +00:00
|
|
|
}
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-08-09 17:49:41 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
|
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
|
|
|
|
StringRef ParentInfoDir);
|
2024-08-12 15:08:30 -04:00
|
|
|
static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
2019-08-09 17:49:41 +00:00
|
|
|
genEnumsBlock(const std::vector<EnumInfo> &Enums,
|
|
|
|
const ClangDocContext &CDCtx) {
|
2019-07-10 19:03:25 +00:00
|
|
|
if (Enums.empty())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Enums"));
|
2019-08-16 18:38:11 +00:00
|
|
|
Out.back()->Attributes.emplace_back("id", "Enums");
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
|
2019-07-10 19:03:25 +00:00
|
|
|
auto &DivBody = Out.back();
|
|
|
|
for (const auto &E : Enums) {
|
2019-08-09 17:49:41 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E, CDCtx);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(Nodes), DivBody->Children);
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::unique_ptr<TagNode>
|
2022-09-19 21:52:24 +00:00
|
|
|
genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
|
2019-07-10 19:03:25 +00:00
|
|
|
if (Members.empty())
|
|
|
|
return nullptr;
|
|
|
|
|
2024-08-16 18:37:17 -04:00
|
|
|
auto List = std::make_unique<TagNode>(HTMLTag::TAG_TBODY);
|
|
|
|
|
|
|
|
for (const auto &M : Members) {
|
|
|
|
auto TRNode = std::make_unique<TagNode>(HTMLTag::TAG_TR);
|
|
|
|
TRNode->Children.emplace_back(
|
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Name));
|
|
|
|
// Use user supplied value if it exists, otherwise use the value
|
|
|
|
if (!M.ValueExpr.empty()) {
|
|
|
|
TRNode->Children.emplace_back(
|
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.ValueExpr));
|
|
|
|
} else {
|
|
|
|
TRNode->Children.emplace_back(
|
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
|
|
|
|
}
|
|
|
|
if (!M.Description.empty()) {
|
|
|
|
auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
|
|
|
|
TD->Children.emplace_back(genHTML(M.Description));
|
|
|
|
TRNode->Children.emplace_back(std::move(TD));
|
|
|
|
}
|
|
|
|
List->Children.emplace_back(std::move(TRNode));
|
|
|
|
}
|
2019-07-10 19:03:25 +00:00
|
|
|
return List;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
2019-07-12 18:32:00 +00:00
|
|
|
genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
|
2019-08-09 17:49:41 +00:00
|
|
|
const ClangDocContext &CDCtx, StringRef ParentInfoDir) {
|
2019-07-10 19:03:25 +00:00
|
|
|
if (Functions.empty())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Functions"));
|
2019-08-16 18:38:11 +00:00
|
|
|
Out.back()->Attributes.emplace_back("id", "Functions");
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_DIV));
|
2019-07-10 19:03:25 +00:00
|
|
|
auto &DivBody = Out.back();
|
|
|
|
for (const auto &F : Functions) {
|
2019-08-09 17:49:41 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Nodes =
|
|
|
|
genHTML(F, CDCtx, ParentInfoDir);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(Nodes), DivBody->Children);
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
2019-07-12 18:32:00 +00:00
|
|
|
genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
|
|
|
|
StringRef ParentInfoDir) {
|
2019-07-10 19:03:25 +00:00
|
|
|
if (Members.empty())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, "Members"));
|
2019-08-16 18:38:11 +00:00
|
|
|
Out.back()->Attributes.emplace_back("id", "Members");
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
|
2019-07-10 19:03:25 +00:00
|
|
|
auto &ULBody = Out.back();
|
|
|
|
for (const auto &M : Members) {
|
2020-05-27 18:17:07 +02:00
|
|
|
std::string Access = getAccessSpelling(M.Access).str();
|
2019-07-10 19:03:25 +00:00
|
|
|
if (Access != "")
|
|
|
|
Access = Access + " ";
|
2019-08-14 23:52:23 +00:00
|
|
|
auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
|
2024-08-12 15:08:30 -04:00
|
|
|
auto MemberDecl = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
|
|
|
MemberDecl->Children.emplace_back(std::make_unique<TextNode>(Access));
|
|
|
|
MemberDecl->Children.emplace_back(genReference(M.Type, ParentInfoDir));
|
|
|
|
MemberDecl->Children.emplace_back(std::make_unique<TextNode>(" " + M.Name));
|
|
|
|
if (!M.Description.empty())
|
|
|
|
LIBody->Children.emplace_back(genHTML(M.Description));
|
|
|
|
LIBody->Children.emplace_back(std::move(MemberDecl));
|
2019-07-12 18:32:00 +00:00
|
|
|
ULBody->Children.emplace_back(std::move(LIBody));
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genReferencesBlock(const std::vector<Reference> &References,
|
2019-08-12 18:42:46 +00:00
|
|
|
llvm::StringRef Title, StringRef ParentPath) {
|
2019-07-10 19:03:25 +00:00
|
|
|
if (References.empty())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H2, Title));
|
2020-01-29 10:32:04 +01:00
|
|
|
Out.back()->Attributes.emplace_back("id", std::string(Title));
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
|
2019-07-10 19:03:25 +00:00
|
|
|
auto &ULBody = Out.back();
|
2019-08-12 18:42:46 +00:00
|
|
|
for (const auto &R : References) {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto LiNode = std::make_unique<TagNode>(HTMLTag::TAG_LI);
|
2019-08-12 18:42:46 +00:00
|
|
|
LiNode->Children.emplace_back(genReference(R, ParentPath));
|
|
|
|
ULBody->Children.emplace_back(std::move(LiNode));
|
|
|
|
}
|
2019-07-10 19:03:25 +00:00
|
|
|
return Out;
|
|
|
|
}
|
2025-03-29 01:45:09 +02:00
|
|
|
static std::unique_ptr<TagNode> writeSourceFileRef(const ClangDocContext &CDCtx,
|
|
|
|
const Location &L) {
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2025-03-29 01:45:09 +02:00
|
|
|
if (!L.IsFileInRootDir && !CDCtx.RepositoryUrl)
|
2019-08-14 23:52:23 +00:00
|
|
|
return std::make_unique<TagNode>(
|
2019-08-09 17:49:41 +00:00
|
|
|
HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
|
|
|
|
" of file " + L.Filename);
|
2025-03-29 01:45:09 +02:00
|
|
|
|
|
|
|
SmallString<128> FileURL(CDCtx.RepositoryUrl.value_or(""));
|
2025-02-06 11:26:17 -08:00
|
|
|
llvm::sys::path::append(
|
|
|
|
FileURL, llvm::sys::path::Style::posix,
|
|
|
|
// If we're on Windows, the file name will be in the wrong format, and
|
|
|
|
// append won't convert the full path being appended to the correct
|
|
|
|
// format, so we need to do that here.
|
|
|
|
llvm::sys::path::convert_to_slash(
|
|
|
|
L.Filename,
|
|
|
|
// The style here is the current style of the path, not the one we're
|
|
|
|
// targeting. If the string is already in the posix style, it will do
|
|
|
|
// nothing.
|
|
|
|
llvm::sys::path::Style::windows));
|
2019-08-14 23:52:23 +00:00
|
|
|
auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
|
|
|
|
Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
|
2019-08-09 17:49:41 +00:00
|
|
|
auto LocNumberNode =
|
2019-08-14 23:52:23 +00:00
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
|
2019-08-09 17:49:41 +00:00
|
|
|
// The links to a specific line in the source code use the github /
|
|
|
|
// googlesource notation so it won't work for all hosting pages.
|
2019-08-16 18:38:11 +00:00
|
|
|
LocNumberNode->Attributes.emplace_back(
|
2025-03-29 01:45:09 +02:00
|
|
|
"href", formatv("{0}#{1}{2}", FileURL,
|
|
|
|
CDCtx.RepositoryLinePrefix.value_or(""), L.LineNumber));
|
2019-08-09 17:49:41 +00:00
|
|
|
Node->Children.emplace_back(std::move(LocNumberNode));
|
2019-08-14 23:52:23 +00:00
|
|
|
Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
|
|
|
|
auto LocFileNode = std::make_unique<TagNode>(
|
2019-08-09 17:49:41 +00:00
|
|
|
HTMLTag::TAG_A, llvm::sys::path::filename(FileURL));
|
2024-01-14 12:17:56 -08:00
|
|
|
LocFileNode->Attributes.emplace_back("href", std::string(FileURL));
|
2019-08-09 17:49:41 +00:00
|
|
|
Node->Children.emplace_back(std::move(LocFileNode));
|
|
|
|
return Node;
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
2025-03-29 01:45:09 +02:00
|
|
|
static void maybeWriteSourceFileRef(std::vector<std::unique_ptr<TagNode>> &Out,
|
|
|
|
const ClangDocContext &CDCtx,
|
|
|
|
const std::optional<Location> &DefLoc) {
|
|
|
|
if (DefLoc)
|
|
|
|
Out.emplace_back(writeSourceFileRef(CDCtx, *DefLoc));
|
|
|
|
}
|
|
|
|
|
2019-08-06 18:31:46 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
2019-08-16 18:38:11 +00:00
|
|
|
genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList);
|
|
|
|
|
|
|
|
// Generates a list of child nodes for the HTML head tag
|
|
|
|
// It contains a meta node, link nodes to import CSS files, and script nodes to
|
|
|
|
// import JS files
|
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genFileHeadNodes(StringRef Title, StringRef InfoPath,
|
|
|
|
const ClangDocContext &CDCtx) {
|
2019-08-06 18:31:46 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2019-08-14 23:52:23 +00:00
|
|
|
auto MetaNode = std::make_unique<TagNode>(HTMLTag::TAG_META);
|
2019-08-16 18:38:11 +00:00
|
|
|
MetaNode->Attributes.emplace_back("charset", "utf-8");
|
2019-08-06 18:31:46 +00:00
|
|
|
Out.emplace_back(std::move(MetaNode));
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_TITLE, Title));
|
2019-08-06 18:31:46 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> StylesheetsNodes =
|
|
|
|
genStylesheetsHTML(InfoPath, CDCtx);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(StylesheetsNodes), Out);
|
2019-08-06 18:31:46 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> JsNodes =
|
|
|
|
genJsScriptsHTML(InfoPath, CDCtx);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(JsNodes), Out);
|
2019-08-06 18:31:46 +00:00
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-08-16 18:38:11 +00:00
|
|
|
// Generates a header HTML node that can be used for any file
|
|
|
|
// It contains the project name
|
|
|
|
static std::unique_ptr<TagNode> genFileHeaderNode(StringRef ProjectName) {
|
|
|
|
auto HeaderNode = std::make_unique<TagNode>(HTMLTag::TAG_HEADER, ProjectName);
|
|
|
|
HeaderNode->Attributes.emplace_back("id", "project-title");
|
|
|
|
return HeaderNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generates a main HTML node that has all the main content of an info file
|
|
|
|
// It contains both indexes and the info's documented information
|
|
|
|
// This function should only be used for the info files (not for the file that
|
|
|
|
// only has the general index)
|
|
|
|
static std::unique_ptr<TagNode> genInfoFileMainNode(
|
|
|
|
StringRef InfoPath,
|
|
|
|
std::vector<std::unique_ptr<TagNode>> &MainContentInnerNodes,
|
|
|
|
const Index &InfoIndex) {
|
|
|
|
auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
|
|
|
|
|
|
|
|
auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
|
|
|
LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
|
2020-01-29 10:32:04 +01:00
|
|
|
LeftSidebarNode->Attributes.emplace_back("path", std::string(InfoPath));
|
2019-08-16 18:38:11 +00:00
|
|
|
LeftSidebarNode->Attributes.emplace_back(
|
|
|
|
"class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
|
|
|
|
|
|
|
|
auto MainContentNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
|
|
|
MainContentNode->Attributes.emplace_back("id", "main-content");
|
|
|
|
MainContentNode->Attributes.emplace_back(
|
|
|
|
"class", "col-xs-12 col-sm-9 col-md-8 main-content");
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(MainContentInnerNodes), MainContentNode->Children);
|
2019-08-16 18:38:11 +00:00
|
|
|
|
|
|
|
auto RightSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
|
|
|
RightSidebarNode->Attributes.emplace_back("id", "sidebar-right");
|
|
|
|
RightSidebarNode->Attributes.emplace_back(
|
|
|
|
"class", "col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right");
|
|
|
|
std::vector<std::unique_ptr<TagNode>> InfoIndexHTML =
|
|
|
|
genHTML(InfoIndex, InfoPath, true);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(InfoIndexHTML), RightSidebarNode->Children);
|
2019-08-16 18:38:11 +00:00
|
|
|
|
|
|
|
MainNode->Children.emplace_back(std::move(LeftSidebarNode));
|
|
|
|
MainNode->Children.emplace_back(std::move(MainContentNode));
|
|
|
|
MainNode->Children.emplace_back(std::move(RightSidebarNode));
|
|
|
|
|
|
|
|
return MainNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generates a footer HTML node that can be used for any file
|
|
|
|
// It contains clang-doc's version
|
|
|
|
static std::unique_ptr<TagNode> genFileFooterNode() {
|
|
|
|
auto FooterNode = std::make_unique<TagNode>(HTMLTag::TAG_FOOTER);
|
|
|
|
auto SpanNode = std::make_unique<TagNode>(
|
|
|
|
HTMLTag::TAG_SPAN, clang::getClangToolFullVersion("clang-doc"));
|
|
|
|
SpanNode->Attributes.emplace_back("class", "no-break");
|
|
|
|
FooterNode->Children.emplace_back(std::move(SpanNode));
|
|
|
|
return FooterNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generates a complete HTMLFile for an Info
|
|
|
|
static HTMLFile
|
|
|
|
genInfoFile(StringRef Title, StringRef InfoPath,
|
|
|
|
std::vector<std::unique_ptr<TagNode>> &MainContentNodes,
|
|
|
|
const Index &InfoIndex, const ClangDocContext &CDCtx) {
|
|
|
|
HTMLFile F;
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> HeadNodes =
|
|
|
|
genFileHeadNodes(Title, InfoPath, CDCtx);
|
|
|
|
std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
|
|
|
|
std::unique_ptr<TagNode> MainNode =
|
|
|
|
genInfoFileMainNode(InfoPath, MainContentNodes, InfoIndex);
|
|
|
|
std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(HeadNodes), F.Children);
|
2019-08-16 18:38:11 +00:00
|
|
|
F.Children.emplace_back(std::move(HeaderNode));
|
|
|
|
F.Children.emplace_back(std::move(MainNode));
|
|
|
|
F.Children.emplace_back(std::move(FooterNode));
|
|
|
|
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
|
2019-08-07 21:04:52 +00:00
|
|
|
template <typename T,
|
|
|
|
typename = std::enable_if<std::is_base_of<T, Info>::value>>
|
|
|
|
static Index genInfoIndexItem(const std::vector<T> &Infos, StringRef Title) {
|
|
|
|
Index Idx(Title, Title);
|
|
|
|
for (const auto &C : Infos)
|
|
|
|
Idx.Children.emplace_back(C.extractName(),
|
|
|
|
llvm::toHex(llvm::toStringRef(C.USR)));
|
|
|
|
return Idx;
|
|
|
|
}
|
|
|
|
|
2019-08-16 18:38:11 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList) {
|
2019-08-07 21:04:52 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
|
|
|
if (!Index.Name.empty()) {
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_SPAN));
|
2019-08-07 21:04:52 +00:00
|
|
|
auto &SpanBody = Out.back();
|
|
|
|
if (!Index.JumpToSection)
|
2019-08-12 18:42:46 +00:00
|
|
|
SpanBody->Children.emplace_back(genReference(Index, InfoPath));
|
2019-08-07 21:04:52 +00:00
|
|
|
else
|
2021-07-08 13:30:14 -07:00
|
|
|
SpanBody->Children.emplace_back(
|
2022-06-20 23:35:53 -07:00
|
|
|
genReference(Index, InfoPath, Index.JumpToSection->str()));
|
2019-08-07 21:04:52 +00:00
|
|
|
}
|
|
|
|
if (Index.Children.empty())
|
|
|
|
return Out;
|
2019-08-16 18:38:11 +00:00
|
|
|
// Only the outermost list should use ol, the others should use ul
|
|
|
|
HTMLTag ListHTMLTag = IsOutermostList ? HTMLTag::TAG_OL : HTMLTag::TAG_UL;
|
|
|
|
Out.emplace_back(std::make_unique<TagNode>(ListHTMLTag));
|
2019-08-07 21:04:52 +00:00
|
|
|
const auto &UlBody = Out.back();
|
|
|
|
for (const auto &C : Index.Children) {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto LiBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
|
2019-08-16 18:38:11 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(C, InfoPath, false);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(Nodes), LiBody->Children);
|
2019-08-07 21:04:52 +00:00
|
|
|
UlBody->Children.emplace_back(std::move(LiBody));
|
|
|
|
}
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
|
|
|
|
if (I.Kind == "FullComment") {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto FullComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
2019-07-10 19:03:25 +00:00
|
|
|
for (const auto &Child : I.Children) {
|
|
|
|
std::unique_ptr<HTMLNode> Node = genHTML(*Child);
|
|
|
|
if (Node)
|
|
|
|
FullComment->Children.emplace_back(std::move(Node));
|
|
|
|
}
|
|
|
|
return std::move(FullComment);
|
2024-06-27 13:46:35 -07:00
|
|
|
}
|
2024-08-16 18:37:17 -04:00
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
if (I.Kind == "ParagraphComment") {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
|
2019-07-10 19:03:25 +00:00
|
|
|
for (const auto &Child : I.Children) {
|
|
|
|
std::unique_ptr<HTMLNode> Node = genHTML(*Child);
|
|
|
|
if (Node)
|
|
|
|
ParagraphComment->Children.emplace_back(std::move(Node));
|
|
|
|
}
|
|
|
|
if (ParagraphComment->Children.empty())
|
|
|
|
return nullptr;
|
|
|
|
return std::move(ParagraphComment);
|
2024-06-27 13:46:35 -07:00
|
|
|
}
|
|
|
|
|
2024-08-20 03:58:47 -04:00
|
|
|
if (I.Kind == "BlockCommandComment") {
|
|
|
|
auto BlockComment = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
|
|
|
BlockComment->Children.emplace_back(
|
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_DIV, I.Name));
|
|
|
|
for (const auto &Child : I.Children) {
|
|
|
|
std::unique_ptr<HTMLNode> Node = genHTML(*Child);
|
|
|
|
if (Node)
|
|
|
|
BlockComment->Children.emplace_back(std::move(Node));
|
|
|
|
}
|
|
|
|
if (BlockComment->Children.empty())
|
|
|
|
return nullptr;
|
|
|
|
return std::move(BlockComment);
|
|
|
|
}
|
2024-06-27 13:46:35 -07:00
|
|
|
if (I.Kind == "TextComment") {
|
2019-07-10 19:03:25 +00:00
|
|
|
if (I.Text == "")
|
|
|
|
return nullptr;
|
2019-08-14 23:52:23 +00:00
|
|
|
return std::make_unique<TextNode>(I.Text);
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto CommentBlock = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
2019-07-10 19:03:25 +00:00
|
|
|
for (const auto &Child : C) {
|
|
|
|
if (std::unique_ptr<HTMLNode> Node = genHTML(Child))
|
|
|
|
CommentBlock->Children.emplace_back(std::move(Node));
|
|
|
|
}
|
|
|
|
return CommentBlock;
|
|
|
|
}
|
|
|
|
|
2019-08-09 17:49:41 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2024-06-27 13:46:35 -07:00
|
|
|
std::string EnumType = I.Scoped ? "enum class " : "enum ";
|
2024-08-16 18:37:17 -04:00
|
|
|
// Determine if enum members have comments attached
|
|
|
|
bool HasComments = std::any_of(
|
|
|
|
I.Members.begin(), I.Members.end(),
|
|
|
|
[](const EnumValueInfo &M) { return !M.Description.empty(); });
|
|
|
|
std::unique_ptr<TagNode> Table =
|
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
|
|
|
|
std::unique_ptr<TagNode> THead =
|
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_THEAD);
|
|
|
|
std::unique_ptr<TagNode> TRow = std::make_unique<TagNode>(HTMLTag::TAG_TR);
|
|
|
|
std::unique_ptr<TagNode> TD =
|
|
|
|
std::make_unique<TagNode>(HTMLTag::TAG_TH, EnumType + I.Name);
|
|
|
|
// Span 3 columns if enum has comments
|
|
|
|
TD->Attributes.emplace_back("colspan", HasComments ? "3" : "2");
|
|
|
|
|
|
|
|
Table->Attributes.emplace_back("id", llvm::toHex(llvm::toStringRef(I.USR)));
|
|
|
|
TRow->Children.emplace_back(std::move(TD));
|
|
|
|
THead->Children.emplace_back(std::move(TRow));
|
|
|
|
Table->Children.emplace_back(std::move(THead));
|
|
|
|
|
|
|
|
if (std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members))
|
|
|
|
Table->Children.emplace_back(std::move(Node));
|
|
|
|
|
|
|
|
Out.emplace_back(std::move(Table));
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2025-03-29 01:45:09 +02:00
|
|
|
maybeWriteSourceFileRef(Out, CDCtx, I.DefLoc);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
std::string Description;
|
|
|
|
if (!I.Description.empty())
|
|
|
|
Out.emplace_back(genHTML(I.Description));
|
|
|
|
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-08-09 17:49:41 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
|
|
|
|
StringRef ParentInfoDir) {
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name));
|
2019-08-07 21:04:52 +00:00
|
|
|
// USR is used as id for functions instead of name to disambiguate function
|
|
|
|
// overloads.
|
2019-08-16 18:38:11 +00:00
|
|
|
Out.back()->Attributes.emplace_back("id",
|
|
|
|
llvm::toHex(llvm::toStringRef(I.USR)));
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
|
2019-07-12 18:32:00 +00:00
|
|
|
auto &FunctionHeader = Out.back();
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2020-05-27 18:17:07 +02:00
|
|
|
std::string Access = getAccessSpelling(I.Access).str();
|
2019-07-10 19:03:25 +00:00
|
|
|
if (Access != "")
|
2019-07-12 18:32:00 +00:00
|
|
|
FunctionHeader->Children.emplace_back(
|
2019-08-14 23:52:23 +00:00
|
|
|
std::make_unique<TextNode>(Access + " "));
|
2019-07-12 18:32:00 +00:00
|
|
|
if (I.ReturnType.Type.Name != "") {
|
|
|
|
FunctionHeader->Children.emplace_back(
|
2019-08-12 18:42:46 +00:00
|
|
|
genReference(I.ReturnType.Type, ParentInfoDir));
|
2019-08-14 23:52:23 +00:00
|
|
|
FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(" "));
|
2019-07-12 18:32:00 +00:00
|
|
|
}
|
|
|
|
FunctionHeader->Children.emplace_back(
|
2019-08-14 23:52:23 +00:00
|
|
|
std::make_unique<TextNode>(I.Name + "("));
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2019-07-12 18:32:00 +00:00
|
|
|
for (const auto &P : I.Params) {
|
|
|
|
if (&P != I.Params.begin())
|
2019-08-14 23:52:23 +00:00
|
|
|
FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(", "));
|
2019-08-12 18:42:46 +00:00
|
|
|
FunctionHeader->Children.emplace_back(genReference(P.Type, ParentInfoDir));
|
2019-07-12 18:32:00 +00:00
|
|
|
FunctionHeader->Children.emplace_back(
|
2019-08-14 23:52:23 +00:00
|
|
|
std::make_unique<TextNode>(" " + P.Name));
|
2019-07-12 18:32:00 +00:00
|
|
|
}
|
2019-08-14 23:52:23 +00:00
|
|
|
FunctionHeader->Children.emplace_back(std::make_unique<TextNode>(")"));
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2025-03-29 01:45:09 +02:00
|
|
|
maybeWriteSourceFileRef(Out, CDCtx, I.DefLoc);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
std::string Description;
|
|
|
|
if (!I.Description.empty())
|
|
|
|
Out.emplace_back(genHTML(I.Description));
|
|
|
|
|
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-08-07 21:04:52 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
2019-08-09 17:49:41 +00:00
|
|
|
genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
|
|
|
|
std::string &InfoTitle) {
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
|
|
|
if (I.Name.str() == "")
|
|
|
|
InfoTitle = "Global Namespace";
|
|
|
|
else
|
|
|
|
InfoTitle = ("namespace " + I.Name).str();
|
|
|
|
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
std::string Description;
|
|
|
|
if (!I.Description.empty())
|
|
|
|
Out.emplace_back(genHTML(I.Description));
|
|
|
|
|
2020-03-06 17:33:56 -08:00
|
|
|
llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
|
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
|
2022-10-14 14:28:43 -07:00
|
|
|
genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(ChildNamespaces), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> ChildRecords =
|
2022-10-14 14:28:43 -07:00
|
|
|
genReferencesBlock(I.Children.Records, "Records", BasePath);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(ChildRecords), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
|
2022-10-14 14:28:43 -07:00
|
|
|
genFunctionsBlock(I.Children.Functions, CDCtx, BasePath);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(ChildFunctions), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> ChildEnums =
|
2022-10-14 14:28:43 -07:00
|
|
|
genEnumsBlock(I.Children.Enums, CDCtx);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(ChildEnums), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
if (!I.Children.Namespaces.empty())
|
2019-08-07 21:04:52 +00:00
|
|
|
InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
|
2022-10-14 14:28:43 -07:00
|
|
|
if (!I.Children.Records.empty())
|
2019-08-07 21:04:52 +00:00
|
|
|
InfoIndex.Children.emplace_back("Records", "Records");
|
2022-10-14 14:28:43 -07:00
|
|
|
if (!I.Children.Functions.empty())
|
2019-08-07 21:04:52 +00:00
|
|
|
InfoIndex.Children.emplace_back(
|
2022-10-14 14:28:43 -07:00
|
|
|
genInfoIndexItem(I.Children.Functions, "Functions"));
|
|
|
|
if (!I.Children.Enums.empty())
|
|
|
|
InfoIndex.Children.emplace_back(
|
|
|
|
genInfoIndexItem(I.Children.Enums, "Enums"));
|
2019-08-07 21:04:52 +00:00
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2019-08-07 21:04:52 +00:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
2019-08-09 17:49:41 +00:00
|
|
|
genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
|
|
|
|
std::string &InfoTitle) {
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> Out;
|
|
|
|
InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2025-03-29 01:45:09 +02:00
|
|
|
maybeWriteSourceFileRef(Out, CDCtx, I.DefLoc);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
std::string Description;
|
|
|
|
if (!I.Description.empty())
|
|
|
|
Out.emplace_back(genHTML(I.Description));
|
|
|
|
|
2019-07-12 18:32:00 +00:00
|
|
|
std::vector<std::unique_ptr<HTMLNode>> Parents =
|
|
|
|
genReferenceList(I.Parents, I.Path);
|
|
|
|
std::vector<std::unique_ptr<HTMLNode>> VParents =
|
|
|
|
genReferenceList(I.VirtualParents, I.Path);
|
2019-07-10 19:03:25 +00:00
|
|
|
if (!Parents.empty() || !VParents.empty()) {
|
2019-08-14 23:52:23 +00:00
|
|
|
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_P));
|
2019-07-12 18:32:00 +00:00
|
|
|
auto &PBody = Out.back();
|
2019-08-14 23:52:23 +00:00
|
|
|
PBody->Children.emplace_back(std::make_unique<TextNode>("Inherits from "));
|
2019-07-10 19:03:25 +00:00
|
|
|
if (Parents.empty())
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(VParents), PBody->Children);
|
2019-07-10 19:03:25 +00:00
|
|
|
else if (VParents.empty())
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(Parents), PBody->Children);
|
2019-07-12 18:32:00 +00:00
|
|
|
else {
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(Parents), PBody->Children);
|
2019-08-14 23:52:23 +00:00
|
|
|
PBody->Children.emplace_back(std::make_unique<TextNode>(", "));
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(VParents), PBody->Children);
|
2019-07-12 18:32:00 +00:00
|
|
|
}
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> Members =
|
2019-07-12 18:32:00 +00:00
|
|
|
genRecordMembersBlock(I.Members, I.Path);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(Members), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> ChildRecords =
|
2022-10-14 14:28:43 -07:00
|
|
|
genReferencesBlock(I.Children.Records, "Records", I.Path);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(ChildRecords), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
|
2022-10-14 14:28:43 -07:00
|
|
|
genFunctionsBlock(I.Children.Functions, CDCtx, I.Path);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(ChildFunctions), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> ChildEnums =
|
2022-10-14 14:28:43 -07:00
|
|
|
genEnumsBlock(I.Children.Enums, CDCtx);
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(ChildEnums), Out);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
2019-08-07 21:04:52 +00:00
|
|
|
if (!I.Members.empty())
|
|
|
|
InfoIndex.Children.emplace_back("Members", "Members");
|
2022-10-14 14:28:43 -07:00
|
|
|
if (!I.Children.Records.empty())
|
2019-08-07 21:04:52 +00:00
|
|
|
InfoIndex.Children.emplace_back("Records", "Records");
|
2022-10-14 14:28:43 -07:00
|
|
|
if (!I.Children.Functions.empty())
|
|
|
|
InfoIndex.Children.emplace_back(
|
|
|
|
genInfoIndexItem(I.Children.Functions, "Functions"));
|
|
|
|
if (!I.Children.Enums.empty())
|
2019-08-07 21:04:52 +00:00
|
|
|
InfoIndex.Children.emplace_back(
|
2022-10-14 14:28:43 -07:00
|
|
|
genInfoIndexItem(I.Children.Enums, "Enums"));
|
2019-08-07 21:04:52 +00:00
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
return Out;
|
|
|
|
}
|
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
static std::vector<std::unique_ptr<TagNode>>
|
|
|
|
genHTML(const TypedefInfo &I, const ClangDocContext &CDCtx,
|
|
|
|
std::string &InfoTitle) {
|
|
|
|
// TODO support typedefs in HTML.
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
/// Generator for HTML documentation.
|
|
|
|
class HTMLGenerator : public Generator {
|
|
|
|
public:
|
|
|
|
static const char *Format;
|
|
|
|
|
2022-11-29 12:35:58 -08:00
|
|
|
llvm::Error generateDocs(StringRef RootDir,
|
|
|
|
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
|
|
|
|
const ClangDocContext &CDCtx) override;
|
|
|
|
llvm::Error createResources(ClangDocContext &CDCtx) override;
|
2019-07-25 22:46:40 +00:00
|
|
|
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
|
|
|
|
const ClangDocContext &CDCtx) override;
|
2019-07-10 19:03:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const char *HTMLGenerator::Format = "html";
|
|
|
|
|
2022-11-29 12:35:58 -08:00
|
|
|
llvm::Error
|
|
|
|
HTMLGenerator::generateDocs(StringRef RootDir,
|
|
|
|
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
|
|
|
|
const ClangDocContext &CDCtx) {
|
|
|
|
// Track which directories we already tried to create.
|
|
|
|
llvm::StringSet<> CreatedDirs;
|
|
|
|
|
|
|
|
// Collect all output by file name and create the nexessary directories.
|
|
|
|
llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
|
|
|
|
for (const auto &Group : Infos) {
|
|
|
|
doc::Info *Info = Group.getValue().get();
|
|
|
|
|
|
|
|
llvm::SmallString<128> Path;
|
|
|
|
llvm::sys::path::native(RootDir, Path);
|
|
|
|
llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
|
2023-03-14 21:30:29 -07:00
|
|
|
if (!CreatedDirs.contains(Path)) {
|
2022-11-29 12:35:58 -08:00
|
|
|
if (std::error_code Err = llvm::sys::fs::create_directories(Path);
|
|
|
|
Err != std::error_code()) {
|
|
|
|
return llvm::createStringError(Err, "Failed to create directory '%s'.",
|
|
|
|
Path.c_str());
|
|
|
|
}
|
|
|
|
CreatedDirs.insert(Path);
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::sys::path::append(Path, Info->getFileBaseName() + ".html");
|
|
|
|
FileToInfos[Path].push_back(Info);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &Group : FileToInfos) {
|
|
|
|
std::error_code FileErr;
|
|
|
|
llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
|
2025-02-04 09:12:45 -05:00
|
|
|
llvm::sys::fs::OF_Text);
|
2022-11-29 12:35:58 -08:00
|
|
|
if (FileErr) {
|
|
|
|
return llvm::createStringError(FileErr, "Error opening file '%s'",
|
|
|
|
Group.getKey().str().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: https://github.com/llvm/llvm-project/issues/59073
|
|
|
|
// If there are multiple Infos for this file name (for example, template
|
|
|
|
// specializations), this will generate multiple complete web pages (with
|
|
|
|
// <DOCTYPE> and <title>, etc.) concatenated together. This generator needs
|
|
|
|
// some refactoring to be able to output the headers separately from the
|
|
|
|
// contents.
|
|
|
|
for (const auto &Info : Group.getValue()) {
|
|
|
|
if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
|
|
|
|
return Err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return llvm::Error::success();
|
|
|
|
}
|
|
|
|
|
2019-07-25 22:46:40 +00:00
|
|
|
llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
|
|
|
|
const ClangDocContext &CDCtx) {
|
2019-07-10 19:03:25 +00:00
|
|
|
std::string InfoTitle;
|
2019-08-16 18:38:11 +00:00
|
|
|
std::vector<std::unique_ptr<TagNode>> MainContentNodes;
|
2019-08-07 21:04:52 +00:00
|
|
|
Index InfoIndex;
|
2019-07-10 19:03:25 +00:00
|
|
|
switch (I->IT) {
|
2019-08-16 18:38:11 +00:00
|
|
|
case InfoType::IT_namespace:
|
|
|
|
MainContentNodes = genHTML(*static_cast<clang::doc::NamespaceInfo *>(I),
|
|
|
|
InfoIndex, CDCtx, InfoTitle);
|
2019-07-10 19:03:25 +00:00
|
|
|
break;
|
2019-08-16 18:38:11 +00:00
|
|
|
case InfoType::IT_record:
|
|
|
|
MainContentNodes = genHTML(*static_cast<clang::doc::RecordInfo *>(I),
|
|
|
|
InfoIndex, CDCtx, InfoTitle);
|
2019-07-10 19:03:25 +00:00
|
|
|
break;
|
2019-08-16 18:38:11 +00:00
|
|
|
case InfoType::IT_enum:
|
|
|
|
MainContentNodes = genHTML(*static_cast<clang::doc::EnumInfo *>(I), CDCtx);
|
2019-07-10 19:03:25 +00:00
|
|
|
break;
|
2019-08-16 18:38:11 +00:00
|
|
|
case InfoType::IT_function:
|
|
|
|
MainContentNodes =
|
2019-08-09 17:49:41 +00:00
|
|
|
genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
|
2019-07-10 19:03:25 +00:00
|
|
|
break;
|
2022-10-14 14:28:43 -07:00
|
|
|
case InfoType::IT_typedef:
|
|
|
|
MainContentNodes =
|
|
|
|
genHTML(*static_cast<clang::doc::TypedefInfo *>(I), CDCtx, InfoTitle);
|
|
|
|
break;
|
2019-07-10 19:03:25 +00:00
|
|
|
case InfoType::IT_default:
|
2019-08-28 02:56:03 +00:00
|
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
|
|
"unexpected info type");
|
2019-07-10 19:03:25 +00:00
|
|
|
}
|
|
|
|
|
2020-03-06 17:33:56 -08:00
|
|
|
HTMLFile F = genInfoFile(InfoTitle, I->getRelativeFilePath(""),
|
|
|
|
MainContentNodes, InfoIndex, CDCtx);
|
2024-06-27 13:46:35 -07:00
|
|
|
F.render(OS);
|
2019-07-10 19:03:25 +00:00
|
|
|
|
|
|
|
return llvm::Error::success();
|
|
|
|
}
|
|
|
|
|
2019-08-06 18:31:46 +00:00
|
|
|
static std::string getRefType(InfoType IT) {
|
|
|
|
switch (IT) {
|
|
|
|
case InfoType::IT_default:
|
|
|
|
return "default";
|
|
|
|
case InfoType::IT_namespace:
|
|
|
|
return "namespace";
|
|
|
|
case InfoType::IT_record:
|
|
|
|
return "record";
|
|
|
|
case InfoType::IT_function:
|
|
|
|
return "function";
|
|
|
|
case InfoType::IT_enum:
|
|
|
|
return "enum";
|
2022-10-14 14:28:43 -07:00
|
|
|
case InfoType::IT_typedef:
|
|
|
|
return "typedef";
|
2019-08-06 18:31:46 +00:00
|
|
|
}
|
|
|
|
llvm_unreachable("Unknown InfoType");
|
|
|
|
}
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
|
2019-08-06 18:31:46 +00:00
|
|
|
std::error_code OK;
|
|
|
|
std::error_code FileErr;
|
|
|
|
llvm::SmallString<128> FilePath;
|
|
|
|
llvm::sys::path::native(CDCtx.OutDirectory, FilePath);
|
|
|
|
llvm::sys::path::append(FilePath, "index_json.js");
|
2025-02-04 09:12:45 -05:00
|
|
|
llvm::raw_fd_ostream OS(FilePath, FileErr, llvm::sys::fs::OF_Text);
|
2019-08-06 18:31:46 +00:00
|
|
|
if (FileErr != OK) {
|
2019-08-28 02:56:03 +00:00
|
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
|
|
"error creating index file: " +
|
|
|
|
FileErr.message());
|
2019-07-25 21:27:50 +00:00
|
|
|
}
|
2024-07-25 03:33:02 -04:00
|
|
|
llvm::SmallString<128> RootPath(CDCtx.OutDirectory);
|
|
|
|
if (llvm::sys::path::is_relative(RootPath)) {
|
|
|
|
llvm::sys::fs::make_absolute(RootPath);
|
|
|
|
}
|
|
|
|
// Replace the escaped characters with a forward slash. It shouldn't matter
|
|
|
|
// when rendering the webpage in a web browser. This helps to prevent the
|
|
|
|
// JavaScript from escaping characters incorrectly, and introducing bad paths
|
|
|
|
// in the URLs.
|
|
|
|
std::string RootPathEscaped = RootPath.str().str();
|
|
|
|
std::replace(RootPathEscaped.begin(), RootPathEscaped.end(), '\\', '/');
|
|
|
|
OS << "var RootPath = \"" << RootPathEscaped << "\";\n";
|
|
|
|
|
2025-03-28 12:49:43 -07:00
|
|
|
llvm::SmallString<128> Base(CDCtx.Base);
|
|
|
|
std::string BaseEscaped = Base.str().str();
|
|
|
|
std::replace(BaseEscaped.begin(), BaseEscaped.end(), '\\', '/');
|
|
|
|
OS << "var Base = \"" << BaseEscaped << "\";\n";
|
|
|
|
|
2019-08-06 18:31:46 +00:00
|
|
|
CDCtx.Idx.sort();
|
|
|
|
llvm::json::OStream J(OS, 2);
|
2022-02-17 13:28:02 +00:00
|
|
|
std::function<void(Index)> IndexToJSON = [&](const Index &I) {
|
2019-08-06 18:31:46 +00:00
|
|
|
J.object([&] {
|
2024-07-23 18:55:25 -04:00
|
|
|
J.attribute("USR", toHex(llvm::toStringRef(I.USR)));
|
2019-08-06 18:31:46 +00:00
|
|
|
J.attribute("Name", I.Name);
|
|
|
|
J.attribute("RefType", getRefType(I.RefType));
|
2020-03-06 17:33:56 -08:00
|
|
|
J.attribute("Path", I.getRelativeFilePath(""));
|
2019-08-06 18:31:46 +00:00
|
|
|
J.attributeArray("Children", [&] {
|
|
|
|
for (const Index &C : I.Children)
|
|
|
|
IndexToJSON(C);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
2024-06-30 11:52:36 -04:00
|
|
|
OS << "async function LoadIndex() {\nreturn";
|
2019-08-06 18:31:46 +00:00
|
|
|
IndexToJSON(CDCtx.Idx);
|
2024-06-30 11:52:36 -04:00
|
|
|
OS << ";\n}";
|
2019-08-26 16:39:48 +00:00
|
|
|
return llvm::Error::success();
|
2019-08-06 18:31:46 +00:00
|
|
|
}
|
|
|
|
|
2019-08-16 18:38:11 +00:00
|
|
|
// Generates a main HTML node that has the main content of the file that shows
|
|
|
|
// only the general index
|
|
|
|
// It contains the general index with links to all the generated files
|
|
|
|
static std::unique_ptr<TagNode> genIndexFileMainNode() {
|
|
|
|
auto MainNode = std::make_unique<TagNode>(HTMLTag::TAG_MAIN);
|
|
|
|
|
|
|
|
auto LeftSidebarNode = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
|
|
|
LeftSidebarNode->Attributes.emplace_back("id", "sidebar-left");
|
|
|
|
LeftSidebarNode->Attributes.emplace_back("path", "");
|
|
|
|
LeftSidebarNode->Attributes.emplace_back(
|
|
|
|
"class", "col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left");
|
|
|
|
LeftSidebarNode->Attributes.emplace_back("style", "flex: 0 100%;");
|
|
|
|
|
|
|
|
MainNode->Children.emplace_back(std::move(LeftSidebarNode));
|
|
|
|
|
|
|
|
return MainNode;
|
|
|
|
}
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
static llvm::Error genIndex(const ClangDocContext &CDCtx) {
|
2019-08-09 20:52:28 +00:00
|
|
|
std::error_code FileErr, OK;
|
|
|
|
llvm::SmallString<128> IndexPath;
|
|
|
|
llvm::sys::path::native(CDCtx.OutDirectory, IndexPath);
|
|
|
|
llvm::sys::path::append(IndexPath, "index.html");
|
2025-02-04 09:12:45 -05:00
|
|
|
llvm::raw_fd_ostream IndexOS(IndexPath, FileErr, llvm::sys::fs::OF_Text);
|
2019-08-09 20:52:28 +00:00
|
|
|
if (FileErr != OK) {
|
2019-08-28 02:56:03 +00:00
|
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
|
|
"error creating main index: " +
|
|
|
|
FileErr.message());
|
2019-08-09 20:52:28 +00:00
|
|
|
}
|
2019-08-16 18:38:11 +00:00
|
|
|
|
2019-08-09 20:52:28 +00:00
|
|
|
HTMLFile F;
|
2019-08-16 18:38:11 +00:00
|
|
|
|
|
|
|
std::vector<std::unique_ptr<TagNode>> HeadNodes =
|
|
|
|
genFileHeadNodes("Index", "", CDCtx);
|
|
|
|
std::unique_ptr<TagNode> HeaderNode = genFileHeaderNode(CDCtx.ProjectName);
|
|
|
|
std::unique_ptr<TagNode> MainNode = genIndexFileMainNode();
|
|
|
|
std::unique_ptr<TagNode> FooterNode = genFileFooterNode();
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
appendVector(std::move(HeadNodes), F.Children);
|
2019-08-16 18:38:11 +00:00
|
|
|
F.Children.emplace_back(std::move(HeaderNode));
|
|
|
|
F.Children.emplace_back(std::move(MainNode));
|
|
|
|
F.Children.emplace_back(std::move(FooterNode));
|
|
|
|
|
2024-06-27 13:46:35 -07:00
|
|
|
F.render(IndexOS);
|
2019-08-16 18:38:11 +00:00
|
|
|
|
2019-08-26 16:39:48 +00:00
|
|
|
return llvm::Error::success();
|
2019-08-09 20:52:28 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 16:39:48 +00:00
|
|
|
llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
|
2024-06-27 13:46:35 -07:00
|
|
|
auto Err = serializeIndex(CDCtx);
|
2019-08-26 16:39:48 +00:00
|
|
|
if (Err)
|
|
|
|
return Err;
|
2024-06-27 13:46:35 -07:00
|
|
|
Err = genIndex(CDCtx);
|
2019-08-26 16:39:48 +00:00
|
|
|
if (Err)
|
|
|
|
return Err;
|
|
|
|
|
|
|
|
for (const auto &FilePath : CDCtx.UserStylesheets) {
|
2024-06-27 13:46:35 -07:00
|
|
|
Err = copyFile(FilePath, CDCtx.OutDirectory);
|
2019-08-26 16:39:48 +00:00
|
|
|
if (Err)
|
|
|
|
return Err;
|
|
|
|
}
|
2024-06-27 16:22:01 -04:00
|
|
|
for (const auto &FilePath : CDCtx.JsScripts) {
|
2024-06-27 13:46:35 -07:00
|
|
|
Err = copyFile(FilePath, CDCtx.OutDirectory);
|
2019-08-26 16:39:48 +00:00
|
|
|
if (Err)
|
|
|
|
return Err;
|
|
|
|
}
|
|
|
|
return llvm::Error::success();
|
2019-07-25 21:27:50 +00:00
|
|
|
}
|
|
|
|
|
2019-07-10 19:03:25 +00:00
|
|
|
static GeneratorRegistry::Add<HTMLGenerator> HTML(HTMLGenerator::Format,
|
|
|
|
"Generator for HTML output.");
|
|
|
|
|
|
|
|
// This anchor is used to force the linker to link in the generated object
|
|
|
|
// file and thus register the generator.
|
|
|
|
volatile int HTMLGeneratorAnchorSource = 0;
|
|
|
|
|
|
|
|
} // namespace doc
|
|
|
|
} // namespace clang
|