Skip to content

[clang-doc] Add HTMLMustacheGenerator methods #138061

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,113 @@ class MustacheTemplateFile : public Template {
MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
};

static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;

static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;

static Error setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
return Error::success();
}

Error MustacheHTMLGenerator::generateDocs(
StringRef RootDir, StringMap<std::unique_ptr<doc::Info>> Infos,
const clang::doc::ClangDocContext &CDCtx) {
if (auto Err = setupTemplateFiles(CDCtx))
return Err;
// Track which directories we already tried to create.
StringSet<> CreatedDirs;
// Collect all output by file name and create the necessary directories.
StringMap<std::vector<doc::Info *>> FileToInfos;
for (const auto &Group : Infos) {
doc::Info *Info = Group.getValue().get();

SmallString<128> Path;
sys::path::native(RootDir, Path);
sys::path::append(Path, Info->getRelativeFilePath(""));
if (!CreatedDirs.contains(Path)) {
if (std::error_code EC = sys::fs::create_directories(Path))
return createStringError(EC, "failed to create directory '%s'.",
Path.c_str());
CreatedDirs.insert(Path);
}

sys::path::append(Path, Info->getFileBaseName() + ".html");
FileToInfos[Path].push_back(Info);
}

for (const auto &Group : FileToInfos) {
std::error_code FileErr;
raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_None);
if (FileErr)
return createFileOpenError(Group.getKey(), FileErr);

for (const auto &Info : Group.getValue())
if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
return Err;
}
return Error::success();
}

static json::Value extractValue(const NamespaceInfo &I,
const ClangDocContext &CDCtx) {
Object NamespaceValue = Object();
return NamespaceValue;
}

static json::Value extractValue(const RecordInfo &I,
const ClangDocContext &CDCtx) {
Object RecordValue = Object();
return RecordValue;
}

static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
Info *I) {
return createStringError(inconvertibleErrorCode(),
"setupTemplateValue is unimplemented");
}

Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
const ClangDocContext &CDCtx) {
switch (I->IT) {
case InfoType::IT_namespace: {
json::Value V =
extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
if (auto Err = setupTemplateValue(CDCtx, V, I))
return Err;
NamespaceTemplate->render(V, OS);
break;
}
case InfoType::IT_record: {
json::Value V =
extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
if (auto Err = setupTemplateValue(CDCtx, V, I))
return Err;
// Serialize the JSON value to the output stream in a readable format.
RecordTemplate->render(V, OS);
break;
}
case InfoType::IT_enum:
OS << "IT_enum\n";
break;
case InfoType::IT_function:
OS << "IT_Function\n";
break;
case InfoType::IT_typedef:
OS << "IT_typedef\n";
Comment on lines +158 to +165
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PeterChou1 I don't see the implementation for these up the stack. Did you ever get these working? Your example webpage had things for Enums and Functions IIRC. Do yo have time to help w/ this and/or reviews?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PeterChou1 It's fine if you're unable to help, but if you could confirm any of these details I'd appreciate it.

break;
case InfoType::IT_default:
return createStringError(inconvertibleErrorCode(), "unexpected InfoType");
}
return Error::success();
}

Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
for (const auto &FilePath : CDCtx.UserStylesheets)
if (Error Err = copyFile(FilePath, CDCtx.OutDirectory))
return Err;
for (const auto &FilePath : CDCtx.JsScripts)
if (Error Err = copyFile(FilePath, CDCtx.OutDirectory))
return Err;
return Error::success();
}

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/unittests/clang-doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ clang_target_link_libraries(ClangDocTests
clangTooling
clangToolingCore
)

target_link_libraries(ClangDocTests
PRIVATE
clangDoc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#include "Generators.h"
#include "Representation.h"
#include "clang/Basic/Version.h"
#include "llvm/Support/Path.h"
#include "llvm/Testing/Support/Error.h"
#include "llvm/Testing/Support/SupportHelpers.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

Expand Down Expand Up @@ -40,13 +42,43 @@ getClangDocContext(std::vector<std::string> UserStylesheets = {},
return CDCtx;
}

static void verifyFileContents(const Twine &Path, StringRef Contents) {
auto Buffer = MemoryBuffer::getFile(Path);
ASSERT_TRUE((bool)Buffer);
StringRef Data = Buffer.get()->getBuffer();
ASSERT_EQ(Data, Contents);
}

TEST(HTMLMustacheGeneratorTest, createResources) {
auto G = getHTMLMustacheGenerator();
ASSERT_THAT(G, NotNull()) << "Could not find HTMLMustacheGenerator";
ClangDocContext CDCtx = getClangDocContext();
EXPECT_THAT_ERROR(G->createResources(CDCtx), Failed())
<< "Empty UserStylesheets or JsScripts should fail!";

unittest::TempDir RootTestDirectory("createResourcesTest", /*Unique=*/true);
CDCtx.OutDirectory = RootTestDirectory.path();

unittest::TempFile CSS("clang-doc-mustache", "css", "CSS");
unittest::TempFile JS("mustache", "js", "JavaScript");

CDCtx.UserStylesheets[0] = CSS.path();
CDCtx.JsScripts[0] = JS.path();

EXPECT_THAT_ERROR(G->createResources(CDCtx), Succeeded())
<< "Failed to create resources.";
<< "Failed to create resources with valid UserStylesheets and JsScripts";
{
SmallString<256> PathBuf;
llvm::sys::path::append(PathBuf, RootTestDirectory.path(),
"clang-doc-mustache.css");
verifyFileContents(PathBuf, "CSS");
}

{
SmallString<256> PathBuf;
llvm::sys::path::append(PathBuf, RootTestDirectory.path(), "mustache.js");
verifyFileContents(PathBuf, "JavaScript");
}
}

TEST(HTMLMustacheGeneratorTest, generateDocs) {
Expand Down Expand Up @@ -79,8 +111,7 @@ TEST(HTMLMustacheGeneratorTest, generateDocsForInfo) {
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();

EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Succeeded())
<< "Failed to generate docs.";
EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Failed());

std::string Expected = R"raw()raw";
EXPECT_THAT(Actual.str(), Eq(Expected));
Expand Down
Loading