https://github.com/ilovepi updated 
https://github.com/llvm/llvm-project/pull/138064

>From bda0fff46cb19612aeadb5027ef92d8b42d7b783 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulki...@google.com>
Date: Wed, 30 Apr 2025 08:13:46 -0700
Subject: [PATCH] [clang-doc] Implement setupTemplateValue for
 HTMLMustacheGenerator

This patch implements the business logic for setupTemplateValue, which
was split from #133161. The implementation configures the relative path
relationships between the various HTML components, and prepares them
prior to their use in the generator.

Co-authored-by: Peter Chou <peter.c...@mail.utoronto.ca>
---
 .../clang-doc/HTMLMustacheGenerator.cpp       |  27 +-
 .../clang-doc/HTMLMustacheGeneratorTest.cpp   | 416 +++++++++++++++++-
 2 files changed, 434 insertions(+), 9 deletions(-)

diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp 
b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 220102d3bdf66..0c64c20db96fb 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -392,7 +392,7 @@ static json::Value extractValue(const RecordInfo &I,
 
   maybeInsertLocation(I.DefLoc, CDCtx, RecordValue);
 
-  StringRef BasePath = I.getRelativeFilePath("");
+  SmallString<64> BasePath = I.getRelativeFilePath("");
   extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
   json::Value PublicMembers = Array();
   json::Array &PubMemberRef = *PublicMembers.getAsArray();
@@ -426,8 +426,28 @@ static json::Value extractValue(const RecordInfo &I,
 
 static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
                                 Info *I) {
-  return createStringError(inconvertibleErrorCode(),
-                           "setupTemplateValue is unimplemented");
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  json::Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  sys::path::native(RelativePath, sys::path::Style::posix);
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    sys::path::append(StylesheetPath, sys::path::Style::posix,
+                      sys::path::filename(FilePath));
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
+  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+
+  json::Value ScriptArr = Array();
+  for (auto Script : CDCtx.JsScripts) {
+    SmallString<128> JsPath = RelativePath;
+    sys::path::append(JsPath, sys::path::Style::posix,
+                      sys::path::filename(Script));
+    ScriptArr.getAsArray()->emplace_back(JsPath);
+  }
+  V.getAsObject()->insert({"Scripts", ScriptArr});
+  return Error::success();
 }
 
 Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
@@ -438,6 +458,7 @@ Error MustacheHTMLGenerator::generateDocForInfo(Info *I, 
raw_ostream &OS,
         extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
     if (auto Err = setupTemplateValue(CDCtx, V, I))
       return Err;
+    assert(NamespaceTemplate && "NamespaceTemplate is nullptr.");
     NamespaceTemplate->render(V, OS);
     break;
   }
diff --git 
a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp 
b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
index 4d1af9d387092..681964969ec01 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
@@ -20,10 +20,10 @@
 
 using namespace llvm;
 using namespace testing;
+using namespace clang;
 using namespace clang::doc;
 
-static const std::string ClangDocVersion =
-    clang::getClangToolFullVersion("clang-doc");
+static const std::string ClangDocVersion = 
getClangToolFullVersion("clang-doc");
 
 static std::unique_ptr<Generator> getHTMLMustacheGenerator() {
   auto G = findGeneratorByName("mustache");
@@ -114,12 +114,416 @@ TEST(HTMLMustacheGeneratorTest, generateDocsForInfo) {
   I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
                                   "Namespace::ChildStruct", "Namespace");
   I.Children.Functions.emplace_back();
-  I.Children.Functions.back().Access = clang::AccessSpecifier::AS_none;
+  I.Children.Functions.back().Access = AccessSpecifier::AS_none;
   I.Children.Functions.back().Name = "OneFunction";
   I.Children.Enums.emplace_back();
 
-  EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Failed());
+  unittest::TempDir RootTestDirectory("generateDocForInfoTest",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Succeeded());
+
+  std::string Expected = R"raw(<!DOCTYPE html>
+<html lang="en-US">
+    <head>
+        <meta charset="utf-8"/>
+        <title>namespace Namespace</title>
+        <link rel="stylesheet" type="text/css" 
href="../clang-doc-mustache.css"/>
+        <link rel="stylesheet" type="text/css" href="../"/>
+        <script src="../mustache-index.js"></script>
+        <script src="../"></script>
+        <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css";>
+        <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js";></script>
+        <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js";></script>
+    </head>
+    <body>
+        <nav class="navbar">
+            Navbar
+        </nav>
+        <main>
+            <div class="container">
+                <div class="sidebar">
+                        Lorem ipsum dolor sit amet, consectetur adipiscing 
elit, 
+                        sed do eiusmod tempor incididunt ut labore et dolore 
magna 
+                        aliqua. Ut enim ad minim veniam, quis nostrud 
exercitation ullamco 
+                        laboris nisi ut aliquip ex ea commodo consequat. 
+                        Duis aute irure dolor in reprehenderit in voluptate 
velit esse 
+                        cillum dolore eu fugiat nulla pariatur. Excepteur sint 
occaecat 
+                        cupidatat non proident, sunt in culpa qui officia 
deserunt mollit 
+                        anim id est laborum
+                </div>
+                <div class="resizer" id="resizer"></div>
+                <div class="content">
+                    Content
+                </div>
+            </div>
+        </main>
+    </body>
+</html>
+)raw";
+  EXPECT_EQ(Actual.str(), Expected);
+}
+
+TEST(HTMLMustacheGeneratorTest, emitRecordHTML) {
+  auto G = getHTMLMustacheGenerator();
+  assert(G && "Could not find HTMLMustacheGenerator");
+  ClangDocContext CDCtx = getClangDocContext();
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+
+  unittest::TempDir RootTestDirectory("emitRecordHTML",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  CDCtx.RepositoryUrl = "http://www.repository.com";;
+
+  RecordInfo I;
+  I.Name = "r";
+  I.Path = "X/Y/Z";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, 10, "dir/test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
+
+  SmallString<16> PathTo;
+  llvm::sys::path::native("path/to", PathTo);
+  I.Members.emplace_back(clang::doc::TypeInfo("int"), "X",
+                         AccessSpecifier::AS_private);
+  I.TagType = TagTypeKind::Class;
+  I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "F", PathTo);
+  I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
+
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+                                  "X::Y::Z::r::ChildStruct", "X/Y/Z/r");
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
+
+  EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Succeeded());
+
+  std::string Expected = R"raw(<!DOCTYPE html>
+<html lang="en-US">
+<head>
+    <meta charset="utf-8"/>
+    <title>r</title>
+        <link rel="stylesheet" type="text/css" 
href="../../../clang-doc-mustache.css"/>
+        <link rel="stylesheet" type="text/css" href="../../../"/>
+        <script src="../../../mustache-index.js"></script>
+        <script src="../../../"></script>
+    <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css";>
+    <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js";></script>
+    <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js";></script>
+</head>
+<body>
+<nav class="navbar">
+    <div class="navbar__container">
+            <div class="navbar__logo">
+                test-project
+            </div>
+        <div class="navbar__menu">
+            <ul class="navbar__links">
+                <li class="navbar__item">
+                    <a href="/" class="navbar__link">Namespace</a>
+                </li>
+                <li class="navbar__item">
+                    <a href="/" class="navbar__link">Class</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</nav>
+<main>
+    <div class="container">
+        <div class="sidebar">
+            <h2>class r</h2>
+            <ul>
+                    <li class="sidebar-section">
+                        <a class="sidebar-item" 
href="#PublicMethods">Protected Members</a>
+                    </li>
+                    <ul>
+                            <li class="sidebar-item-container">
+                                <a class="sidebar-item" href="#X">X</a>
+                            </li>
+                    </ul>
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#PublicMethods">Public 
Method</a>
+                </li>
+                <ul>
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" 
href="#0000000000000000000000000000000000000000">OneFunction</a>
+                    </li>
+                </ul>
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#Enums">Enums</a>
+                </li>
+                <ul>
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" 
href="#0000000000000000000000000000000000000000">enum OneEnum</a>
+                    </li>
+                </ul>
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#Classes">Inner Classes</a>
+                </li>
+                <ul>
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" 
href="#0000000000000000000000000000000000000000">ChildStruct</a>
+                    </li>
+                </ul>
+            </ul>
+        </div>
+        <div class="resizer" id="resizer"></div>
+        <div class="content">
+            <section class="hero section-container">
+                <div class="hero__title">
+                    <h1 class="hero__title-large">class r</h1>
+                </div>
+            </section>
+            <section id="ProtectedMembers" class="section-container">
+                <h2>Protected Members</h2>
+                <div>
+                    <div id="X" class="delimiter-container">
+                        <pre>
+<code class="language-cpp code-clang-doc" >int X</code>
+                        </pre>
+                    </div>
+                </div>
+            </section>
+            <section id="PublicMethods" class="section-container">
+                <h2>Public Methods</h2>
+                <div>
+<div class="delimiter-container">
+    <div id="0000000000000000000000000000000000000000">
+        <pre>
+            <code class="language-cpp code-clang-doc">
+ OneFunction ()
+            </code>
+        </pre>
+    </div>
+</div>
+                </div>
+            </section>
+            <section id="Enums" class="section-container">
+                <h2>Enumerations</h2>
+                <div>
+<div id="0000000000000000000000000000000000000000" class="delimiter-container">
+    <div>
+        <pre>
+            <code class="language-cpp code-clang-doc">
+enum OneEnum
+            </code>
+        </pre>
+    </div>
+    <table class="table-wrapper">
+        <tbody>
+        <tr>
+            <th>Name</th>
+            <th>Value</th>
+        </tr>
+        </tbody>
+    </table>
+</div>
+                </div>
+            </section>
+            <section id="Classes" class="section-container">
+                <h2>Inner Classes</h2>
+                <ul class="class-container">
+                    <li id="0000000000000000000000000000000000000000" 
style="max-height: 40px;">
+<a href="../../../X/Y/Z/r/ChildStruct.html"><pre><code class="language-cpp 
code-clang-doc" >class ChildStruct</code></pre></a>
+                    </li>
+                </ul>
+            </section>
+        </div>
+    </div>
+</main>
+</body>
+</html>
+)raw";
+  EXPECT_EQ(Actual.str(), Expected);
+}
+
+TEST(HTMLGeneratorTest, emitFunctionHTML) {
+  auto G = getHTMLMustacheGenerator();
+  assert(G && "Could not find HTMLMustacheGenerator");
+  ClangDocContext CDCtx = getClangDocContext();
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+
+  unittest::TempDir RootTestDirectory("emitRecordHTML",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  CDCtx.RepositoryUrl = "http://www.repository.com";;
+
+  FunctionInfo I;
+  I.Name = "f";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, 10, "dir/test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
+
+  I.Access = AccessSpecifier::AS_none;
+
+  SmallString<16> PathTo;
+  llvm::sys::path::native("path/to", PathTo);
+  I.ReturnType = doc::TypeInfo(
+      Reference(EmptySID, "float", InfoType::IT_default, "float", PathTo));
+  I.Params.emplace_back(doc::TypeInfo("int", PathTo), "P");
+  I.IsMethod = true;
+  I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
+
+  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
+  assert(!Err);
+  std::string Expected = R"raw(IT_Function
+)raw";
+
+  // FIXME: Functions are not handled yet.
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLMustacheGeneratorTest, emitEnumHTML) {
+  auto G = getHTMLMustacheGenerator();
+  assert(G && "Could not find HTMLMustacheGenerator");
+  ClangDocContext CDCtx = getClangDocContext();
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+
+  unittest::TempDir RootTestDirectory("emitEnumHTML",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  CDCtx.RepositoryUrl = "http://www.repository.com";;
+
+  EnumInfo I;
+  I.Name = "e";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, 10, "test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
+
+  I.Members.emplace_back("X");
+  I.Scoped = true;
+
+  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
+  assert(!Err);
+
+  std::string Expected = R"raw(IT_enum
+)raw";
+
+  // FIXME: Enums are not handled yet.
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLMustacheGeneratorTest, emitCommentHTML) {
+  auto G = getHTMLMustacheGenerator();
+  assert(G && "Could not find HTMLMustacheGenerator");
+  ClangDocContext CDCtx = getClangDocContext();
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+
+  unittest::TempDir RootTestDirectory("emitCommentHTML",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  CDCtx.RepositoryUrl = "http://www.repository.com";;
+
+  FunctionInfo I;
+  I.Name = "f";
+  I.DefLoc = Location(10, 10, "test.cpp", true);
+  I.ReturnType = doc::TypeInfo("void");
+  I.Params.emplace_back(doc::TypeInfo("int"), "I");
+  I.Params.emplace_back(doc::TypeInfo("int"), "J");
+  I.Access = AccessSpecifier::AS_none;
+
+  CommentInfo Top;
+  Top.Kind = "FullComment";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *BlankLine = Top.Children.back().get();
+  BlankLine->Kind = "ParagraphComment";
+  BlankLine->Children.emplace_back(std::make_unique<CommentInfo>());
+  BlankLine->Children.back()->Kind = "TextComment";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *Brief = Top.Children.back().get();
+  Brief->Kind = "ParagraphComment";
+  Brief->Children.emplace_back(std::make_unique<CommentInfo>());
+  Brief->Children.back()->Kind = "TextComment";
+  Brief->Children.back()->Name = "ParagraphComment";
+  Brief->Children.back()->Text = " Brief description.";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *Extended = Top.Children.back().get();
+  Extended->Kind = "ParagraphComment";
+  Extended->Children.emplace_back(std::make_unique<CommentInfo>());
+  Extended->Children.back()->Kind = "TextComment";
+  Extended->Children.back()->Text = " Extended description that";
+  Extended->Children.emplace_back(std::make_unique<CommentInfo>());
+  Extended->Children.back()->Kind = "TextComment";
+  Extended->Children.back()->Text = " continues onto the next line.";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *Entities = Top.Children.back().get();
+  Entities->Kind = "ParagraphComment";
+  Entities->Children.emplace_back(std::make_unique<CommentInfo>());
+  Entities->Children.back()->Kind = "TextComment";
+  Entities->Children.back()->Name = "ParagraphComment";
+  Entities->Children.back()->Text =
+      " Comment with html entities: &, <, >, \", \'.";
+
+  I.Description.emplace_back(std::move(Top));
+
+  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
+  assert(!Err);
+  std::string Expected = R"raw(IT_Function
+)raw";
 
-  std::string Expected = R"raw()raw";
-  EXPECT_THAT(Actual.str(), Eq(Expected));
+  // FIXME: Functions are not handled yet.
+  EXPECT_EQ(Expected, Actual.str());
 }

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to