[clang-tools-extra] [llvm] [WIP] add a mustache backend (PR #108653)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 11 19:58:45 PST 2024
https://github.com/PeterChou1 updated https://github.com/llvm/llvm-project/pull/108653
>From ac1c7b5cbf3024bf8cd4021174a47b914d7f7dea Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 30 Jul 2024 23:58:27 -0400
Subject: [PATCH 01/62] [clang-doc] add suport for clang-doc enum generation
---
clang-tools-extra/clang-doc/BitcodeReader.cpp | 4 +
clang-tools-extra/clang-doc/BitcodeWriter.cpp | 2 +
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 88 ++++++++++++--
.../clang-doc/Representation.cpp | 2 +
clang-tools-extra/clang-doc/Representation.h | 4 +
clang-tools-extra/clang-doc/Serialize.cpp | 14 ++-
clang-tools-extra/test/clang-doc/enum.cpp | 113 +++++++++++++-----
7 files changed, 181 insertions(+), 46 deletions(-)
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index bfb04e7407b380..1f2fb0a8b2b855 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -415,6 +415,10 @@ template <> llvm::Expected<CommentInfo *> getCommentInfo(TypedefInfo *I) {
return &I->Description.emplace_back();
}
+template <> llvm::Expected<CommentInfo *> getCommentInfo(EnumValueInfo *I) {
+ return &I->Description.emplace_back();
+}
+
template <> llvm::Expected<CommentInfo *> getCommentInfo(CommentInfo *I) {
I->Children.emplace_back(std::make_unique<CommentInfo>());
return I->Children.back().get();
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 7e5a11783d303a..06f30f76e33d8c 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -536,6 +536,8 @@ void ClangDocBitcodeWriter::emitBlock(const EnumValueInfo &I) {
emitRecord(I.Name, ENUM_VALUE_NAME);
emitRecord(I.Value, ENUM_VALUE_VALUE);
emitRecord(I.ValueExpr, ENUM_VALUE_EXPR);
+ for (const auto &CI : I.Description)
+ emitBlock(CI);
}
void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index aef22453035c30..a37192d6ceb9b0 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -48,6 +48,12 @@ class HTMLTag {
TAG_SPAN,
TAG_TITLE,
TAG_UL,
+ TAG_TABLE,
+ TAG_THEAD,
+ TAG_TBODY,
+ TAG_TR,
+ TAG_TD,
+ TAG_TH
};
HTMLTag() = default;
@@ -133,6 +139,12 @@ bool HTMLTag::isSelfClosing() const {
case HTMLTag::TAG_SPAN:
case HTMLTag::TAG_TITLE:
case HTMLTag::TAG_UL:
+ case HTMLTag::TAG_TABLE:
+ case HTMLTag::TAG_THEAD:
+ case HTMLTag::TAG_TBODY:
+ case HTMLTag::TAG_TR:
+ case HTMLTag::TAG_TD:
+ case HTMLTag::TAG_TH:
return false;
}
llvm_unreachable("Unhandled HTMLTag::TagType");
@@ -174,6 +186,18 @@ StringRef HTMLTag::toString() const {
return "title";
case HTMLTag::TAG_UL:
return "ul";
+ 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";
}
llvm_unreachable("Unhandled HTMLTag::TagType");
}
@@ -352,6 +376,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
static std::vector<std::unique_ptr<TagNode>>
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
StringRef ParentInfoDir);
+static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C);
static std::vector<std::unique_ptr<TagNode>>
genEnumsBlock(const std::vector<EnumInfo> &Enums,
@@ -372,14 +397,33 @@ genEnumsBlock(const std::vector<EnumInfo> &Enums,
}
static std::unique_ptr<TagNode>
-genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
+genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members,
+ bool HasComments) {
if (Members.empty())
return nullptr;
- auto List = std::make_unique<TagNode>(HTMLTag::TAG_UL);
- for (const auto &M : Members)
- List->Children.emplace_back(
- std::make_unique<TagNode>(HTMLTag::TAG_LI, M.Name));
+ 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 (HasComments) {
+ 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));
+ }
return List;
}
@@ -653,15 +697,35 @@ static std::vector<std::unique_ptr<TagNode>>
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
std::string EnumType = I.Scoped ? "enum class " : "enum ";
+ // Determine if enum members have comments attached
+ bool HasComments = false;
+ for (const auto &M : I.Members) {
+ if (!M.Description.empty()) {
+ HasComments = true;
+ break;
+ }
+ }
+ 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));
+
+ std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members, HasComments);
- Out.emplace_back(
- std::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name));
- Out.back()->Attributes.emplace_back("id",
- llvm::toHex(llvm::toStringRef(I.USR)));
-
- std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
if (Node)
- Out.emplace_back(std::move(Node));
+ Table->Children.emplace_back(std::move(Node));
+
+ Out.emplace_back(std::move(Table));
if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index d08afbb9621890..028dffc21793ae 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -266,6 +266,8 @@ void EnumInfo::merge(EnumInfo &&Other) {
Scoped = Other.Scoped;
if (Members.empty())
Members = std::move(Other.Members);
+ if (Other.HasComments || HasComments)
+ HasComments = true;
SymbolInfo::merge(std::move(Other));
}
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index d70c279f7a2bdc..db3aff2d60a1b5 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -431,6 +431,8 @@ struct EnumValueInfo {
// Stores the user-supplied initialization expression for this enumeration
// constant. This will be empty for implicit enumeration values.
SmallString<16> ValueExpr;
+
+ std::vector<CommentInfo> Description; // Comment description of this field.
};
// TODO: Expand to allow for documenting templating.
@@ -443,6 +445,8 @@ struct EnumInfo : public SymbolInfo {
// Indicates whether this enum is scoped (e.g. enum class).
bool Scoped = false;
+ // Indicates whether or not enum members have comments attached
+ bool HasComments = false;
// Set to nonempty to the type when this is an explicitly typed enum. For
// enum Foo : short { ... };
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 3b074d849e8a9c..78b7041368d6df 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -394,10 +394,20 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
std::string ValueExpr;
if (const Expr *InitExpr = E->getInitExpr())
ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
-
SmallString<16> ValueStr;
E->getInitVal().toString(ValueStr);
- I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
+ I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
+ ASTContext &Context = E->getASTContext();
+ RawComment *Comment = E->getASTContext().getRawCommentForDeclNoCache(E);
+ if (Comment) {
+ CommentInfo CInfo;
+ Comment->setAttached();
+ if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
+ EnumValueInfo &Member = I.Members.back();
+ Member.Description.emplace_back();
+ parseFullComment(Fc, Member.Description.back());
+ }
+ }
}
}
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index e559940a31de69..fd7bbcb53f2d2b 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -21,9 +21,9 @@
enum Color {
// MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
- Red, ///< Red
- Green, ///< Green
- Blue ///< Blue
+ Red, ///< Comment 1
+ Green, ///< Comment 2
+ Blue ///< Comment 3
};
// MD-INDEX: ## Enums
@@ -34,11 +34,16 @@ enum Color {
// MD-INDEX: | Blue |
// MD-INDEX: **brief** For specifying RGB colors
-// HTML-INDEX: <h2 id="Enums">Enums</h2>
-// HTML-INDEX: <h3 id="{{([0-9A-F]{40})}}">enum Color</h3>
-// HTML-INDEX: <li>Red</li>
-// HTML-INDEX: <li>Green</li>
-// HTML-INDEX: <li>Blue</li>
+// HTML-INDEX: <th colspan="3">enum Color</th>
+// HTML-INDEX: <td>Red</td>
+// HTML-INDEX: <td>0</td>
+// HTML-INDEX: <p> Comment 1</p>
+// HTML-INDEX: <td>Green</td>
+// HTML-INDEX: <td>1</td>
+// HTML-INDEX: <p> Comment 2</p>
+// HTML-INDEX: <td>Blue</td>
+// HTML-INDEX: <td>2</td>
+// HTML-INDEX: <p> Comment 3</p>
/**
* @brief Shape Types
@@ -46,11 +51,12 @@ enum Color {
enum class Shapes {
// MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
- /// Circle
+
+ /// Comment 1
Circle,
- /// Rectangle
+ /// Comment 2
Rectangle,
- /// Triangle
+ /// Comment 3
Triangle
};
// MD-INDEX: | enum class Shapes |
@@ -60,10 +66,17 @@ enum class Shapes {
// MD-INDEX: | Triangle |
// MD-INDEX: **brief** Shape Types
-// HTML-INDEX: <h3 id="{{([0-9A-F]{40})}}">enum class Shapes</h3>
-// HTML-INDEX: <li>Circle</li>
-// HTML-INDEX: <li>Rectangle</li>
-// HTML-INDEX: <li>Triangle</li>
+// HTML-INDEX: <th colspan="3">enum class Shapes</th>
+// HTML-INDEX: <td>Circle</td>
+// HTML-INDEX: <td>0</td>
+// HTML-INDEX: <p> Comment 1</p>
+// HTML-INDEX: <td>Rectangle</td>
+// HTML-INDEX: <td>1</td>
+// HTML-INDEX: <p> Comment 2</p>
+// HTML-INDEX: <td>Triangle</td>
+// HTML-INDEX: <td>2</td>
+// HTML-INDEX: <p> Comment 3</p>
+
class Animals {
@@ -76,18 +89,25 @@ class Animals {
enum AnimalType {
// MD-ANIMAL-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// HTML-ANIMAL-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
- Dog, /// Man's best friend
- Cat, /// Man's other best friend
- Iguana /// A lizard
+ Dog, ///< Man's best friend
+ Cat, ///< Man's other best friend
+ Iguana ///< A lizard
};
};
// HTML-ANIMAL: <h1>class Animals</h1>
// HTML-ANIMAL: <h2 id="Enums">Enums</h2>
-// HTML-ANIMAL: <h3 id="{{([0-9A-F]{40})}}">enum AnimalType</h3>
-// HTML-ANIMAL: <li>Dog</li>
-// HTML-ANIMAL: <li>Cat</li>
-// HTML-ANIMAL: <li>Iguana</li>
+// HTML-ANIMAL: <th colspan="3">enum AnimalType</th>
+// HTML-ANIMAL: <td>Dog</td>
+// HTML-ANIMAL: <td>0</td>
+// HTML-ANIMAL: <p> Man's best friend</p>
+// HTML-ANIMAL: <td>Cat</td>
+// HTML-ANIMAL: <td>1</td>
+// HTML-ANIMAL: <p> Man's other best friend</p>
+// HTML-ANIMAL: <td>Iguana</td>
+// HTML-ANIMAL: <td>2</td>
+// HTML-ANIMAL: <p> A lizard</p>
+
// MD-ANIMAL: # class Animals
// MD-ANIMAL: ## Enums
@@ -106,10 +126,11 @@ namespace Vehicles {
enum Car {
// MD-VEHICLES-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// HTML-VEHICLES-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
- Sedan, /// Sedan
- SUV, /// SUV
- Pickup, /// Pickup
- Hatchback /// Hatchback
+
+ Sedan, ///< Comment 1
+ SUV, ///< Comment 2
+ Pickup, ///< Comment 3
+ Hatchback ///< Comment 4
};
}
@@ -124,9 +145,37 @@ namespace Vehicles {
// MD-VEHICLES: **brief** specify type of car
// HTML-VEHICLES: <h1>namespace Vehicles</h1>
-// HTML-VEHICLES: <h2 id="Enums">Enums</h2>
-// HTML-VEHICLES: <h3 id="{{([0-9A-F]{40})}}">enum Car</h3>
-// HTML-VEHICLES: <li>Sedan</li>
-// HTML-VEHICLES: <li>SUV</li>
-// HTML-VEHICLES: <li>Pickup</li>
-// HTML-VEHICLES: <li>Hatchback</li>
\ No newline at end of file
+// HTML-VEHICLES: <th colspan="3">enum Car</th>
+// HTML-VEHICLES: <td>Sedan</td>
+// HTML-VEHICLES: <td>0</td>
+// HTML-VEHICLES: <p> Comment 1</p>
+// HTML-VEHICLES: <td>SUV</td>
+// HTML-VEHICLES: <td>1</td>
+// HTML-VEHICLES: <p> Comment 2</p>
+// HTML-VEHICLES: <td>Pickup</td>
+// HTML-VEHICLES: <td>2</td>
+// HTML-VEHICLES: <p> Comment 3</p>
+// HTML-VEHICLES: <td>Hatchback</td>
+// HTML-VEHICLES: <td>3</td>
+// HTML-VEHICLES: <p> Comment 4</p>
+
+
+enum ColorUserSpecified {
+ RedUserSpecified = 'A',
+ GreenUserSpecified = 2,
+ BlueUserSpecified = 'C'
+};
+
+// MD-INDEX: | enum ColorUserSpecified |
+// MD-INDEX: --
+// MD-INDEX: | RedUserSpecified |
+// MD-INDEX: | GreenUserSpecified |
+// MD-INDEX: | BlueUserSpecified |
+
+// HTML-INDEX: <th colspan="2">enum ColorUserSpecified</th>
+// HTML-INDEX: <td>RedUserSpecified</td>
+// HTML-INDEX: <td>'A'</td>
+// HTML-INDEX: <td>GreenUserSpecified</td>
+// HTML-INDEX: <td>2</td>
+// HTML-INDEX: <td>BlueUserSpecified</td>
+// HTML-INDEX: <td>'C'</td>
\ No newline at end of file
>From bcc4b0dc8b3b0216e6249c4de6f2cbcf14006bf6 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 31 Jul 2024 00:02:13 -0400
Subject: [PATCH 02/62] [clang-doc] remove useless code
---
clang-tools-extra/clang-doc/Representation.cpp | 2 --
clang-tools-extra/clang-doc/Representation.h | 2 --
2 files changed, 4 deletions(-)
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 028dffc21793ae..d08afbb9621890 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -266,8 +266,6 @@ void EnumInfo::merge(EnumInfo &&Other) {
Scoped = Other.Scoped;
if (Members.empty())
Members = std::move(Other.Members);
- if (Other.HasComments || HasComments)
- HasComments = true;
SymbolInfo::merge(std::move(Other));
}
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index db3aff2d60a1b5..bd5254b0a84657 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -445,8 +445,6 @@ struct EnumInfo : public SymbolInfo {
// Indicates whether this enum is scoped (e.g. enum class).
bool Scoped = false;
- // Indicates whether or not enum members have comments attached
- bool HasComments = false;
// Set to nonempty to the type when this is an explicitly typed enum. For
// enum Foo : short { ... };
>From 5fe47ca87f8dd592fee7a45401eed2620152e5c1 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 31 Jul 2024 00:33:48 -0400
Subject: [PATCH 03/62] [clang-doc] modify unittest
---
.../unittests/clang-doc/HTMLGeneratorTest.cpp | 33 +++++++++++++++----
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
index e4a7340318b934..7ee482e275149d 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -92,7 +92,13 @@ TEST(HTMLGeneratorTest, emitNamespaceHTML) {
</div>
<h2 id="Enums">Enums</h2>
<div>
- <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3>
+ <table id="0000000000000000000000000000000000000000">
+ <thead>
+ <tr>
+ <th colspan="2">enum OneEnum</th>
+ </tr>
+ </thead>
+ </table>
</div>
</div>
<div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
@@ -212,7 +218,13 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
</div>
<h2 id="Enums">Enums</h2>
<div>
- <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3>
+ <table id="0000000000000000000000000000000000000000">
+ <thead>
+ <tr>
+ <th colspan="2">enum OneEnum</th>
+ </tr>
+ </thead>
+ </table>
</div>
</div>
<div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
@@ -346,10 +358,19 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
<main>
<div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
<div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
- <h3 id="0000000000000000000000000000000000000000">enum class e</h3>
- <ul>
- <li>X</li>
- </ul>
+ <table id="0000000000000000000000000000000000000000">
+ <thead>
+ <tr>
+ <th colspan="2">enum class e</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>X</td>
+ <td>0</td>
+ </tr>
+ </tbody>
+ </table>
<p>
Defined at line
<a href="https://www.repository.com/test.cpp#10">10</a>
>From 28fb40f0cdbe37257d8aea9f05519519d9f2c470 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 17:12:48 -0400
Subject: [PATCH 04/62] [clang-doc] address pr comments
---
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 38 ++++++++-----------
.../clang-doc/Representation.cpp | 2 +-
clang-tools-extra/clang-doc/Representation.h | 4 +-
clang-tools-extra/clang-doc/Serialize.cpp | 8 ++--
4 files changed, 23 insertions(+), 29 deletions(-)
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index a37192d6ceb9b0..ad7e08667e5cbc 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -397,8 +397,7 @@ genEnumsBlock(const std::vector<EnumInfo> &Enums,
}
static std::unique_ptr<TagNode>
-genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members,
- bool HasComments) {
+genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
if (Members.empty())
return nullptr;
@@ -416,8 +415,7 @@ genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members,
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
}
-
- if (HasComments) {
+ 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));
@@ -663,7 +661,7 @@ static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
}
return std::move(FullComment);
}
-
+
if (I.Kind == "ParagraphComment") {
auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
for (const auto &Child : I.Children) {
@@ -698,16 +696,12 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
std::string EnumType = I.Scoped ? "enum class " : "enum ";
// Determine if enum members have comments attached
- bool HasComments = false;
- for (const auto &M : I.Members) {
- if (!M.Description.empty()) {
- HasComments = true;
- break;
- }
- }
+ 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::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 =
@@ -717,10 +711,10 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
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));
+ THead->Children.emplace_back(std::move(TRow));
+ Table->Children.emplace_back(std::move(THead));
- std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members, HasComments);
+ std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
if (Node)
Table->Children.emplace_back(std::move(Node));
@@ -731,8 +725,8 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
- Out.emplace_back(writeFileDefinition(
- *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+ Out.emplace_back(
+ writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}
std::string Description;
@@ -780,8 +774,8 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
- Out.emplace_back(writeFileDefinition(
- *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+ Out.emplace_back(
+ writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}
std::string Description;
@@ -847,8 +841,8 @@ genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
- Out.emplace_back(writeFileDefinition(
- *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+ Out.emplace_back(
+ writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}
std::string Description;
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index d08afbb9621890..da948ee74c9d63 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -221,7 +221,7 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
}
NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
- : Info(InfoType::IT_namespace, USR, Name, Path) {}
+ : Info(InfoType::IT_namespace, USR, Name, Path) {}
void NamespaceInfo::merge(NamespaceInfo &&Other) {
assert(mergeable(Other));
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index bd5254b0a84657..873ac728066261 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -48,7 +48,7 @@ enum class InfoType {
// A representation of a parsed comment.
struct CommentInfo {
CommentInfo() = default;
- CommentInfo(CommentInfo &Other) = delete;
+ CommentInfo(CommentInfo &Other) = default;
CommentInfo(CommentInfo &&Other) = default;
CommentInfo &operator=(CommentInfo &&Other) = default;
@@ -432,7 +432,7 @@ struct EnumValueInfo {
// constant. This will be empty for implicit enumeration values.
SmallString<16> ValueExpr;
- std::vector<CommentInfo> Description; // Comment description of this field.
+ std::vector<CommentInfo> Description; /// Comment description of this field.
};
// TODO: Expand to allow for documenting templating.
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 78b7041368d6df..273bc10d3b55d8 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -398,8 +398,8 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
E->getInitVal().toString(ValueStr);
I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
ASTContext &Context = E->getASTContext();
- RawComment *Comment = E->getASTContext().getRawCommentForDeclNoCache(E);
- if (Comment) {
+ if (RawComment *Comment =
+ E->getASTContext().getRawCommentForDeclNoCache(E)) {
CommentInfo CInfo;
Comment->setAttached();
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
@@ -568,7 +568,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
- ASTContext& Context = D->getASTContext();
+ ASTContext &Context = D->getASTContext();
// TODO investigate whether we can use ASTContext::getCommentForDecl instead
// of this logic. See also similar code in Mapper.cpp.
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
@@ -576,7 +576,7 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
return;
Comment->setAttached();
- if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
+ if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
I.Description.emplace_back();
parseFullComment(fc, I.Description.back());
}
>From 0d150ea08af767017e108096533f0c0d8b31668a Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 17:24:55 -0400
Subject: [PATCH 05/62] [clang-doc] revert CommentInfo change
---
clang-tools-extra/clang-doc/Representation.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 873ac728066261..8f2bba786316fe 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -48,7 +48,7 @@ enum class InfoType {
// A representation of a parsed comment.
struct CommentInfo {
CommentInfo() = default;
- CommentInfo(CommentInfo &Other) = default;
+ CommentInfo(CommentInfo &Other) = delete;
CommentInfo(CommentInfo &&Other) = default;
CommentInfo &operator=(CommentInfo &&Other) = default;
>From e5e70b87003e4e7b8e68d23fc89f7edd6961764f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 18:29:39 -0400
Subject: [PATCH 06/62] [clang-doc] fix test
---
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ad7e08667e5cbc..00f94788ced44c 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -415,7 +415,7 @@ genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
}
- if (M.Description.empty()) {
+ 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));
>From 1c4f631df65c1560523ea63c82229102f3d99f54 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 19:19:39 -0400
Subject: [PATCH 07/62] [clang-doc] fix unittest
---
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 00f94788ced44c..6baed082af4a81 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -698,7 +698,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
// 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(); });
+ [](const EnumValueInfo &M) { return !M.Description.empty(); });
std::unique_ptr<TagNode> Table =
std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
std::unique_ptr<TagNode> THead =
>From b8f3f9c97ccd503c07387f1364cb3b357fa92821 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 19:35:15 -0400
Subject: [PATCH 08/62] [clang-doc] address pr comments
---
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 20 +++++++++----------
clang-tools-extra/clang-doc/Serialize.cpp | 2 +-
clang-tools-extra/test/clang-doc/enum.cpp | 2 +-
3 files changed, 11 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 6baed082af4a81..5b023a409c364f 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -696,9 +696,9 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
std::string EnumType = I.Scoped ? "enum class " : "enum ";
// 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(); });
+ 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 =
@@ -713,10 +713,8 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
TRow->Children.emplace_back(std::move(TD));
THead->Children.emplace_back(std::move(TRow));
Table->Children.emplace_back(std::move(THead));
-
- std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
-
- if (Node)
+
+ if (std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members))
Table->Children.emplace_back(std::move(Node));
Out.emplace_back(std::move(Table));
@@ -774,8 +772,8 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
- Out.emplace_back(
- writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+ Out.emplace_back(writeFileDefinition(
+ *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}
std::string Description;
@@ -841,8 +839,8 @@ genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
- Out.emplace_back(
- writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+ Out.emplace_back(writeFileDefinition(
+ *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}
std::string Description;
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 273bc10d3b55d8..b9db78cf7d688f 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -568,7 +568,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
- ASTContext &Context = D->getASTContext();
+ ASTContext& Context = D->getASTContext();
// TODO investigate whether we can use ASTContext::getCommentForDecl instead
// of this logic. See also similar code in Mapper.cpp.
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index fd7bbcb53f2d2b..ef768e33b45668 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -178,4 +178,4 @@ enum ColorUserSpecified {
// HTML-INDEX: <td>GreenUserSpecified</td>
// HTML-INDEX: <td>2</td>
// HTML-INDEX: <td>BlueUserSpecified</td>
-// HTML-INDEX: <td>'C'</td>
\ No newline at end of file
+// HTML-INDEX: <td>'C'</td>
>From bb98aad362353e44267f2df6ffb985005e67f147 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 19:45:02 -0400
Subject: [PATCH 09/62] [clang-doc] clang-format
---
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 5b023a409c364f..0ced83f91724ff 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -713,7 +713,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
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));
>From c60e2b505e27cebffac27b0085fee12d68bbedf7 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 23 Aug 2024 17:39:16 -0400
Subject: [PATCH 10/62] [llvm] implement support for mustache template language
---
llvm/include/llvm/Support/Mustache.h | 109 ++++++++++
llvm/lib/Support/CMakeLists.txt | 1 +
llvm/lib/Support/Mustache.cpp | 276 ++++++++++++++++++++++++
llvm/unittests/Support/CMakeLists.txt | 1 +
llvm/unittests/Support/MustacheTest.cpp | 135 ++++++++++++
5 files changed, 522 insertions(+)
create mode 100644 llvm/include/llvm/Support/Mustache.h
create mode 100644 llvm/lib/Support/Mustache.cpp
create mode 100644 llvm/unittests/Support/MustacheTest.cpp
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
new file mode 100644
index 00000000000000..a1ce9d945a37c5
--- /dev/null
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -0,0 +1,109 @@
+//===--- Mustache.h ---------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the Mustache templating language supports version 1.4.2
+// (https://mustache.github.io/mustache.5.html).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_MUSTACHE
+#define LLVM_SUPPORT_MUSTACHE
+
+#include "Error.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/JSON.h"
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace llvm {
+namespace mustache {
+
+using Accessor = std::vector<std::string>;
+
+class Token {
+public:
+ enum class Type {
+ Text,
+ Variable,
+ Partial,
+ SectionOpen,
+ SectionClose,
+ InvertSectionOpen,
+ UnescapeVariable,
+ Comment,
+ };
+
+ Token(std::string Str);
+
+ Token(std::string Str, char Identifier);
+
+ std::string getTokenBody() const { return TokenBody; };
+
+ Accessor getAccessor() const { return Accessor; };
+
+ Type getType() const { return TokenType; };
+
+private:
+ Type TokenType;
+ Accessor Accessor;
+ std::string TokenBody;
+};
+
+class ASTNode {
+public:
+ enum Type {
+ Root,
+ Text,
+ Partial,
+ Variable,
+ UnescapeVariable,
+ Section,
+ InvertSection,
+ };
+
+ ASTNode() : T(Type::Root), LocalContext(nullptr){};
+
+ ASTNode(std::string Body, std::shared_ptr<ASTNode> Parent)
+ : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
+
+ // Constructor for Section/InvertSection/Variable/UnescapeVariable
+ ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
+ : T(T), Accessor(Accessor), Parent(Parent), LocalContext(nullptr),
+ Children({}){};
+
+ void addChild(std::shared_ptr<ASTNode> Child) {
+ Children.emplace_back(Child);
+ };
+
+ std::string render(llvm::json::Value Data);
+
+ llvm::json::Value findContext();
+
+ Type T;
+ std::string Body;
+ std::weak_ptr<ASTNode> Parent;
+ std::vector<std::shared_ptr<ASTNode>> Children;
+ Accessor Accessor;
+ llvm::json::Value LocalContext;
+};
+
+class Template {
+public:
+ static Expected<Template> createTemplate(std::string TemplateStr);
+
+ std::string render(llvm::json::Value Data);
+
+private:
+ Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree){};
+ std::shared_ptr<ASTNode> Tree;
+};
+
+} // namespace mustache
+} // end namespace llvm
+#endif // LLVM_SUPPORT_MUSTACHE
\ No newline at end of file
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index f653379e303349..91c8c3fd7de0e6 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -207,6 +207,7 @@ add_llvm_component_library(LLVMSupport
MD5.cpp
MSP430Attributes.cpp
MSP430AttributeParser.cpp
+ Mustache.cpp
NativeFormatting.cpp
OptimizedStructLayout.cpp
Optional.cpp
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
new file mode 100644
index 00000000000000..19a0ccffe87acc
--- /dev/null
+++ b/llvm/lib/Support/Mustache.cpp
@@ -0,0 +1,276 @@
+//===-- Mustache.cpp ------------------------------------------------------===//
+//
+// 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 "llvm/Support/Mustache.h"
+#include "llvm/Support/Error.h"
+#include <iostream>
+#include <regex>
+#include <sstream>
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::mustache;
+
+std::string escapeHtml(const std::string &Input) {
+ DenseMap<char, std::string> HtmlEntities = {{'&', "&"},
+ {'<', "<"},
+ {'>', ">"},
+ {'"', """},
+ {'"', "'"}};
+ std::string EscapedString;
+ EscapedString.reserve(Input.size());
+
+ for (char C : Input) {
+ if (HtmlEntities.find(C) != HtmlEntities.end()) {
+ EscapedString += HtmlEntities[C];
+ } else {
+ EscapedString += C;
+ }
+ }
+
+ return EscapedString;
+}
+
+std::vector<std::string> split(const std::string &Str, char Delimiter) {
+ std::vector<std::string> Tokens;
+ std::string Token;
+ std::stringstream SS(Str);
+ if (Str == ".") {
+ Tokens.push_back(Str);
+ return Tokens;
+ }
+ while (std::getline(SS, Token, Delimiter)) {
+ Tokens.push_back(Token);
+ }
+ return Tokens;
+}
+
+Token::Token(std::string Str, char Identifier) {
+ switch (Identifier) {
+ case '#':
+ TokenType = Type::SectionOpen;
+ break;
+ case '/':
+ TokenType = Type::SectionClose;
+ break;
+ case '^':
+ TokenType = Type::InvertSectionOpen;
+ break;
+ case '!':
+ TokenType = Type::Comment;
+ break;
+ case '>':
+ TokenType = Type::Partial;
+ break;
+ case '&':
+ TokenType = Type::UnescapeVariable;
+ break;
+ default:
+ TokenType = Type::Variable;
+ }
+ if (TokenType == Type::Comment)
+ return;
+
+ TokenBody = Str;
+ std::string AccessorStr = Str;
+ if (TokenType != Type::Variable) {
+ AccessorStr = Str.substr(1);
+ }
+ Accessor = split(StringRef(AccessorStr).trim().str(), '.');
+}
+
+Token::Token(std::string Str)
+ : TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+
+std::vector<Token> tokenize(std::string Template) {
+ std::vector<Token> Tokens;
+ std::regex Re(R"(\{\{(.*?)\}\})");
+ std::sregex_token_iterator Iter(Template.begin(), Template.end(), Re,
+ {-1, 0});
+ std::sregex_token_iterator End;
+
+ for (; Iter != End; ++Iter) {
+ if (!Iter->str().empty()) {
+ std::string Token = *Iter;
+ std::smatch Match;
+ if (std::regex_match(Token, Match, Re)) {
+ std::string Group = Match[1];
+ Tokens.emplace_back(Group, Group[0]);
+ } else {
+ Tokens.emplace_back(Token);
+ }
+ }
+ }
+
+ return Tokens;
+}
+
+class Parser {
+public:
+ Parser(std::string TemplateStr) : TemplateStr(TemplateStr) {}
+
+ std::shared_ptr<ASTNode> parse();
+
+private:
+ void parseMustache(std::shared_ptr<ASTNode> Parent);
+
+ std::vector<Token> Tokens;
+ std::size_t CurrentPtr;
+ std::string TemplateStr;
+};
+
+std::shared_ptr<ASTNode> Parser::parse() {
+ Tokens = tokenize(TemplateStr);
+ CurrentPtr = 0;
+ std::shared_ptr<ASTNode> Root = std::make_shared<ASTNode>();
+ parseMustache(Root);
+ return Root;
+}
+
+void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
+
+ while (CurrentPtr < Tokens.size()) {
+ Token CurrentToken = Tokens[CurrentPtr];
+ CurrentPtr++;
+ Accessor A = CurrentToken.getAccessor();
+ std::shared_ptr<ASTNode> CurrentNode;
+
+ switch (CurrentToken.getType()) {
+ case Token::Type::Text: {
+ CurrentNode =
+ std::make_shared<ASTNode>(CurrentToken.getTokenBody(), Parent);
+ Parent->addChild(CurrentNode);
+ break;
+ }
+ case Token::Type::Variable: {
+ CurrentNode = std::make_shared<ASTNode>(ASTNode::Variable, A, Parent);
+ Parent->addChild(CurrentNode);
+ break;
+ }
+ case Token::Type::UnescapeVariable: {
+ CurrentNode =
+ std::make_shared<ASTNode>(ASTNode::UnescapeVariable, A, Parent);
+ Parent->addChild(CurrentNode);
+ break;
+ }
+ case Token::Type::Partial: {
+ CurrentNode = std::make_shared<ASTNode>(ASTNode::Partial, A, Parent);
+ Parent->addChild(CurrentNode);
+ break;
+ }
+ case Token::Type::SectionOpen: {
+ CurrentNode = std::make_shared<ASTNode>(ASTNode::Section, A, Parent);
+ parseMustache(CurrentNode);
+ Parent->addChild(CurrentNode);
+ break;
+ }
+ case Token::Type::InvertSectionOpen: {
+ CurrentNode =
+ std::make_shared<ASTNode>(ASTNode::InvertSection, A, Parent);
+ parseMustache(CurrentNode);
+ Parent->addChild(CurrentNode);
+ break;
+ }
+ case Token::Type::SectionClose: {
+ return;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+Expected<Template> Template::createTemplate(std::string TemplateStr) {
+ Parser P = Parser(TemplateStr);
+ Expected<std::shared_ptr<ASTNode>> MustacheTree = P.parse();
+ if (!MustacheTree)
+ return MustacheTree.takeError();
+ return Template(MustacheTree.get());
+}
+std::string Template::render(Value Data) { return Tree->render(Data); }
+
+std::string printJson(Value &Data) {
+ if (Data.getAsNull().has_value()) {
+ return "";
+ }
+ if (auto *Arr = Data.getAsArray()) {
+ if (Arr->empty()) {
+ return "";
+ }
+ }
+ if (Data.getAsString().has_value()) {
+ return Data.getAsString()->str();
+ }
+ return llvm::formatv("{0:2}", Data);
+}
+
+std::string ASTNode::render(Value Data) {
+ LocalContext = Data;
+ Value Context = T == Root ? Data : findContext();
+ switch (T) {
+ case Root: {
+ std::string Result = "";
+ for (std::shared_ptr<ASTNode> Child : Children) {
+ Result += Child->render(Context);
+ }
+ return Result;
+ }
+ case Text:
+ return escapeHtml(Body);
+ case Partial:
+ break;
+ case Variable:
+ return escapeHtml(printJson(Context));
+ case UnescapeVariable:
+ return printJson(Context);
+ case Section:
+ break;
+ case InvertSection:
+ break;
+ }
+
+ return std::string();
+}
+
+Value ASTNode::findContext() {
+ if (Accessor.empty()) {
+ return nullptr;
+ }
+ if (Accessor[0] == ".") {
+ return LocalContext;
+ }
+ json::Object *CurrentContext = LocalContext.getAsObject();
+ std::string &CurrentAccessor = Accessor[0];
+ std::weak_ptr<ASTNode> CurrentParent = Parent;
+
+ while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
+ if (auto Ptr = CurrentParent.lock()) {
+ CurrentContext = Ptr->LocalContext.getAsObject();
+ CurrentParent = Ptr->Parent;
+ continue;
+ }
+ return nullptr;
+ }
+ Value Context = nullptr;
+ for (std::size_t i = 0; i < Accessor.size(); i++) {
+ CurrentAccessor = Accessor[i];
+ Value *CurrentValue = CurrentContext->get(CurrentAccessor);
+ if (!CurrentValue) {
+ return nullptr;
+ }
+ if (i < Accessor.size() - 1) {
+ CurrentContext = CurrentValue->getAsObject();
+ if (!CurrentContext) {
+ return nullptr;
+ }
+ } else {
+ Context = *CurrentValue;
+ }
+ }
+ return Context;
+}
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 631f2e6bf00df0..63a6a13019bdcf 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -60,6 +60,7 @@ add_llvm_unittest(SupportTests
MemoryBufferRefTest.cpp
MemoryBufferTest.cpp
MemoryTest.cpp
+ MustacheTest.cpp
NativeFormatTests.cpp
OptimizedStructLayoutTest.cpp
ParallelTest.cpp
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
new file mode 100644
index 00000000000000..7479e29816e944
--- /dev/null
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -0,0 +1,135 @@
+//===- llvm/unittest/Support/MustacheTest.cpp ----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Test conforming to Mustache 1.4.2 spec found here:
+// https://github.com/mustache/spec
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Mustache.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::mustache;
+using namespace llvm::json;
+
+TEST(MustacheInterpolation, NoInterpolation) {
+ // Mustache-free templates should render as-is.
+ Value D = {};
+ auto T = Template::createTemplate("Hello from {Mustache}!\n");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("Hello from {Mustache}!\n", Out);
+}
+
+TEST(MustacheInterpolation, BasicInterpolation) {
+ // Unadorned tags should interpolate content into the template.
+ Value D = Object{{"subject", "World"}};
+ auto T = Template::createTemplate("Hello, {{subject}}!");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("Hello, World!", Out);
+}
+
+TEST(MustacheInterpolation, NoReinterpolation) {
+ // Interpolated tag output should not be re-interpolated.
+ Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
+ auto T = Template::createTemplate("{{template}}: {{planet}}");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("{{planet}}: Earth", Out);
+}
+
+TEST(MustacheInterpolation, HTMLEscaping) {
+ // Interpolated tag output should not be re-interpolated.
+ Value D = Object{
+ {"forbidden", "& \" < >"},
+ };
+ auto T = Template::createTemplate(
+ "These characters should be HTML escaped: {{forbidden}}\n");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
+ Out);
+}
+
+TEST(MustacheInterpolation, Ampersand) {
+ // Interpolated tag output should not be re-interpolated.
+ Value D = Object{
+ {"forbidden", "& \" < >"},
+ };
+ auto T = Template::createTemplate(
+ "These characters should not be HTML escaped: {{&forbidden}}\n");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
+}
+
+TEST(MustacheInterpolation, BasicIntegerInterpolation) {
+ Value D = Object{{"mph", 85}};
+ auto T = Template::createTemplate("{{mph}} miles an hour!");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("85 miles an hour!", Out);
+}
+
+TEST(MustacheInterpolation, BasicDecimalInterpolation) {
+ Value D = Object{{"power", 1.21}};
+ auto T = Template::createTemplate("{{power}} jiggawatts!");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("1.21 jiggawatts!", Out);
+}
+
+TEST(MustacheInterpolation, BasicNullInterpolation) {
+ Value D = Object{{"cannot", nullptr}};
+ auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("I () be seen!", Out);
+}
+
+TEST(MustacheInterpolation, BasicContextMissInterpolation) {
+ Value D = Object{};
+ auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("I () be seen!", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
+ Value D = Object{{"person", Object{{"name", "Joe"}}}};
+ auto T = Template::createTemplate(
+ "{{person.name}} == {{#person}}{{name}}{{/person}}");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("Joe == Joe", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
+ Value D = Object{
+ {"a",
+ Object{{"b",
+ Object{{"c",
+ Object{{"d",
+ Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
+ auto T = Template::createTemplate("{{a.b.c.d.e.name}} == Phil");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("Phil == Phil", Out);
+}
+
+TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
+ Value D = "world";
+ auto T = Template::createTemplate("Hello, {{.}}!\n");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("Hello, world!\n", Out);
+}
+
+TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
+ Value D = Object{{"string", "---"}};
+ auto T = Template::createTemplate("| {{string}} |");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("| --- |", Out);
+}
+
+TEST(MustacheInterpolation, InterpolationWithPadding) {
+ Value D = Object{{"string", "---"}};
+ auto T = Template::createTemplate("|{{ string }}|");
+ auto Out = T.get().render(D);
+ EXPECT_EQ("|---|", Out);
+}
>From 8290c381a099864db4c827e6102c015f09cfed5c Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 02:35:33 -0400
Subject: [PATCH 11/62] [llvm][Support] Finish implementation of Mustache
---
llvm/include/llvm/Support/Mustache.h | 63 +-
llvm/lib/Support/Mustache.cpp | 383 ++++++++---
llvm/unittests/Support/MustacheTest.cpp | 877 +++++++++++++++++++++++-
3 files changed, 1204 insertions(+), 119 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index a1ce9d945a37c5..cb7d80e47809e8 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -24,7 +24,9 @@
namespace llvm {
namespace mustache {
-using Accessor = std::vector<std::string>;
+using Accessor = std::vector<SmallString<128>>;
+using Lambda = std::function<llvm::json::Value()>;
+using SectionLambda = std::function<llvm::json::Value(StringRef)>;
class Token {
public:
@@ -39,20 +41,27 @@ class Token {
Comment,
};
- Token(std::string Str);
+ Token(StringRef Str);
- Token(std::string Str, char Identifier);
+ Token(StringRef RawBody, StringRef Str, char Identifier);
- std::string getTokenBody() const { return TokenBody; };
+ StringRef getTokenBody() const { return TokenBody; };
+
+ StringRef getRawBody() const { return RawBody; };
+
+ void setTokenBody(SmallString<128> NewBody) { TokenBody = NewBody; };
Accessor getAccessor() const { return Accessor; };
Type getType() const { return TokenType; };
+ static Type getTokenType(char Identifier);
+
private:
Type TokenType;
+ SmallString<128> RawBody;
Accessor Accessor;
- std::string TokenBody;
+ SmallString<128> TokenBody;
};
class ASTNode {
@@ -69,7 +78,7 @@ class ASTNode {
ASTNode() : T(Type::Root), LocalContext(nullptr){};
- ASTNode(std::string Body, std::shared_ptr<ASTNode> Parent)
+ ASTNode(StringRef Body, std::shared_ptr<ASTNode> Parent)
: T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
@@ -81,26 +90,56 @@ class ASTNode {
Children.emplace_back(Child);
};
- std::string render(llvm::json::Value Data);
+ SmallString<128> getBody() const { return Body; };
- llvm::json::Value findContext();
+ void setBody(StringRef NewBody) { Body = NewBody; };
+
+ void setRawBody(StringRef NewBody) { RawBody = NewBody; };
+
+ SmallString<128> getRawBody() const { return RawBody; };
+
+ std::shared_ptr<ASTNode> getLastChild() const {
+ return Children.empty() ? nullptr : Children.back();
+ };
+ SmallString<128>
+ render(llvm::json::Value Data,
+ DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
+ DenseMap<StringRef, Lambda> &Lambdas,
+ DenseMap<StringRef, SectionLambda> &SectionLambdas,
+ DenseMap<char, StringRef> &Escapes);
+
+private:
+ llvm::json::Value findContext();
Type T;
- std::string Body;
+ SmallString<128> RawBody;
+ SmallString<128> Body;
std::weak_ptr<ASTNode> Parent;
std::vector<std::shared_ptr<ASTNode>> Children;
- Accessor Accessor;
+ const Accessor Accessor;
llvm::json::Value LocalContext;
};
class Template {
public:
- static Expected<Template> createTemplate(std::string TemplateStr);
+ static Template createTemplate(StringRef TemplateStr);
+
+ SmallString<128> render(llvm::json::Value Data);
+
+ void registerPartial(StringRef Name, StringRef Partial);
+
+ void registerLambda(StringRef Name, Lambda Lambda);
+
+ void registerLambda(StringRef Name, SectionLambda Lambda);
- std::string render(llvm::json::Value Data);
+ void registerEscape(DenseMap<char, StringRef> Escapes);
private:
Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree){};
+ DenseMap<StringRef, std::shared_ptr<ASTNode>> Partials;
+ DenseMap<StringRef, Lambda> Lambdas;
+ DenseMap<StringRef, SectionLambda> SectionLambdas;
+ DenseMap<char, StringRef> Escapes;
std::shared_ptr<ASTNode> Tree;
};
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 19a0ccffe87acc..80b3d8bc53651b 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -16,103 +16,172 @@ using namespace llvm;
using namespace llvm::json;
using namespace llvm::mustache;
-std::string escapeHtml(const std::string &Input) {
- DenseMap<char, std::string> HtmlEntities = {{'&', "&"},
- {'<', "<"},
- {'>', ">"},
- {'"', """},
- {'"', "'"}};
- std::string EscapedString;
- EscapedString.reserve(Input.size());
-
+SmallString<128> escapeString(StringRef Input,
+ DenseMap<char, StringRef> &Escape) {
+ SmallString<128> EscapedString("");
for (char C : Input) {
- if (HtmlEntities.find(C) != HtmlEntities.end()) {
- EscapedString += HtmlEntities[C];
+ if (Escape.find(C) != Escape.end()) {
+ EscapedString += Escape[C];
} else {
EscapedString += C;
}
}
-
return EscapedString;
}
-std::vector<std::string> split(const std::string &Str, char Delimiter) {
- std::vector<std::string> Tokens;
- std::string Token;
- std::stringstream SS(Str);
+std::vector<SmallString<128>> split(StringRef Str, char Delimiter) {
+ std::vector<SmallString<128>> Tokens;
if (Str == ".") {
Tokens.push_back(Str);
return Tokens;
}
- while (std::getline(SS, Token, Delimiter)) {
- Tokens.push_back(Token);
+ StringRef Ref(Str);
+ while (!Ref.empty()) {
+ llvm::StringRef Part;
+ std::tie(Part, Ref) = Ref.split(Delimiter);
+ Tokens.push_back(Part.trim());
}
return Tokens;
}
-Token::Token(std::string Str, char Identifier) {
+Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
+ : RawBody(RawBody), TokenBody(InnerBody) {
+
+ TokenType = getTokenType(Identifier);
+ if (TokenType == Type::Comment)
+ return;
+
+ StringRef AccessorStr = InnerBody;
+ if (TokenType != Type::Variable) {
+ AccessorStr = InnerBody.substr(1);
+ }
+ Accessor = split(AccessorStr.trim(), '.');
+}
+
+Token::Token(StringRef Str)
+ : RawBody(Str), TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+
+Token::Type Token::getTokenType(char Identifier) {
switch (Identifier) {
case '#':
- TokenType = Type::SectionOpen;
- break;
+ return Type::SectionOpen;
case '/':
- TokenType = Type::SectionClose;
- break;
+ return Type::SectionClose;
case '^':
- TokenType = Type::InvertSectionOpen;
- break;
+ return Type::InvertSectionOpen;
case '!':
- TokenType = Type::Comment;
- break;
+ return Type::Comment;
case '>':
- TokenType = Type::Partial;
- break;
+ return Type::Partial;
case '&':
- TokenType = Type::UnescapeVariable;
- break;
+ return Type::UnescapeVariable;
default:
- TokenType = Type::Variable;
+ return Type::Variable;
}
- if (TokenType == Type::Comment)
- return;
+}
- TokenBody = Str;
- std::string AccessorStr = Str;
- if (TokenType != Type::Variable) {
- AccessorStr = Str.substr(1);
+std::vector<Token> tokenize(StringRef Template) {
+ // Simple tokenizer that splits the template into tokens
+ // the mustache spec allows {{{ }}} to unescape variables
+ // but we don't support that here unescape variable
+ // is represented only by {{& variable}}
+ std::vector<Token> Tokens;
+ SmallString<128> Open("{{");
+ SmallString<128> Close("}}");
+ std::size_t Start = 0;
+ std::size_t DelimiterStart = Template.find(Open);
+ if (DelimiterStart == StringRef::npos) {
+ Tokens.push_back(Token(Template));
+ return Tokens;
}
- Accessor = split(StringRef(AccessorStr).trim().str(), '.');
-}
+ while (DelimiterStart != StringRef::npos) {
+ if (DelimiterStart != Start) {
+ Token TextToken = Token(Template.substr(Start, DelimiterStart - Start));
+ Tokens.push_back(TextToken);
+ }
+
+ std::size_t DelimiterEnd = Template.find(Close, DelimiterStart);
+ if (DelimiterEnd == StringRef::npos) {
+ break;
+ }
-Token::Token(std::string Str)
- : TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+ SmallString<128> Interpolated =
+ Template.substr(DelimiterStart + Open.size(),
+ DelimiterEnd - DelimiterStart - Close.size());
+ SmallString<128> RawBody;
+ RawBody += Open;
+ RawBody += Interpolated;
+ RawBody += Close;
-std::vector<Token> tokenize(std::string Template) {
- std::vector<Token> Tokens;
- std::regex Re(R"(\{\{(.*?)\}\})");
- std::sregex_token_iterator Iter(Template.begin(), Template.end(), Re,
- {-1, 0});
- std::sregex_token_iterator End;
-
- for (; Iter != End; ++Iter) {
- if (!Iter->str().empty()) {
- std::string Token = *Iter;
- std::smatch Match;
- if (std::regex_match(Token, Match, Re)) {
- std::string Group = Match[1];
- Tokens.emplace_back(Group, Group[0]);
- } else {
- Tokens.emplace_back(Token);
+ Tokens.push_back(Token(RawBody, Interpolated, Interpolated[0]));
+ Start = DelimiterEnd + Close.size();
+ DelimiterStart = Template.find(Open, Start);
+ }
+
+ if (Start < Template.size()) {
+ Tokens.push_back(Token(Template.substr(Start)));
+ }
+
+ // fix up white spaces for
+ // open sections/inverted sections/close section/comment
+ for (std::size_t I = 0; I < Tokens.size(); I++) {
+ Token::Type CurrentType = Tokens[I].getType();
+ bool RequiresCleanUp = CurrentType == Token::Type::SectionOpen ||
+ CurrentType == Token::Type::InvertSectionOpen ||
+ CurrentType == Token::Type::SectionClose ||
+ CurrentType == Token::Type::Comment ||
+ CurrentType == Token::Type::Partial;
+
+ bool NoTextBehind = false;
+ bool NoTextAhead = false;
+ if (I > 0 && Tokens[I - 1].getType() == Token::Type::Text &&
+ RequiresCleanUp) {
+ Token &PrevToken = Tokens[I - 1];
+ StringRef TokenBody = PrevToken.getTokenBody().rtrim(" \t\v\t");
+ if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
+ TokenBody.empty()) {
+ NoTextBehind = true;
+ }
+ }
+ if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
+ RequiresCleanUp) {
+ Token &NextToken = Tokens[I + 1];
+ StringRef TokenBody = NextToken.getTokenBody().ltrim(" ");
+ if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n")) {
+ NoTextAhead = true;
}
}
- }
+ if (NoTextBehind && NoTextAhead) {
+ Token &PrevToken = Tokens[I - 1];
+ Token &NextToken = Tokens[I + 1];
+ StringRef NextTokenBody = NextToken.getTokenBody();
+ PrevToken.setTokenBody(PrevToken.getTokenBody().rtrim(" \t\v\t"));
+ if (NextTokenBody.starts_with("\r\n")) {
+ NextToken.setTokenBody(NextTokenBody.substr(2));
+ } else if (NextToken.getTokenBody().starts_with("\n")) {
+ NextToken.setTokenBody(NextTokenBody.substr(1));
+ }
+ } else if (NoTextAhead && I == 0) {
+ Token &NextToken = Tokens[I + 1];
+ StringRef NextTokenBody = NextToken.getTokenBody();
+ if (NextTokenBody.starts_with("\r\n")) {
+ NextToken.setTokenBody(NextTokenBody.substr(2));
+ } else if (NextToken.getTokenBody().starts_with("\n")) {
+ NextToken.setTokenBody(NextTokenBody.substr(1));
+ }
+ } else if (NoTextBehind && I == Tokens.size() - 1) {
+ Token &PrevToken = Tokens[I - 1];
+ StringRef PrevTokenBody = PrevToken.getTokenBody();
+ PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
+ }
+ }
return Tokens;
}
class Parser {
public:
- Parser(std::string TemplateStr) : TemplateStr(TemplateStr) {}
+ Parser(StringRef TemplateStr) : TemplateStr(TemplateStr) {}
std::shared_ptr<ASTNode> parse();
@@ -121,7 +190,7 @@ class Parser {
std::vector<Token> Tokens;
std::size_t CurrentPtr;
- std::string TemplateStr;
+ StringRef TemplateStr;
};
std::shared_ptr<ASTNode> Parser::parse() {
@@ -165,14 +234,34 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
}
case Token::Type::SectionOpen: {
CurrentNode = std::make_shared<ASTNode>(ASTNode::Section, A, Parent);
+ std::size_t Start = CurrentPtr;
parseMustache(CurrentNode);
+ std::size_t End = CurrentPtr;
+ SmallString<128> RawBody;
+ if (Start + 1 < End - 1)
+ for (std::size_t I = Start + 1; I < End - 1; I++) {
+ RawBody += Tokens[I].getRawBody();
+ }
+ else if (Start + 1 == End - 1)
+ RawBody = Tokens[Start].getRawBody();
+ CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
break;
}
case Token::Type::InvertSectionOpen: {
CurrentNode =
std::make_shared<ASTNode>(ASTNode::InvertSection, A, Parent);
+ std::size_t Start = CurrentPtr;
parseMustache(CurrentNode);
+ std::size_t End = CurrentPtr;
+ SmallString<128> RawBody;
+ if (Start + 1 < End - 1)
+ for (std::size_t I = Start + 1; I < End - 1; I++) {
+ RawBody += Tokens[I].getRawBody();
+ }
+ else if (Start + 1 == End - 1)
+ RawBody = Tokens[Start].getRawBody();
+ CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
break;
}
@@ -185,59 +274,167 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
}
}
-Expected<Template> Template::createTemplate(std::string TemplateStr) {
+Template Template::createTemplate(StringRef TemplateStr) {
Parser P = Parser(TemplateStr);
- Expected<std::shared_ptr<ASTNode>> MustacheTree = P.parse();
- if (!MustacheTree)
- return MustacheTree.takeError();
- return Template(MustacheTree.get());
+ std::shared_ptr<ASTNode> MustacheTree = P.parse();
+ Template T = Template(MustacheTree);
+ // the default behaviour is to escape html entities
+ DenseMap<char, StringRef> HtmlEntities = {{'&', "&"},
+ {'<', "<"},
+ {'>', ">"},
+ {'"', """},
+ {'\'', "'"}};
+ T.registerEscape(HtmlEntities);
+ return T;
+}
+
+SmallString<128> Template::render(Value Data) {
+ return Tree->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+}
+
+void Template::registerPartial(StringRef Name, StringRef Partial) {
+ Parser P = Parser(Partial);
+ std::shared_ptr<ASTNode> PartialTree = P.parse();
+ Partials[Name] = PartialTree;
+}
+
+void Template::registerLambda(StringRef Name, Lambda L) { Lambdas[Name] = L; }
+
+void Template::registerLambda(StringRef Name, SectionLambda L) {
+ SectionLambdas[Name] = L;
}
-std::string Template::render(Value Data) { return Tree->render(Data); }
-std::string printJson(Value &Data) {
- if (Data.getAsNull().has_value()) {
- return "";
+void Template::registerEscape(DenseMap<char, StringRef> E) { Escapes = E; }
+
+SmallString<128> printJson(Value &Data) {
+
+ SmallString<128> Result;
+ if (Data.getAsNull()) {
+ return Result;
}
if (auto *Arr = Data.getAsArray()) {
if (Arr->empty()) {
- return "";
+ return Result;
}
}
- if (Data.getAsString().has_value()) {
- return Data.getAsString()->str();
+ if (Data.getAsString()) {
+ Result += Data.getAsString()->str();
+ return Result;
}
return llvm::formatv("{0:2}", Data);
}
-std::string ASTNode::render(Value Data) {
+bool isFalsey(Value &V) {
+ return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
+ (V.getAsArray() && V.getAsArray()->empty()) ||
+ (V.getAsObject() && V.getAsObject()->empty());
+}
+
+SmallString<128>
+ASTNode::render(Value Data,
+ DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
+ DenseMap<StringRef, Lambda> &Lambdas,
+ DenseMap<StringRef, SectionLambda> &SectionLambdas,
+ DenseMap<char, StringRef> &Escapes) {
LocalContext = Data;
Value Context = T == Root ? Data : findContext();
+ SmallString<128> Result;
switch (T) {
case Root: {
- std::string Result = "";
- for (std::shared_ptr<ASTNode> Child : Children) {
- Result += Child->render(Context);
- }
+ for (std::shared_ptr<ASTNode> Child : Children)
+ Result +=
+ Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
return Result;
}
case Text:
- return escapeHtml(Body);
- case Partial:
- break;
- case Variable:
- return escapeHtml(printJson(Context));
- case UnescapeVariable:
+ return Body;
+ case Partial: {
+ if (Partials.find(Accessor[0]) != Partials.end()) {
+ std::shared_ptr<ASTNode> Partial = Partials[Accessor[0]];
+ Result +=
+ Partial->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+ return Result;
+ }
+ }
+ case Variable: {
+ if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
+ Lambda &L = Lambdas[Accessor[0]];
+ Value LambdaResult = L();
+ StringRef LambdaStr = printJson(LambdaResult);
+ Parser P = Parser(LambdaStr);
+ std::shared_ptr<ASTNode> LambdaNode = P.parse();
+ return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
+ Escapes);
+ }
+ return escapeString(printJson(Context), Escapes);
+ }
+ case UnescapeVariable: {
+ if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
+ Lambda &L = Lambdas[Accessor[0]];
+ Value LambdaResult = L();
+ StringRef LambdaStr = printJson(LambdaResult);
+ Parser P = Parser(LambdaStr);
+ std::shared_ptr<ASTNode> LambdaNode = P.parse();
+ DenseMap<char, StringRef> EmptyEscapes;
+ return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
+ EmptyEscapes);
+ }
return printJson(Context);
- case Section:
- break;
- case InvertSection:
- break;
}
+ case Section: {
+ // Sections are not rendered if the context is falsey
+ bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+
+ if (isFalsey(Context) && !IsLambda)
+ return Result;
+
+ if (IsLambda) {
+ SectionLambda &Lambda = SectionLambdas[Accessor[0]];
+ Value Return = Lambda(RawBody);
+ if (isFalsey(Return))
+ return Result;
+ StringRef LambdaStr = printJson(Return);
+ Parser P = Parser(LambdaStr);
+ std::shared_ptr<ASTNode> LambdaNode = P.parse();
+ return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
+ Escapes);
+ }
+
+ if (Context.getAsArray()) {
+ json::Array *Arr = Context.getAsArray();
+ for (Value &V : *Arr) {
+ for (std::shared_ptr<ASTNode> Child : Children)
+ Result +=
+ Child->render(V, Partials, Lambdas, SectionLambdas, Escapes);
+ }
+ return Result;
+ }
- return std::string();
+ for (std::shared_ptr<ASTNode> Child : Children)
+ Result +=
+ Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+
+ return Result;
+ }
+ case InvertSection: {
+ bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+ if (!isFalsey(Context) || IsLambda)
+ return Result;
+ for (std::shared_ptr<ASTNode> Child : Children)
+ Result +=
+ Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+ return Result;
+ }
+ }
+ llvm_unreachable("Invalid ASTNode type");
}
Value ASTNode::findContext() {
+ // The mustache spec allows for dot notation to access nested values
+ // a single dot refers to the current context
+ // We attempt to find the JSON context in the current node if it is not found
+ // we traverse the parent nodes to find the context until we reach the root
+ // node or the context is found
if (Accessor.empty()) {
return nullptr;
}
@@ -245,7 +442,7 @@ Value ASTNode::findContext() {
return LocalContext;
}
json::Object *CurrentContext = LocalContext.getAsObject();
- std::string &CurrentAccessor = Accessor[0];
+ SmallString<128> CurrentAccessor = Accessor[0];
std::weak_ptr<ASTNode> CurrentParent = Parent;
while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
@@ -257,13 +454,13 @@ Value ASTNode::findContext() {
return nullptr;
}
Value Context = nullptr;
- for (std::size_t i = 0; i < Accessor.size(); i++) {
- CurrentAccessor = Accessor[i];
+ for (std::size_t I = 0; I < Accessor.size(); I++) {
+ CurrentAccessor = Accessor[I];
Value *CurrentValue = CurrentContext->get(CurrentAccessor);
if (!CurrentValue) {
return nullptr;
}
- if (i < Accessor.size() - 1) {
+ if (I < Accessor.size() - 1) {
CurrentContext = CurrentValue->getAsObject();
if (!CurrentContext) {
return nullptr;
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index 7479e29816e944..ceee8942f88f4e 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -22,7 +22,7 @@ TEST(MustacheInterpolation, NoInterpolation) {
// Mustache-free templates should render as-is.
Value D = {};
auto T = Template::createTemplate("Hello from {Mustache}!\n");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("Hello from {Mustache}!\n", Out);
}
@@ -30,7 +30,7 @@ TEST(MustacheInterpolation, BasicInterpolation) {
// Unadorned tags should interpolate content into the template.
Value D = Object{{"subject", "World"}};
auto T = Template::createTemplate("Hello, {{subject}}!");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("Hello, World!", Out);
}
@@ -38,7 +38,7 @@ TEST(MustacheInterpolation, NoReinterpolation) {
// Interpolated tag output should not be re-interpolated.
Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
auto T = Template::createTemplate("{{template}}: {{planet}}");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("{{planet}}: Earth", Out);
}
@@ -49,7 +49,7 @@ TEST(MustacheInterpolation, HTMLEscaping) {
};
auto T = Template::createTemplate(
"These characters should be HTML escaped: {{forbidden}}\n");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
Out);
}
@@ -61,47 +61,78 @@ TEST(MustacheInterpolation, Ampersand) {
};
auto T = Template::createTemplate(
"These characters should not be HTML escaped: {{&forbidden}}\n");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
}
TEST(MustacheInterpolation, BasicIntegerInterpolation) {
+ // Integers should interpolate seamlessly.
Value D = Object{{"mph", 85}};
auto T = Template::createTemplate("{{mph}} miles an hour!");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
+ EXPECT_EQ("85 miles an hour!", Out);
+}
+
+TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
+ // Integers should interpolate seamlessly.
+ Value D = Object{{"mph", 85}};
+ auto T = Template::createTemplate("{{&mph}} miles an hour!");
+ auto Out = T.render(D);
EXPECT_EQ("85 miles an hour!", Out);
}
TEST(MustacheInterpolation, BasicDecimalInterpolation) {
+ // Decimals should interpolate seamlessly with proper significance.
Value D = Object{{"power", 1.21}};
auto T = Template::createTemplate("{{power}} jiggawatts!");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("1.21 jiggawatts!", Out);
}
TEST(MustacheInterpolation, BasicNullInterpolation) {
+ // Nulls should interpolate as the empty string.
Value D = Object{{"cannot", nullptr}};
auto T = Template::createTemplate("I ({{cannot}}) be seen!");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
+ EXPECT_EQ("I () be seen!", Out);
+}
+
+TEST(MustacheInterpolation, AmpersandNullInterpolation) {
+ // Nulls should interpolate as the empty string.
+ Value D = Object{{"cannot", nullptr}};
+ auto T = Template::createTemplate("I ({{&cannot}}) be seen!");
+ auto Out = T.render(D);
EXPECT_EQ("I () be seen!", Out);
}
TEST(MustacheInterpolation, BasicContextMissInterpolation) {
+ // Failed context lookups should default to empty strings.
Value D = Object{};
auto T = Template::createTemplate("I ({{cannot}}) be seen!");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("I () be seen!", Out);
}
TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
+ // Dotted names should be considered a form of shorthand for sections.
Value D = Object{{"person", Object{{"name", "Joe"}}}};
auto T = Template::createTemplate(
"{{person.name}} == {{#person}}{{name}}{{/person}}");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
+ EXPECT_EQ("Joe == Joe", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {
+ // Dotted names should be considered a form of shorthand for sections.
+ Value D = Object{{"person", Object{{"name", "Joe"}}}};
+ auto T = Template::createTemplate(
+ "{{&person.name}} == {{#person}}{{&name}}{{/person}}");
+ auto Out = T.render(D);
EXPECT_EQ("Joe == Joe", Out);
}
TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
+ // Dotted names should be functional to any level of nesting.
Value D = Object{
{"a",
Object{{"b",
@@ -109,27 +140,845 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
Object{{"d",
Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
auto T = Template::createTemplate("{{a.b.c.d.e.name}} == Phil");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("Phil == Phil", Out);
}
+TEST(MustacheInterpolation, DottedNamesBrokenChains) {
+ // Any falsey value prior to the last part of the name should yield ''.
+ Value D = Object{{"a", Object{}}};
+ auto T = Template::createTemplate("{{a.b.c}} == ");
+ auto Out = T.render(D);
+ EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {
+ // Each part of a dotted name should resolve only against its parent.
+ Value D =
+ Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};
+ auto T = Template::createTemplate("{{a.b.c.name}} == ");
+ auto Out = T.render(D);
+ EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesInitialResolution) {
+ // The first part of a dotted name should resolve as any other name.
+ Value D = Object{
+ {"a",
+ Object{
+ {"b",
+ Object{{"c",
+ Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},
+ {"b",
+ Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
+ auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}} == Phil");
+ auto Out = T.render(D);
+ EXPECT_EQ("Phil == Phil", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
+ // Dotted names should be resolved against former resolutions.
+ Value D =
+ Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};
+ auto T = Template::createTemplate("{{#a}}{{b.c}}{{/a}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
+ // Dotted names shall not be parsed as single, atomic keys
+ Value D = Object{{"a.b", "c"}};
+ auto T = Template::createTemplate("{{a.b}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesNoMasking) {
+ // Dotted Names in a given context are unavailable due to dot splitting
+ Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};
+ auto T = Template::createTemplate("{{a.b}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("d", Out);
+}
+
TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
+ // Unadorned tags should interpolate content into the template.
Value D = "world";
auto T = Template::createTemplate("Hello, {{.}}!\n");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("Hello, world!\n", Out);
}
+TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
+ // Basic interpolation should be HTML escaped.
+ Value D = "& \" < >";
+ auto T = Template::createTemplate(
+ "These characters should not be HTML escaped: {{&.}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
+}
+
+TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
+ // Integers should interpolate seamlessly.
+ Value D = 85;
+ auto T = Template::createTemplate("{{.}} miles an hour!\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("85 miles an hour!\n", Out);
+}
+
TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
+ // Interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
auto T = Template::createTemplate("| {{string}} |");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
EXPECT_EQ("| --- |", Out);
}
+TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
+ // Interpolation should not alter surrounding whitespace.
+ Value D = Object{{"string", "---"}};
+ auto T = Template::createTemplate("| {{&string}} |");
+ auto Out = T.render(D);
+ EXPECT_EQ("| --- |", Out);
+}
+
+TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
+ // Standalone interpolation should not alter surrounding whitespace.
+ Value D = Object{{"string", "---"}};
+ auto T = Template::createTemplate(" {{string}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" ---\n", Out);
+}
+
+TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
+ // Standalone interpolation should not alter surrounding whitespace.
+ Value D = Object{{"string", "---"}};
+ auto T = Template::createTemplate(" {{&string}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" ---\n", Out);
+}
+
TEST(MustacheInterpolation, InterpolationWithPadding) {
+ // Superfluous in-tag whitespace should be ignored.
Value D = Object{{"string", "---"}};
auto T = Template::createTemplate("|{{ string }}|");
- auto Out = T.get().render(D);
+ auto Out = T.render(D);
+ EXPECT_EQ("|---|", Out);
+}
+
+TEST(MustacheInterpolation, AmpersandWithPadding) {
+ // Superfluous in-tag whitespace should be ignored.
+ Value D = Object{{"string", "---"}};
+ auto T = Template::createTemplate("|{{& string }}|");
+ auto Out = T.render(D);
+ EXPECT_EQ("|---|", Out);
+}
+
+TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
+ // Superfluous in-tag whitespace should be ignored.
+ Value D = Object{{"string", "---"}};
+ auto T = Template::createTemplate("|{{ string \n\n\n }}|");
+ auto Out = T.render(D);
EXPECT_EQ("|---|", Out);
}
+
+TEST(MustacheSections, Truthy) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(
+ "{{#boolean}}This should be rendered.{{/boolean}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("This should be rendered.", Out);
+}
+
+TEST(MustacheSections, Falsey) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(
+ "{{#boolean}}This should not be rendered.{{/boolean}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheSections, NullIsFalsey) {
+ Value D = Object{{"null", nullptr}};
+ auto T = Template::createTemplate(
+ "{{#null}}This should not be rendered.{{/null}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheSections, Context) {
+ Value D = Object{{"context", Object{{"name", "Joe"}}}};
+ auto T = Template::createTemplate("{{#context}}Hi {{name}}.{{/context}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("Hi Joe.", Out);
+}
+
+TEST(MustacheSections, ParentContexts) {
+ Value D = Object{{"a", "foo"},
+ {"b", "wrong"},
+ {"sec", Object{{"b", "bar"}}},
+ {"c", Object{{"d", "baz"}}}};
+ auto T = Template::createTemplate("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("foo, bar, baz", Out);
+}
+
+TEST(MustacheSections, VariableTest) {
+ Value D = Object{{"foo", "bar"}};
+ auto T = Template::createTemplate("{{#foo}}{{.}} is {{foo}}{{/foo}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("bar is bar", Out);
+}
+
+TEST(MustacheSections, ListContexts) {
+ Value D = Object{
+ {"tops",
+ Array{Object{
+ {"tname", Object{{"upper", "A"}, {"lower", "a"}}},
+ {"middles",
+ Array{Object{{"mname", "1"},
+ {"bottoms", Array{Object{{"bname", "x"}},
+ Object{{"bname", "y"}}}}}}}}}}};
+ auto T = Template::createTemplate("{{#tops}}"
+ "{{#middles}}"
+ "{{tname.lower}}{{mname}}."
+ "{{#bottoms}}"
+ "{{tname.upper}}{{mname}}{{bname}}."
+ "{{/bottoms}}"
+ "{{/middles}}"
+ "{{/tops}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("a1.A1x.A1y.", Out);
+}
+
+TEST(MustacheSections, DeeplyNestedContexts) {
+ Value D = Object{
+ {"a", Object{{"one", 1}}},
+ {"b", Object{{"two", 2}}},
+ {"c", Object{{"three", 3}, {"d", Object{{"four", 4}, {"five", 5}}}}}};
+ auto T = Template::createTemplate(
+ "{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{"
+ "three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{"
+ "{two}}{{one}}\n{{#five}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}"
+ "}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{.}}6{{.}}{{"
+ "four}}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{"
+ "four}}{{three}}{{two}}{{one}}\n{{/"
+ "five}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/"
+ "d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/"
+ "c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("1\n121\n12321\n1234321\n123454321\n12345654321\n123454321\n1234321"
+ "\n12321\n121\n1\n",
+ Out);
+}
+
+TEST(MustacheSections, List) {
+ Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
+ Object{{"item", 3}}}}};
+ auto T = Template::createTemplate("{{#list}}{{item}}{{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("123", Out);
+}
+
+TEST(MustacheSections, EmptyList) {
+ Value D = Object{{"list", Array{}}};
+ auto T = Template::createTemplate("{{#list}}Yay lists!{{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheSections, Doubled) {
+ Value D = Object{{"bool", true}, {"two", "second"}};
+ auto T = Template::createTemplate("{{#bool}}\n* first\n{{/bool}}\n* "
+ "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("* first\n* second\n* third\n", Out);
+}
+
+TEST(MustacheSections, NestedTruthy) {
+ Value D = Object{{"bool", true}};
+ auto T = Template::createTemplate(
+ "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+ auto Out = T.render(D);
+ EXPECT_EQ("| A B C D E |", Out);
+}
+
+TEST(MustacheSections, NestedFalsey) {
+ Value D = Object{{"bool", false}};
+ auto T = Template::createTemplate(
+ "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+ auto Out = T.render(D);
+ EXPECT_EQ("| A E |", Out);
+}
+
+TEST(MustacheSections, ContextMisses) {
+ Value D = Object{};
+ auto T = Template::createTemplate(
+ "[{{#missing}}Found key 'missing'!{{/missing}}]");
+ auto Out = T.render(D);
+ EXPECT_EQ("[]", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorString) {
+ Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};
+ auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("(a)(b)(c)(d)(e)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorInteger) {
+ Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};
+ auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("(1)(2)(3)(4)(5)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorArray) {
+ Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};
+ auto T = Template::createTemplate("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("(123)(abc)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {
+ Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
+ auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("(&)(")(<)(>)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorAmpersand) {
+ Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
+ auto T = Template::createTemplate("{{#list}}({{&.}}){{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("(&)(\")(<)(>)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorRootLevel) {
+ Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};
+ auto T = Template::createTemplate("{{#.}}({{value}}){{/.}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("(a)(b)", Out);
+}
+
+TEST(MustacheSections, DottedNamesTruthy) {
+ Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
+ auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == Here");
+ auto Out = T.render(D);
+ EXPECT_EQ("Here == Here", Out);
+}
+
+TEST(MustacheSections, DottedNamesFalsey) {
+ Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
+ auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+ auto Out = T.render(D);
+ EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheSections, DottedNamesBrokenChains) {
+ Value D = Object{{"a", Object{}}};
+ auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+ auto Out = T.render(D);
+ EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheSections, SurroundingWhitespace) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(" | {{#boolean}}\t|\t{{/boolean}} | \n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" | \t|\t | \n", Out);
+}
+
+TEST(MustacheSections, InternalWhitespace) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(
+ " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" | \n | \n", Out);
+}
+
+TEST(MustacheSections, IndentedInlineSections) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(
+ " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" YES\n GOOD\n", Out);
+}
+
+TEST(MustacheSections, StandaloneLines) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(
+ "| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheSections, IndentedStandaloneLines) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(
+ "| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheSections, StandaloneLineEndings) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
+ auto Out = T.render(D);
+ EXPECT_EQ("|\r\n|", Out);
+}
+
+TEST(MustacheSections, StandaloneWithoutPreviousLine) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(" {{#boolean}}\n#{{/boolean}}\n/");
+ auto Out = T.render(D);
+ EXPECT_EQ("#\n/", Out);
+}
+
+TEST(MustacheSections, StandaloneWithoutNewline) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate("#{{#boolean}}\n/\n {{/boolean}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("#\n/\n", Out);
+}
+
+TEST(MustacheSections, Padding) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate("|{{# boolean }}={{/ boolean }}|");
+ auto Out = T.render(D);
+ EXPECT_EQ("|=|", Out);
+}
+
+TEST(MustacheInvertedSections, Falsey) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(
+ "{{^boolean}}This should be rendered.{{/boolean}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("This should be rendered.", Out);
+}
+
+TEST(MustacheInvertedSections, Truthy) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate(
+ "{{^boolean}}This should not be rendered.{{/boolean}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInvertedSections, NullIsFalsey) {
+ Value D = Object{{"null", nullptr}};
+ auto T =
+ Template::createTemplate("{{^null}}This should be rendered.{{/null}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("This should be rendered.", Out);
+}
+
+TEST(MustacheInvertedSections, Context) {
+ Value D = Object{{"context", Object{{"name", "Joe"}}}};
+ auto T = Template::createTemplate("{{^context}}Hi {{name}}.{{/context}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInvertedSections, List) {
+ Value D = Object{
+ {"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
+ auto T = Template::createTemplate("{{^list}}{{n}}{{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInvertedSections, EmptyList) {
+ Value D = Object{{"list", Array{}}};
+ auto T = Template::createTemplate("{{^list}}Yay lists!{{/list}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("Yay lists!", Out);
+}
+
+TEST(MustacheInvertedSections, Doubled) {
+ Value D = Object{{"bool", false}, {"two", "second"}};
+ auto T = Template::createTemplate("{{^bool}}\n* first\n{{/bool}}\n* "
+ "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("* first\n* second\n* third\n", Out);
+}
+
+TEST(MustacheInvertedSections, NestedFalsey) {
+ Value D = Object{{"bool", false}};
+ auto T = Template::createTemplate(
+ "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+ auto Out = T.render(D);
+ EXPECT_EQ("| A B C D E |", Out);
+}
+
+TEST(MustacheInvertedSections, NestedTruthy) {
+ Value D = Object{{"bool", true}};
+ auto T = Template::createTemplate(
+ "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+ auto Out = T.render(D);
+ EXPECT_EQ("| A E |", Out);
+}
+
+TEST(MustacheInvertedSections, ContextMisses) {
+ Value D = Object{};
+ auto T = Template::createTemplate(
+ "[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
+ auto Out = T.render(D);
+ EXPECT_EQ("[Cannot find key 'missing'!]", Out);
+}
+
+TEST(MustacheInvertedSections, DottedNamesTruthy) {
+ Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
+ auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == ");
+ auto Out = T.render(D);
+ EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheInvertedSections, DottedNamesFalsey) {
+ Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
+ auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+ auto Out = T.render(D);
+ EXPECT_EQ("Not Here == Not Here", Out);
+}
+
+TEST(MustacheInvertedSections, DottedNamesBrokenChains) {
+ Value D = Object{{"a", Object{}}};
+ auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+ auto Out = T.render(D);
+ EXPECT_EQ("Not Here == Not Here", Out);
+}
+
+TEST(MustacheInvertedSections, SurroundingWhitespace) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(" | {{^boolean}}\t|\t{{/boolean}} | \n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" | \t|\t | \n", Out);
+}
+
+TEST(MustacheInvertedSections, InternalWhitespace) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(
+ " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" | \n | \n", Out);
+}
+
+TEST(MustacheInvertedSections, IndentedInlineSections) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(
+ " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" NO\n WAY\n", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneLines) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(
+ "| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneIndentedLines) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(
+ "| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneLineEndings) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
+ auto Out = T.render(D);
+ EXPECT_EQ("|\r\n|", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate(" {{^boolean}}\n^{{/boolean}}\n/");
+ auto Out = T.render(D);
+ EXPECT_EQ("^\n/", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneWithoutNewline) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate("^{{^boolean}}\n/\n {{/boolean}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("^\n/\n", Out);
+}
+
+TEST(MustacheInvertedSections, Padding) {
+ Value D = Object{{"boolean", false}};
+ auto T = Template::createTemplate("|{{^ boolean }}={{/ boolean }}|");
+ auto Out = T.render(D);
+ EXPECT_EQ("|=|", Out);
+}
+
+TEST(MustachePartials, BasicBehavior) {
+ Value D = Object{};
+ auto T = Template::createTemplate("{{>text}}");
+ T.registerPartial("text", "from partial");
+ auto Out = T.render(D);
+ EXPECT_EQ("from partial", Out);
+}
+
+TEST(MustachePartials, FailedLookup) {
+ Value D = Object{};
+ auto T = Template::createTemplate("{{>text}}");
+ auto Out = T.render(D);
+ EXPECT_EQ("", Out);
+}
+
+TEST(MustachePartials, Context) {
+ Value D = Object{{"text", "content"}};
+ auto T = Template::createTemplate("{{>partial}}");
+ T.registerPartial("partial", "*{{text}}*");
+ auto Out = T.render(D);
+ EXPECT_EQ("*content*", Out);
+}
+
+TEST(MustachePartials, Recursion) {
+ Value D =
+ Object{{"content", "X"},
+ {"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
+ auto T = Template::createTemplate("{{>node}}");
+ T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
+ auto Out = T.render(D);
+ EXPECT_EQ("X(Y())", Out);
+}
+
+TEST(MustachePartials, Nested) {
+ Value D = Object{{"a", "hello"}, {"b", "world"}};
+ auto T = Template::createTemplate("{{>outer}}");
+ T.registerPartial("outer", "*{{a}} {{>inner}}*");
+ T.registerPartial("inner", "{{b}}!");
+ auto Out = T.render(D);
+ EXPECT_EQ("*hello world!*", Out);
+}
+
+TEST(MustachePartials, SurroundingWhitespace) {
+ Value D = Object{};
+ auto T = Template::createTemplate("| {{>partial}} |");
+ T.registerPartial("partial", "\t|\t");
+ auto Out = T.render(D);
+ EXPECT_EQ("| \t|\t |", Out);
+}
+
+TEST(MustachePartials, InlineIndentation) {
+ Value D = Object{{"data", "|"}};
+ auto T = Template::createTemplate(" {{data}} {{> partial}}\n");
+ T.registerPartial("partial", "(\n(");
+ auto Out = T.render(D);
+ EXPECT_EQ(" | (\n(\n", Out);
+}
+
+TEST(MustachePartials, PaddingWhitespace) {
+ Value D = Object{{"boolean", true}};
+ auto T = Template::createTemplate("|{{> partial }}|");
+ T.registerPartial("partial", "[]");
+ auto Out = T.render(D);
+ EXPECT_EQ("|[]|", Out);
+}
+
+TEST(MustacheLambdas, BasicInterpolation) {
+ Value D = Object{};
+ auto T = Template::createTemplate("Hello, {{lambda}}!");
+ Lambda L = []() -> llvm::SmallString<128> {
+ llvm::SmallString<128> Result("World");
+ return Result;
+ };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("Hello, World!", Out);
+}
+
+TEST(MustacheLambdas, InterpolationExpansion) {
+ Value D = Object{{"planet", "World"}};
+ auto T = Template::createTemplate("Hello, {{lambda}}!");
+ Lambda L = []() -> llvm::SmallString<128> {
+ return llvm::SmallString<128>("{{planet}}");
+ };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("Hello, World!", Out);
+}
+
+TEST(MustacheLambdas, BasicMultipleCalls) {
+ Value D = Object{};
+ auto T = Template::createTemplate("{{lambda}} == {{lambda}} == {{lambda}}");
+ int I = 0;
+ Lambda L = [&I]() -> llvm::json::Value {
+ I += 1;
+ return I;
+ };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("1 == 2 == 3", Out);
+}
+
+TEST(MustacheLambdas, Escaping) {
+ Value D = Object{};
+ auto T = Template::createTemplate("<{{lambda}}{{&lambda}}");
+ Lambda L = []() -> llvm::json::Value { return ">"; };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("<>>", Out);
+}
+
+TEST(MustacheLambdas, Sections) {
+ Value D = Object{};
+ auto T = Template::createTemplate("<{{#lambda}}{{x}}{{/lambda}}>");
+ SectionLambda L = [](StringRef Text) -> llvm::json::Value {
+ if (Text == "{{x}}") {
+ return "yes";
+ }
+ return "no";
+ };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("<yes>", Out);
+}
+
+TEST(MustacheLambdas, SectionExpansion) {
+ Value D = Object{
+ {"planet", "Earth"},
+ };
+ auto T = Template::createTemplate("<{{#lambda}}-{{/lambda}}>");
+ SectionLambda L = [](StringRef Text) -> llvm::json::Value {
+ SmallString<128> Result;
+ Result += Text;
+ Result += "{{planet}}";
+ Result += Text;
+ return Result;
+ };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("<-Earth->", Out);
+}
+
+TEST(MustacheLambdas, SectionsMultipleCalls) {
+ Value D = Object{};
+ auto T = Template::createTemplate(
+ "{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}");
+ SectionLambda L = [](StringRef Text) -> llvm::json::Value {
+ SmallString<128> Result;
+ Result += "__";
+ Result += Text;
+ Result += "__";
+ return Result;
+ };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("__FILE__ != __LINE__", Out);
+}
+
+TEST(MustacheLambdas, InvertedSections) {
+ Value D = Object{{"static", "static"}};
+ auto T = Template::createTemplate("<{{^lambda}}{{static}}{{/lambda}}>");
+ SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
+ T.registerLambda("lambda", L);
+ auto Out = T.render(D);
+ EXPECT_EQ("<>", Out);
+}
+
+TEST(MustacheComments, Inline) {
+ // Comment blocks should be removed from the template.
+ Value D = {};
+ auto T = Template::createTemplate("12345{{! Comment Block! }}67890");
+ auto Out = T.render(D);
+ EXPECT_EQ("1234567890", Out);
+}
+
+TEST(MustacheComments, Multiline) {
+ // Multiline comments should be permitted.
+ Value D = {};
+ auto T = Template::createTemplate(
+ "12345{{!\n This is a\n multi-line comment...\n}}67890\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("1234567890\n", Out);
+}
+
+TEST(MustacheComments, Standalone) {
+ // All standalone comment lines should be removed.
+ Value D = {};
+ auto T = Template::createTemplate("Begin.\n{{! Comment Block! }}\nEnd.\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, IndentedStandalone) {
+ // All standalone comment lines should be removed.
+ Value D = {};
+ auto T = Template::createTemplate(
+ "Begin.\n {{! Indented Comment Block! }}\nEnd.\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, StandaloneLineEndings) {
+ // "\r\n" should be considered a newline for standalone tags.
+ Value D = {};
+ auto T = Template::createTemplate("|\r\n{{! Standalone Comment }}\r\n|");
+ auto Out = T.render(D);
+ EXPECT_EQ("|\r\n|", Out);
+}
+
+TEST(MustacheComments, StandaloneWithoutPreviousLine) {
+ // Standalone tags should not require a newline to precede them.
+ Value D = {};
+ auto T = Template::createTemplate(" {{! I'm Still Standalone }}\n!");
+ auto Out = T.render(D);
+ EXPECT_EQ("!", Out);
+}
+
+TEST(MustacheComments, StandaloneWithoutNewline) {
+ // Standalone tags should not require a newline to follow them.
+ Value D = {};
+ auto T = Template::createTemplate("!\n {{! I'm Still Standalone }}");
+ auto Out = T.render(D);
+ EXPECT_EQ("!\n", Out);
+}
+
+TEST(MustacheComments, MultilineStandalone) {
+ // All standalone comment lines should be removed.
+ Value D = {};
+ auto T = Template::createTemplate(
+ "Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, IndentedMultilineStandalone) {
+ // All standalone comment lines should be removed.
+ Value D = {};
+ auto T = Template::createTemplate(
+ "Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n");
+ auto Out = T.render(D);
+ EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, IndentedInline) {
+ // Inline comments should not strip whitespace.
+ Value D = {};
+ auto T = Template::createTemplate(" 12 {{! 34 }}\n");
+ auto Out = T.render(D);
+ EXPECT_EQ(" 12 \n", Out);
+}
+
+TEST(MustacheComments, SurroundingWhitespace) {
+ // Comment removal should preserve surrounding whitespace.
+ Value D = {};
+ auto T = Template::createTemplate("12345 {{! Comment Block! }} 67890");
+ auto Out = T.render(D);
+ EXPECT_EQ("12345 67890", Out);
+}
+
+TEST(MustacheComments, VariableNameCollision) {
+ // Comments must never render, even if a variable with the same name exists.
+ Value D = Object{
+ {"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};
+ auto T = Template::createTemplate("comments never show: >{{! comment }}<");
+ auto Out = T.render(D);
+ EXPECT_EQ("comments never show: ><", Out);
+}
\ No newline at end of file
>From 6e893c07ca49773825ddcb6d99107b96b37d5540 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 15:51:33 -0400
Subject: [PATCH 12/62] [llvm][support] fix mustache test
---
llvm/lib/Support/Mustache.cpp | 11 ++++++-----
llvm/unittests/Support/MustacheTest.cpp | 4 ++--
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 80b3d8bc53651b..09adba4d6239ee 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -137,16 +137,16 @@ std::vector<Token> tokenize(StringRef Template) {
if (I > 0 && Tokens[I - 1].getType() == Token::Type::Text &&
RequiresCleanUp) {
Token &PrevToken = Tokens[I - 1];
- StringRef TokenBody = PrevToken.getTokenBody().rtrim(" \t\v\t");
+ StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
- TokenBody.empty()) {
+ (TokenBody.empty() && I == 1)) {
NoTextBehind = true;
}
}
if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
RequiresCleanUp) {
Token &NextToken = Tokens[I + 1];
- StringRef TokenBody = NextToken.getTokenBody().ltrim(" ");
+ StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n")) {
NoTextAhead = true;
}
@@ -363,8 +363,9 @@ ASTNode::render(Value Data,
StringRef LambdaStr = printJson(LambdaResult);
Parser P = Parser(LambdaStr);
std::shared_ptr<ASTNode> LambdaNode = P.parse();
- return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
- Escapes);
+ SmallString<128> RenderStr =
+ LambdaNode->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+ return escapeString(RenderStr, Escapes);
}
return escapeString(printJson(Context), Escapes);
}
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index ceee8942f88f4e..dfd28b0b5b2377 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -768,9 +768,9 @@ TEST(MustachePartials, SurroundingWhitespace) {
TEST(MustachePartials, InlineIndentation) {
Value D = Object{{"data", "|"}};
auto T = Template::createTemplate(" {{data}} {{> partial}}\n");
- T.registerPartial("partial", "(\n(");
+ T.registerPartial("partial", "<\n<");
auto Out = T.render(D);
- EXPECT_EQ(" | (\n(\n", Out);
+ EXPECT_EQ(" | <\n<\n", Out);
}
TEST(MustachePartials, PaddingWhitespace) {
>From 976593eff073b8741b6e3d1e4d7bcf648f6983d8 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 16:39:30 -0400
Subject: [PATCH 13/62] [llvm][support] add comments
---
llvm/include/llvm/Support/Mustache.h | 51 +++++++++++++++++--
llvm/lib/Support/Mustache.cpp | 75 +++++++++-------------------
2 files changed, 71 insertions(+), 55 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index cb7d80e47809e8..f5714c19089348 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -7,8 +7,50 @@
//===----------------------------------------------------------------------===//
//
// Implementation of the Mustache templating language supports version 1.4.2
+// currently relies on llvm::json::Value for data input
+// see the Mustache spec for more information
// (https://mustache.github.io/mustache.5.html).
//
+// Current Features Supported:
+// - Variables
+// - Sections
+// - Inverted Sections
+// - Partials
+// - Comments
+// - Lambdas
+// - Unescaped Variables
+//
+// Features Not Supported:
+// - Set Delimiter
+// - Blocks
+// - Parents
+// - Dynamic Names
+//
+// Usage:
+// - Creating a simple template and rendering it:
+// \code
+// auto Template = Template::createTemplate("Hello, {{name}}!");
+// Value Data = {{"name", "World"}};
+// StringRef Rendered = Template.render(Data);
+// // Rendered == "Hello, World!"
+// \endcode
+// - Creating a template with a partial and rendering it:
+// \code
+// auto Template = Template::createTemplate("{{>partial}}");
+// Template.registerPartial("partial", "Hello, {{name}}!");
+// Value Data = {{"name", "World"}};
+// StringRef Rendered = Template.render(Data);
+// // Rendered == "Hello, World!"
+// \endcode
+// - Creating a template with a lambda and rendering it:
+// \code
+// auto Template = Template::createTemplate("{{#lambda}}Hello,
+// {{name}}!{{/lambda}}");
+// Template.registerLambda("lambda", []() { return true; });
+// Value Data = {{"name", "World"}};
+// StringRef Rendered = Template.render(Data);
+// // Rendered == "Hello, World!"
+// \endcode
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_MUSTACHE
@@ -17,8 +59,6 @@
#include "Error.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/JSON.h"
-#include <string>
-#include <variant>
#include <vector>
namespace llvm {
@@ -96,8 +136,6 @@ class ASTNode {
void setRawBody(StringRef NewBody) { RawBody = NewBody; };
- SmallString<128> getRawBody() const { return RawBody; };
-
std::shared_ptr<ASTNode> getLastChild() const {
return Children.empty() ? nullptr : Children.back();
};
@@ -120,6 +158,8 @@ class ASTNode {
llvm::json::Value LocalContext;
};
+// A Template represents the container for the AST and the partials
+// and Lambdas that are registered with it.
class Template {
public:
static Template createTemplate(StringRef TemplateStr);
@@ -132,6 +172,9 @@ class Template {
void registerLambda(StringRef Name, SectionLambda Lambda);
+ // By default the Mustache Spec Specifies that HTML special characters
+ // should be escaped. This function allows the user to specify which
+ // characters should be escaped.
void registerEscape(DenseMap<char, StringRef> Escapes);
private:
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 09adba4d6239ee..18a94f9f241c4d 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -8,9 +8,6 @@
#include "llvm/Support/Mustache.h"
#include "llvm/Support/Error.h"
-#include <iostream>
-#include <regex>
-#include <sstream>
using namespace llvm;
using namespace llvm::json;
@@ -20,11 +17,10 @@ SmallString<128> escapeString(StringRef Input,
DenseMap<char, StringRef> &Escape) {
SmallString<128> EscapedString("");
for (char C : Input) {
- if (Escape.find(C) != Escape.end()) {
+ if (Escape.find(C) != Escape.end())
EscapedString += Escape[C];
- } else {
+ else
EscapedString += C;
- }
}
return EscapedString;
}
@@ -52,9 +48,9 @@ Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
return;
StringRef AccessorStr = InnerBody;
- if (TokenType != Type::Variable) {
+ if (TokenType != Type::Variable)
AccessorStr = InnerBody.substr(1);
- }
+
Accessor = split(AccessorStr.trim(), '.');
}
@@ -118,9 +114,8 @@ std::vector<Token> tokenize(StringRef Template) {
DelimiterStart = Template.find(Open, Start);
}
- if (Start < Template.size()) {
+ if (Start < Template.size())
Tokens.push_back(Token(Template.substr(Start)));
- }
// fix up white spaces for
// open sections/inverted sections/close section/comment
@@ -139,38 +134,27 @@ std::vector<Token> tokenize(StringRef Template) {
Token &PrevToken = Tokens[I - 1];
StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
- (TokenBody.empty() && I == 1)) {
+ (TokenBody.empty() && I == 1))
NoTextBehind = true;
- }
}
if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
RequiresCleanUp) {
Token &NextToken = Tokens[I + 1];
StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
- if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n")) {
+ if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n"))
NoTextAhead = true;
- }
}
- if (NoTextBehind && NoTextAhead) {
- Token &PrevToken = Tokens[I - 1];
- Token &NextToken = Tokens[I + 1];
- StringRef NextTokenBody = NextToken.getTokenBody();
- PrevToken.setTokenBody(PrevToken.getTokenBody().rtrim(" \t\v\t"));
- if (NextTokenBody.starts_with("\r\n")) {
- NextToken.setTokenBody(NextTokenBody.substr(2));
- } else if (NextToken.getTokenBody().starts_with("\n")) {
- NextToken.setTokenBody(NextTokenBody.substr(1));
- }
- } else if (NoTextAhead && I == 0) {
+ if ((NoTextBehind && NoTextAhead) || (NoTextAhead && I == 0)) {
Token &NextToken = Tokens[I + 1];
StringRef NextTokenBody = NextToken.getTokenBody();
- if (NextTokenBody.starts_with("\r\n")) {
+ if (NextTokenBody.starts_with("\r\n"))
NextToken.setTokenBody(NextTokenBody.substr(2));
- } else if (NextToken.getTokenBody().starts_with("\n")) {
+ else if (NextToken.getTokenBody().starts_with("\n"))
NextToken.setTokenBody(NextTokenBody.substr(1));
- }
- } else if (NoTextBehind && I == Tokens.size() - 1) {
+ }
+ if ((NoTextBehind && NoTextAhead) ||
+ (NoTextBehind && I == Tokens.size() - 1)) {
Token &PrevToken = Tokens[I - 1];
StringRef PrevTokenBody = PrevToken.getTokenBody();
PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
@@ -239,9 +223,8 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
std::size_t End = CurrentPtr;
SmallString<128> RawBody;
if (Start + 1 < End - 1)
- for (std::size_t I = Start + 1; I < End - 1; I++) {
+ for (std::size_t I = Start + 1; I < End - 1; I++)
RawBody += Tokens[I].getRawBody();
- }
else if (Start + 1 == End - 1)
RawBody = Tokens[Start].getRawBody();
CurrentNode->setRawBody(RawBody);
@@ -256,18 +239,16 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
std::size_t End = CurrentPtr;
SmallString<128> RawBody;
if (Start + 1 < End - 1)
- for (std::size_t I = Start + 1; I < End - 1; I++) {
+ for (std::size_t I = Start + 1; I < End - 1; I++)
RawBody += Tokens[I].getRawBody();
- }
else if (Start + 1 == End - 1)
RawBody = Tokens[Start].getRawBody();
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
break;
}
- case Token::Type::SectionClose: {
+ case Token::Type::SectionClose:
return;
- }
default:
break;
}
@@ -309,14 +290,11 @@ void Template::registerEscape(DenseMap<char, StringRef> E) { Escapes = E; }
SmallString<128> printJson(Value &Data) {
SmallString<128> Result;
- if (Data.getAsNull()) {
+ if (Data.getAsNull())
return Result;
- }
- if (auto *Arr = Data.getAsArray()) {
- if (Arr->empty()) {
+ if (auto *Arr = Data.getAsArray())
+ if (Arr->empty())
return Result;
- }
- }
if (Data.getAsString()) {
Result += Data.getAsString()->str();
return Result;
@@ -436,12 +414,10 @@ Value ASTNode::findContext() {
// We attempt to find the JSON context in the current node if it is not found
// we traverse the parent nodes to find the context until we reach the root
// node or the context is found
- if (Accessor.empty()) {
+ if (Accessor.empty())
return nullptr;
- }
- if (Accessor[0] == ".") {
+ if (Accessor[0] == ".")
return LocalContext;
- }
json::Object *CurrentContext = LocalContext.getAsObject();
SmallString<128> CurrentAccessor = Accessor[0];
std::weak_ptr<ASTNode> CurrentParent = Parent;
@@ -458,17 +434,14 @@ Value ASTNode::findContext() {
for (std::size_t I = 0; I < Accessor.size(); I++) {
CurrentAccessor = Accessor[I];
Value *CurrentValue = CurrentContext->get(CurrentAccessor);
- if (!CurrentValue) {
+ if (!CurrentValue)
return nullptr;
- }
if (I < Accessor.size() - 1) {
CurrentContext = CurrentValue->getAsObject();
- if (!CurrentContext) {
+ if (!CurrentContext)
return nullptr;
- }
- } else {
+ } else
Context = *CurrentValue;
- }
}
return Context;
}
>From 6a60eabf0e28e1a43d0943b5952f2eb5865cb5b7 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:21:22 -0400
Subject: [PATCH 14/62] [llvm][support] add comments
---
llvm/include/llvm/Support/Mustache.h | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index f5714c19089348..03892ea5679ca0 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -27,23 +27,21 @@
// - Dynamic Names
//
// Usage:
-// - Creating a simple template and rendering it:
// \code
+// // Creating a simple template and rendering it
// auto Template = Template::createTemplate("Hello, {{name}}!");
// Value Data = {{"name", "World"}};
// StringRef Rendered = Template.render(Data);
// // Rendered == "Hello, World!"
-// \endcode
-// - Creating a template with a partial and rendering it:
-// \code
+//
+// // Creating a template with a partial and rendering it
// auto Template = Template::createTemplate("{{>partial}}");
// Template.registerPartial("partial", "Hello, {{name}}!");
// Value Data = {{"name", "World"}};
// StringRef Rendered = Template.render(Data);
// // Rendered == "Hello, World!"
-// \endcode
-// - Creating a template with a lambda and rendering it:
-// \code
+//
+// // Creating a template with a lambda and rendering it
// auto Template = Template::createTemplate("{{#lambda}}Hello,
// {{name}}!{{/lambda}}");
// Template.registerLambda("lambda", []() { return true; });
>From 6a1fcc8f0f4c40a35d4afff7ebd66c2c99b356c1 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:25:06 -0400
Subject: [PATCH 15/62] [llvm][support] use enumerate for loop
---
llvm/lib/Support/Mustache.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 18a94f9f241c4d..ad5d30d335f626 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -431,8 +431,7 @@ Value ASTNode::findContext() {
return nullptr;
}
Value Context = nullptr;
- for (std::size_t I = 0; I < Accessor.size(); I++) {
- CurrentAccessor = Accessor[I];
+ for (auto CurrentAccessor : Accessor) {
Value *CurrentValue = CurrentContext->get(CurrentAccessor);
if (!CurrentValue)
return nullptr;
>From eb1e1a66382551ed2d65c37a2a6fc307051904a2 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:34:43 -0400
Subject: [PATCH 16/62] [llvm][support] clang-format
---
llvm/include/llvm/Support/Mustache.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 03892ea5679ca0..db09089707167d 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -114,15 +114,15 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), LocalContext(nullptr){};
+ ASTNode() : T(Type::Root), LocalContext(nullptr) {};
ASTNode(StringRef Body, std::shared_ptr<ASTNode> Parent)
- : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
+ : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
: T(T), Accessor(Accessor), Parent(Parent), LocalContext(nullptr),
- Children({}){};
+ Children({}) {};
void addChild(std::shared_ptr<ASTNode> Child) {
Children.emplace_back(Child);
>From f4b052084bf41dd109213af38622fc5df581691f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:43:01 -0400
Subject: [PATCH 17/62] [llvm][support] fix mustache test
---
llvm/unittests/Support/MustacheTest.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index dfd28b0b5b2377..c38a2ed2823379 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -139,9 +139,9 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
Object{{"c",
Object{{"d",
Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
- auto T = Template::createTemplate("{{a.b.c.d.e.name}} == Phil");
+ auto T = Template::createTemplate("{{a.b.c.d.e.name}}");
auto Out = T.render(D);
- EXPECT_EQ("Phil == Phil", Out);
+ EXPECT_EQ("Phil", Out);
}
TEST(MustacheInterpolation, DottedNamesBrokenChains) {
@@ -171,9 +171,9 @@ TEST(MustacheInterpolation, DottedNamesInitialResolution) {
Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},
{"b",
Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
- auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}} == Phil");
+ auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}}");
auto Out = T.render(D);
- EXPECT_EQ("Phil == Phil", Out);
+ EXPECT_EQ("Phil", Out);
}
TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
>From 7ffaeec649728e21687e81fc3c479def997b1d83 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:50:37 -0400
Subject: [PATCH 18/62] [llvm][support] clang-format
---
llvm/include/llvm/Support/Mustache.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index db09089707167d..afde8d995cc147 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -176,7 +176,7 @@ class Template {
void registerEscape(DenseMap<char, StringRef> Escapes);
private:
- Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree){};
+ Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree) {};
DenseMap<StringRef, std::shared_ptr<ASTNode>> Partials;
DenseMap<StringRef, Lambda> Lambdas;
DenseMap<StringRef, SectionLambda> SectionLambdas;
>From 746fb97f94d3533b84ff674f9f7140fdc1ef5522 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 18:22:52 -0400
Subject: [PATCH 19/62] [llvm][support] use llvm enumerate
---
llvm/lib/Support/Mustache.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index ad5d30d335f626..f0fb6767f6fcd5 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -431,11 +431,11 @@ Value ASTNode::findContext() {
return nullptr;
}
Value Context = nullptr;
- for (auto CurrentAccessor : Accessor) {
- Value *CurrentValue = CurrentContext->get(CurrentAccessor);
+ for (auto E : enumerate(Accessor)) {
+ Value *CurrentValue = CurrentContext->get(E.value());
if (!CurrentValue)
return nullptr;
- if (I < Accessor.size() - 1) {
+ if (E.index() < Accessor.size() - 1) {
CurrentContext = CurrentValue->getAsObject();
if (!CurrentContext)
return nullptr;
>From bcc86fe0ad6f55a27c3fe50231da8d152517ac17 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 11 Sep 2024 18:41:38 -0400
Subject: [PATCH 20/62] [llvm][support] fix unittest
---
llvm/include/llvm/Support/Mustache.h | 4 ++--
llvm/lib/Support/Mustache.cpp | 7 ++++---
llvm/unittests/Support/MustacheTest.cpp | 10 +++++-----
3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index afde8d995cc147..6243e29f52282d 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -121,8 +121,8 @@ class ASTNode {
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
- : T(T), Accessor(Accessor), Parent(Parent), LocalContext(nullptr),
- Children({}) {};
+ : T(T), Parent(Parent), Children({}), Accessor(Accessor),
+ LocalContext(nullptr) {};
void addChild(std::shared_ptr<ASTNode> Child) {
Children.emplace_back(Child);
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index f0fb6767f6fcd5..7372a248d209bc 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -55,7 +55,7 @@ Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
}
Token::Token(StringRef Str)
- : RawBody(Str), TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+ : TokenType(Type::Text), RawBody(Str), Accessor({}), TokenBody(Str) {}
Token::Type Token::getTokenType(char Identifier) {
switch (Identifier) {
@@ -256,7 +256,7 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
}
Template Template::createTemplate(StringRef TemplateStr) {
- Parser P = Parser(TemplateStr);
+ Parser P = Parser(TemplateStr.str());
std::shared_ptr<ASTNode> MustacheTree = P.parse();
Template T = Template(MustacheTree);
// the default behaviour is to escape html entities
@@ -333,6 +333,7 @@ ASTNode::render(Value Data,
Partial->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
return Result;
}
+ return Result;
}
case Variable: {
if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
@@ -373,7 +374,7 @@ ASTNode::render(Value Data,
if (isFalsey(Return))
return Result;
StringRef LambdaStr = printJson(Return);
- Parser P = Parser(LambdaStr);
+ Parser P = Parser(LambdaStr.str());
std::shared_ptr<ASTNode> LambdaNode = P.parse();
return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
Escapes);
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index c38a2ed2823379..33cbad56240faf 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/Mustache.h"
+#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
using namespace llvm;
@@ -784,9 +785,8 @@ TEST(MustachePartials, PaddingWhitespace) {
TEST(MustacheLambdas, BasicInterpolation) {
Value D = Object{};
auto T = Template::createTemplate("Hello, {{lambda}}!");
- Lambda L = []() -> llvm::SmallString<128> {
- llvm::SmallString<128> Result("World");
- return Result;
+ Lambda L = []() -> llvm::json::Value {
+ return "World";
};
T.registerLambda("lambda", L);
auto Out = T.render(D);
@@ -796,8 +796,8 @@ TEST(MustacheLambdas, BasicInterpolation) {
TEST(MustacheLambdas, InterpolationExpansion) {
Value D = Object{{"planet", "World"}};
auto T = Template::createTemplate("Hello, {{lambda}}!");
- Lambda L = []() -> llvm::SmallString<128> {
- return llvm::SmallString<128>("{{planet}}");
+ Lambda L = []() -> llvm::json::Value {
+ return "{{planet}}";
};
T.registerLambda("lambda", L);
auto Out = T.render(D);
>From 95ad7a6a0b128128b608606bd055f386d8162a5b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 11 Sep 2024 18:50:01 -0400
Subject: [PATCH 21/62] [llvm][support] clang-format
---
llvm/unittests/Support/MustacheTest.cpp | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index 33cbad56240faf..3f3f04a9b55b13 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -785,9 +785,7 @@ TEST(MustachePartials, PaddingWhitespace) {
TEST(MustacheLambdas, BasicInterpolation) {
Value D = Object{};
auto T = Template::createTemplate("Hello, {{lambda}}!");
- Lambda L = []() -> llvm::json::Value {
- return "World";
- };
+ Lambda L = []() -> llvm::json::Value { return "World"; };
T.registerLambda("lambda", L);
auto Out = T.render(D);
EXPECT_EQ("Hello, World!", Out);
@@ -796,9 +794,7 @@ TEST(MustacheLambdas, BasicInterpolation) {
TEST(MustacheLambdas, InterpolationExpansion) {
Value D = Object{{"planet", "World"}};
auto T = Template::createTemplate("Hello, {{lambda}}!");
- Lambda L = []() -> llvm::json::Value {
- return "{{planet}}";
- };
+ Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
T.registerLambda("lambda", L);
auto Out = T.render(D);
EXPECT_EQ("Hello, World!", Out);
>From bb3b1acfedc0776e922c36ddec1ed38b7d9af5cf Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 06:02:49 -0400
Subject: [PATCH 22/62] [llvm][support] address mustache comments
---
llvm/include/llvm/Support/Mustache.h | 59 ++---
llvm/lib/Support/Mustache.cpp | 304 +++++++++++++-----------
llvm/unittests/Support/MustacheTest.cpp | 275 ++++++++++-----------
3 files changed, 316 insertions(+), 322 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 6243e29f52282d..607e33b4ec2c9b 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -26,24 +26,30 @@
// - Parents
// - Dynamic Names
//
+// The Template class is container class outputs the Mustache template string
+// and is main class for users. It stores all the lambdas and the ASTNode Tree.
+// When the Template is instantiated it calls tokenize the Template String into
+// the Token class and calls a basic recursive descent parser to construct the
+// ASTNode Tree. The ASTNodes are all stored in an arena allocator which is
+// freed once the template class goes out of scope
+//
// Usage:
// \code
// // Creating a simple template and rendering it
-// auto Template = Template::createTemplate("Hello, {{name}}!");
+// auto Template = Template("Hello, {{name}}!");
// Value Data = {{"name", "World"}};
// StringRef Rendered = Template.render(Data);
// // Rendered == "Hello, World!"
//
// // Creating a template with a partial and rendering it
-// auto Template = Template::createTemplate("{{>partial}}");
+// auto Template = Template("{{>partial}}");
// Template.registerPartial("partial", "Hello, {{name}}!");
// Value Data = {{"name", "World"}};
// StringRef Rendered = Template.render(Data);
// // Rendered == "Hello, World!"
//
// // Creating a template with a lambda and rendering it
-// auto Template = Template::createTemplate("{{#lambda}}Hello,
-// {{name}}!{{/lambda}}");
+// auto Template = Template("{{#lambda}}Hello, {{name}}!{{/lambda}}");
// Template.registerLambda("lambda", []() { return true; });
// Value Data = {{"name", "World"}};
// StringRef Rendered = Template.render(Data);
@@ -56,13 +62,14 @@
#include "Error.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Allocator.h"
#include "llvm/Support/JSON.h"
#include <vector>
namespace llvm {
namespace mustache {
-using Accessor = std::vector<SmallString<128>>;
+using Accessor = SmallVector<SmallString<128>>;
using Lambda = std::function<llvm::json::Value()>;
using SectionLambda = std::function<llvm::json::Value(StringRef)>;
@@ -114,19 +121,17 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), LocalContext(nullptr) {};
+ ASTNode() : T(Type::Root), LocalContext(nullptr){};
- ASTNode(StringRef Body, std::shared_ptr<ASTNode> Parent)
- : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
+ ASTNode(StringRef Body, ASTNode *Parent)
+ : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
- ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
+ ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- LocalContext(nullptr) {};
+ LocalContext(nullptr){};
- void addChild(std::shared_ptr<ASTNode> Child) {
- Children.emplace_back(Child);
- };
+ void addChild(ASTNode *Child) { Children.emplace_back(Child); };
SmallString<128> getBody() const { return Body; };
@@ -134,24 +139,20 @@ class ASTNode {
void setRawBody(StringRef NewBody) { RawBody = NewBody; };
- std::shared_ptr<ASTNode> getLastChild() const {
- return Children.empty() ? nullptr : Children.back();
- };
-
- SmallString<128>
- render(llvm::json::Value Data,
- DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
- DenseMap<StringRef, Lambda> &Lambdas,
- DenseMap<StringRef, SectionLambda> &SectionLambdas,
- DenseMap<char, StringRef> &Escapes);
+ SmallString<128> render(llvm::json::Value Data,
+ llvm::BumpPtrAllocator &Allocator,
+ DenseMap<StringRef, ASTNode *> &Partials,
+ DenseMap<StringRef, Lambda> &Lambdas,
+ DenseMap<StringRef, SectionLambda> &SectionLambdas,
+ DenseMap<char, StringRef> &Escapes);
private:
llvm::json::Value findContext();
Type T;
SmallString<128> RawBody;
SmallString<128> Body;
- std::weak_ptr<ASTNode> Parent;
- std::vector<std::shared_ptr<ASTNode>> Children;
+ ASTNode *Parent;
+ std::vector<ASTNode *> Children;
const Accessor Accessor;
llvm::json::Value LocalContext;
};
@@ -160,7 +161,7 @@ class ASTNode {
// and Lambdas that are registered with it.
class Template {
public:
- static Template createTemplate(StringRef TemplateStr);
+ Template(StringRef TemplateStr);
SmallString<128> render(llvm::json::Value Data);
@@ -176,12 +177,12 @@ class Template {
void registerEscape(DenseMap<char, StringRef> Escapes);
private:
- Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree) {};
- DenseMap<StringRef, std::shared_ptr<ASTNode>> Partials;
+ DenseMap<StringRef, ASTNode *> Partials;
DenseMap<StringRef, Lambda> Lambdas;
DenseMap<StringRef, SectionLambda> SectionLambdas;
DenseMap<char, StringRef> Escapes;
- std::shared_ptr<ASTNode> Tree;
+ llvm::BumpPtrAllocator Allocator;
+ ASTNode *Tree;
};
} // namespace mustache
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 7372a248d209bc..dd6e5ebb19209c 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -25,31 +25,29 @@ SmallString<128> escapeString(StringRef Input,
return EscapedString;
}
-std::vector<SmallString<128>> split(StringRef Str, char Delimiter) {
- std::vector<SmallString<128>> Tokens;
+Accessor split(StringRef Str, char Delimiter) {
+ Accessor Tokens;
if (Str == ".") {
- Tokens.push_back(Str);
+ Tokens.emplace_back(Str);
return Tokens;
}
StringRef Ref(Str);
while (!Ref.empty()) {
- llvm::StringRef Part;
+ StringRef Part;
std::tie(Part, Ref) = Ref.split(Delimiter);
- Tokens.push_back(Part.trim());
+ Tokens.emplace_back(Part.trim());
}
return Tokens;
}
Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
: RawBody(RawBody), TokenBody(InnerBody) {
-
TokenType = getTokenType(Identifier);
if (TokenType == Type::Comment)
return;
- StringRef AccessorStr = InnerBody;
- if (TokenType != Type::Variable)
- AccessorStr = InnerBody.substr(1);
+ StringRef AccessorStr =
+ TokenType == Type::Variable ? InnerBody : InnerBody.substr(1);
Accessor = split(AccessorStr.trim(), '.');
}
@@ -76,86 +74,106 @@ Token::Type Token::getTokenType(char Identifier) {
}
}
-std::vector<Token> tokenize(StringRef Template) {
- // Simple tokenizer that splits the template into tokens
- // the mustache spec allows {{{ }}} to unescape variables
- // but we don't support that here unescape variable
- // is represented only by {{& variable}}
- std::vector<Token> Tokens;
- SmallString<128> Open("{{");
- SmallString<128> Close("}}");
+// Function to check if there's no meaningful text behind
+bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
+ if (Idx == 0 || Tokens[Idx - 1].getType() != Token::Type::Text)
+ return false;
+ const Token &PrevToken = Tokens[Idx - 1];
+ StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
+ return TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
+ (TokenBody.empty() && Idx == 1);
+}
+// Function to check if there's no meaningful text ahead
+bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
+ if (Idx >= Tokens.size() - 1 ||
+ Tokens[Idx + 1].getType() != Token::Type::Text)
+ return false;
+
+ const Token &NextToken = Tokens[Idx + 1];
+ StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
+ return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
+}
+
+// Simple tokenizer that splits the template into tokens
+// the mustache spec allows {{{ }}} to unescape variables
+// but we don't support that here unescape variable
+// is represented only by {{& variable}}
+SmallVector<Token, 0> tokenize(StringRef Template) {
+ SmallVector<Token, 0> Tokens;
+ StringRef Open("{{");
+ StringRef Close("}}");
std::size_t Start = 0;
std::size_t DelimiterStart = Template.find(Open);
if (DelimiterStart == StringRef::npos) {
- Tokens.push_back(Token(Template));
+ Tokens.emplace_back(Template);
return Tokens;
}
while (DelimiterStart != StringRef::npos) {
if (DelimiterStart != Start) {
- Token TextToken = Token(Template.substr(Start, DelimiterStart - Start));
- Tokens.push_back(TextToken);
+ Tokens.emplace_back(Template.substr(Start, DelimiterStart - Start));
}
- std::size_t DelimiterEnd = Template.find(Close, DelimiterStart);
+ size_t DelimiterEnd = Template.find(Close, DelimiterStart);
if (DelimiterEnd == StringRef::npos) {
break;
}
+ // Extract the Interpolated variable without {{ and }}
+ size_t InterpolatedStart = DelimiterStart + Open.size();
+ size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
SmallString<128> Interpolated =
- Template.substr(DelimiterStart + Open.size(),
- DelimiterEnd - DelimiterStart - Close.size());
+ Template.substr(InterpolatedStart, InterpolatedEnd);
SmallString<128> RawBody;
RawBody += Open;
RawBody += Interpolated;
RawBody += Close;
- Tokens.push_back(Token(RawBody, Interpolated, Interpolated[0]));
+ Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
Start = DelimiterEnd + Close.size();
DelimiterStart = Template.find(Open, Start);
}
if (Start < Template.size())
- Tokens.push_back(Token(Template.substr(Start)));
+ Tokens.emplace_back(Template.substr(Start));
// fix up white spaces for
// open sections/inverted sections/close section/comment
- for (std::size_t I = 0; I < Tokens.size(); I++) {
- Token::Type CurrentType = Tokens[I].getType();
- bool RequiresCleanUp = CurrentType == Token::Type::SectionOpen ||
- CurrentType == Token::Type::InvertSectionOpen ||
- CurrentType == Token::Type::SectionClose ||
- CurrentType == Token::Type::Comment ||
- CurrentType == Token::Type::Partial;
-
- bool NoTextBehind = false;
- bool NoTextAhead = false;
- if (I > 0 && Tokens[I - 1].getType() == Token::Type::Text &&
- RequiresCleanUp) {
- Token &PrevToken = Tokens[I - 1];
- StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
- if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
- (TokenBody.empty() && I == 1))
- NoTextBehind = true;
- }
- if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
- RequiresCleanUp) {
- Token &NextToken = Tokens[I + 1];
- StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
- if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n"))
- NoTextAhead = true;
- }
+ // This loop attempts to find standalone tokens and tries to trim out
+ // the whitespace around them
+ // for example:
+ // if you have the template string
+ // "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
+ // The output would be
+ // "Line 1\n Line 2\n Line 3"
+ size_t LastIdx = Tokens.size() - 1;
+ for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
+ Token::Type CurrentType = Tokens[Idx].getType();
+ // Check if token type requires cleanup
+ bool RequiresCleanUp = (CurrentType == Token::Type::SectionOpen ||
+ CurrentType == Token::Type::InvertSectionOpen ||
+ CurrentType == Token::Type::SectionClose ||
+ CurrentType == Token::Type::Comment ||
+ CurrentType == Token::Type::Partial);
+
+ if (!RequiresCleanUp)
+ continue;
- if ((NoTextBehind && NoTextAhead) || (NoTextAhead && I == 0)) {
- Token &NextToken = Tokens[I + 1];
+ bool NoTextBehind = noTextBehind(Idx, Tokens);
+ bool NoTextAhead = noTextAhead(Idx, Tokens);
+
+ // Adjust next token body if no text ahead
+ if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
+ Token &NextToken = Tokens[Idx + 1];
StringRef NextTokenBody = NextToken.getTokenBody();
- if (NextTokenBody.starts_with("\r\n"))
+ if (NextTokenBody.starts_with("\r\n")) {
NextToken.setTokenBody(NextTokenBody.substr(2));
- else if (NextToken.getTokenBody().starts_with("\n"))
+ } else if (NextTokenBody.starts_with("\n")) {
NextToken.setTokenBody(NextTokenBody.substr(1));
+ }
}
- if ((NoTextBehind && NoTextAhead) ||
- (NoTextBehind && I == Tokens.size() - 1)) {
- Token &PrevToken = Tokens[I - 1];
+ // Adjust previous token body if no text behind
+ if ((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx)) {
+ Token &PrevToken = Tokens[Idx - 1];
StringRef PrevTokenBody = PrevToken.getTokenBody();
PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
}
@@ -165,84 +183,87 @@ std::vector<Token> tokenize(StringRef Template) {
class Parser {
public:
- Parser(StringRef TemplateStr) : TemplateStr(TemplateStr) {}
+ Parser(StringRef TemplateStr, BumpPtrAllocator &Allocator)
+ : Allocator(Allocator), TemplateStr(TemplateStr) {}
- std::shared_ptr<ASTNode> parse();
+ ASTNode *parse();
private:
- void parseMustache(std::shared_ptr<ASTNode> Parent);
+ void parseMustache(ASTNode *Parent);
- std::vector<Token> Tokens;
- std::size_t CurrentPtr;
+ BumpPtrAllocator &Allocator;
+ SmallVector<Token, 0> Tokens;
+ size_t CurrentPtr;
StringRef TemplateStr;
};
-std::shared_ptr<ASTNode> Parser::parse() {
+ASTNode *Parser::parse() {
Tokens = tokenize(TemplateStr);
CurrentPtr = 0;
- std::shared_ptr<ASTNode> Root = std::make_shared<ASTNode>();
- parseMustache(Root);
- return Root;
+ void *Root = Allocator.Allocate(sizeof(ASTNode), alignof(ASTNode));
+ ASTNode *RootNode = new (Root) ASTNode();
+ parseMustache(RootNode);
+ return RootNode;
}
-void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
+void Parser::parseMustache(ASTNode *Parent) {
while (CurrentPtr < Tokens.size()) {
Token CurrentToken = Tokens[CurrentPtr];
CurrentPtr++;
Accessor A = CurrentToken.getAccessor();
- std::shared_ptr<ASTNode> CurrentNode;
+ ASTNode *CurrentNode;
+ void *Node = Allocator.Allocate(sizeof(ASTNode), alignof(ASTNode));
switch (CurrentToken.getType()) {
case Token::Type::Text: {
- CurrentNode =
- std::make_shared<ASTNode>(CurrentToken.getTokenBody(), Parent);
+ CurrentNode = new (Node) ASTNode(CurrentToken.getTokenBody(), Parent);
Parent->addChild(CurrentNode);
break;
}
case Token::Type::Variable: {
- CurrentNode = std::make_shared<ASTNode>(ASTNode::Variable, A, Parent);
+ CurrentNode = new (Node) ASTNode(ASTNode::Variable, A, Parent);
Parent->addChild(CurrentNode);
break;
}
case Token::Type::UnescapeVariable: {
- CurrentNode =
- std::make_shared<ASTNode>(ASTNode::UnescapeVariable, A, Parent);
+ CurrentNode = new (Node) ASTNode(ASTNode::UnescapeVariable, A, Parent);
Parent->addChild(CurrentNode);
break;
}
case Token::Type::Partial: {
- CurrentNode = std::make_shared<ASTNode>(ASTNode::Partial, A, Parent);
+ CurrentNode = new (Node) ASTNode(ASTNode::Partial, A, Parent);
Parent->addChild(CurrentNode);
break;
}
case Token::Type::SectionOpen: {
- CurrentNode = std::make_shared<ASTNode>(ASTNode::Section, A, Parent);
- std::size_t Start = CurrentPtr;
+ CurrentNode = new (Node) ASTNode(ASTNode::Section, A, Parent);
+ size_t Start = CurrentPtr;
parseMustache(CurrentNode);
- std::size_t End = CurrentPtr;
+ size_t End = CurrentPtr;
SmallString<128> RawBody;
- if (Start + 1 < End - 1)
+ if (Start + 1 < End - 1) {
for (std::size_t I = Start + 1; I < End - 1; I++)
RawBody += Tokens[I].getRawBody();
- else if (Start + 1 == End - 1)
+ } else if (Start + 1 == End - 1) {
RawBody = Tokens[Start].getRawBody();
+ }
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
break;
}
case Token::Type::InvertSectionOpen: {
- CurrentNode =
- std::make_shared<ASTNode>(ASTNode::InvertSection, A, Parent);
- std::size_t Start = CurrentPtr;
+ CurrentNode = new (Node) ASTNode(ASTNode::InvertSection, A, Parent);
+ size_t Start = CurrentPtr;
parseMustache(CurrentNode);
- std::size_t End = CurrentPtr;
+ size_t End = CurrentPtr;
SmallString<128> RawBody;
- if (Start + 1 < End - 1)
- for (std::size_t I = Start + 1; I < End - 1; I++)
- RawBody += Tokens[I].getRawBody();
- else if (Start + 1 == End - 1)
+ if (Start + 1 < End - 1) {
+ for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
+ RawBody += Tokens[Idx].getRawBody();
+ } else if (Start + 1 == End - 1) {
RawBody = Tokens[Start].getRawBody();
+ }
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
break;
@@ -255,27 +276,15 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
}
}
-Template Template::createTemplate(StringRef TemplateStr) {
- Parser P = Parser(TemplateStr.str());
- std::shared_ptr<ASTNode> MustacheTree = P.parse();
- Template T = Template(MustacheTree);
- // the default behaviour is to escape html entities
- DenseMap<char, StringRef> HtmlEntities = {{'&', "&"},
- {'<', "<"},
- {'>', ">"},
- {'"', """},
- {'\'', "'"}};
- T.registerEscape(HtmlEntities);
- return T;
-}
-
SmallString<128> Template::render(Value Data) {
- return Tree->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+ BumpPtrAllocator LocalAllocator;
+ return Tree->render(Data, LocalAllocator, Partials, Lambdas, SectionLambdas,
+ Escapes);
}
void Template::registerPartial(StringRef Name, StringRef Partial) {
- Parser P = Parser(Partial);
- std::shared_ptr<ASTNode> PartialTree = P.parse();
+ Parser P = Parser(Partial, Allocator);
+ ASTNode *PartialTree = P.parse();
Partials[Name] = PartialTree;
}
@@ -287,6 +296,18 @@ void Template::registerLambda(StringRef Name, SectionLambda L) {
void Template::registerEscape(DenseMap<char, StringRef> E) { Escapes = E; }
+Template::Template(StringRef TemplateStr) {
+ Parser P = Parser(TemplateStr, Allocator);
+ Tree = P.parse();
+ // the default behaviour is to escape html entities
+ DenseMap<char, StringRef> HtmlEntities = {{'&', "&"},
+ {'<', "<"},
+ {'>', ">"},
+ {'"', """},
+ {'\'', "'"}};
+ registerEscape(HtmlEntities);
+}
+
SmallString<128> printJson(Value &Data) {
SmallString<128> Result;
@@ -299,7 +320,7 @@ SmallString<128> printJson(Value &Data) {
Result += Data.getAsString()->str();
return Result;
}
- return llvm::formatv("{0:2}", Data);
+ return formatv("{0:2}", Data);
}
bool isFalsey(Value &V) {
@@ -309,8 +330,8 @@ bool isFalsey(Value &V) {
}
SmallString<128>
-ASTNode::render(Value Data,
- DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
+ASTNode::render(Value Data, BumpPtrAllocator &Allocator,
+ DenseMap<StringRef, ASTNode *> &Partials,
DenseMap<StringRef, Lambda> &Lambdas,
DenseMap<StringRef, SectionLambda> &SectionLambdas,
DenseMap<char, StringRef> &Escapes) {
@@ -319,18 +340,18 @@ ASTNode::render(Value Data,
SmallString<128> Result;
switch (T) {
case Root: {
- for (std::shared_ptr<ASTNode> Child : Children)
- Result +=
- Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+ for (ASTNode *Child : Children)
+ Result += Child->render(Context, Allocator, Partials, Lambdas,
+ SectionLambdas, Escapes);
return Result;
}
case Text:
return Body;
case Partial: {
if (Partials.find(Accessor[0]) != Partials.end()) {
- std::shared_ptr<ASTNode> Partial = Partials[Accessor[0]];
- Result +=
- Partial->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+ ASTNode *Partial = Partials[Accessor[0]];
+ Result += Partial->render(Data, Allocator, Partials, Lambdas,
+ SectionLambdas, Escapes);
return Result;
}
return Result;
@@ -340,10 +361,10 @@ ASTNode::render(Value Data,
Lambda &L = Lambdas[Accessor[0]];
Value LambdaResult = L();
StringRef LambdaStr = printJson(LambdaResult);
- Parser P = Parser(LambdaStr);
- std::shared_ptr<ASTNode> LambdaNode = P.parse();
- SmallString<128> RenderStr =
- LambdaNode->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+ Parser P = Parser(LambdaStr, Allocator);
+ ASTNode *LambdaNode = P.parse();
+ SmallString<128> RenderStr = LambdaNode->render(
+ Data, Allocator, Partials, Lambdas, SectionLambdas, Escapes);
return escapeString(RenderStr, Escapes);
}
return escapeString(printJson(Context), Escapes);
@@ -353,56 +374,51 @@ ASTNode::render(Value Data,
Lambda &L = Lambdas[Accessor[0]];
Value LambdaResult = L();
StringRef LambdaStr = printJson(LambdaResult);
- Parser P = Parser(LambdaStr);
- std::shared_ptr<ASTNode> LambdaNode = P.parse();
+ Parser P = Parser(LambdaStr, Allocator);
+ ASTNode *LambdaNode = P.parse();
DenseMap<char, StringRef> EmptyEscapes;
- return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
- EmptyEscapes);
+ return LambdaNode->render(Data, Allocator, Partials, Lambdas,
+ SectionLambdas, EmptyEscapes);
}
return printJson(Context);
}
case Section: {
// Sections are not rendered if the context is falsey
bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
-
if (isFalsey(Context) && !IsLambda)
return Result;
-
if (IsLambda) {
SectionLambda &Lambda = SectionLambdas[Accessor[0]];
Value Return = Lambda(RawBody);
if (isFalsey(Return))
return Result;
StringRef LambdaStr = printJson(Return);
- Parser P = Parser(LambdaStr.str());
- std::shared_ptr<ASTNode> LambdaNode = P.parse();
- return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
- Escapes);
+ Parser P = Parser(LambdaStr.str(), Allocator);
+ ASTNode *LambdaNode = P.parse();
+ return LambdaNode->render(Data, Allocator, Partials, Lambdas,
+ SectionLambdas, Escapes);
}
-
if (Context.getAsArray()) {
json::Array *Arr = Context.getAsArray();
for (Value &V : *Arr) {
- for (std::shared_ptr<ASTNode> Child : Children)
- Result +=
- Child->render(V, Partials, Lambdas, SectionLambdas, Escapes);
+ for (ASTNode *Child : Children)
+ Result += Child->render(V, Allocator, Partials, Lambdas,
+ SectionLambdas, Escapes);
}
return Result;
}
-
- for (std::shared_ptr<ASTNode> Child : Children)
- Result +=
- Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
-
+ for (ASTNode *Child : Children)
+ Result += Child->render(Context, Allocator, Partials, Lambdas,
+ SectionLambdas, Escapes);
return Result;
}
case InvertSection: {
bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
if (!isFalsey(Context) || IsLambda)
return Result;
- for (std::shared_ptr<ASTNode> Child : Children)
- Result +=
- Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+ for (ASTNode *Child : Children)
+ Result += Child->render(Context, Allocator, Partials, Lambdas,
+ SectionLambdas, Escapes);
return Result;
}
}
@@ -420,13 +436,13 @@ Value ASTNode::findContext() {
if (Accessor[0] == ".")
return LocalContext;
json::Object *CurrentContext = LocalContext.getAsObject();
- SmallString<128> CurrentAccessor = Accessor[0];
- std::weak_ptr<ASTNode> CurrentParent = Parent;
+ StringRef CurrentAccessor = Accessor[0];
+ ASTNode *CurrentParent = Parent;
while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
- if (auto Ptr = CurrentParent.lock()) {
- CurrentContext = Ptr->LocalContext.getAsObject();
- CurrentParent = Ptr->Parent;
+ if (CurrentParent->T != Root) {
+ CurrentContext = CurrentParent->LocalContext.getAsObject();
+ CurrentParent = CurrentParent->Parent;
continue;
}
return nullptr;
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index 3f3f04a9b55b13..d120736be40856 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -22,7 +22,7 @@ using namespace llvm::json;
TEST(MustacheInterpolation, NoInterpolation) {
// Mustache-free templates should render as-is.
Value D = {};
- auto T = Template::createTemplate("Hello from {Mustache}!\n");
+ auto T = Template("Hello from {Mustache}!\n");
auto Out = T.render(D);
EXPECT_EQ("Hello from {Mustache}!\n", Out);
}
@@ -30,7 +30,7 @@ TEST(MustacheInterpolation, NoInterpolation) {
TEST(MustacheInterpolation, BasicInterpolation) {
// Unadorned tags should interpolate content into the template.
Value D = Object{{"subject", "World"}};
- auto T = Template::createTemplate("Hello, {{subject}}!");
+ auto T = Template("Hello, {{subject}}!");
auto Out = T.render(D);
EXPECT_EQ("Hello, World!", Out);
}
@@ -38,7 +38,7 @@ TEST(MustacheInterpolation, BasicInterpolation) {
TEST(MustacheInterpolation, NoReinterpolation) {
// Interpolated tag output should not be re-interpolated.
Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
- auto T = Template::createTemplate("{{template}}: {{planet}}");
+ auto T = Template("{{template}}: {{planet}}");
auto Out = T.render(D);
EXPECT_EQ("{{planet}}: Earth", Out);
}
@@ -48,8 +48,7 @@ TEST(MustacheInterpolation, HTMLEscaping) {
Value D = Object{
{"forbidden", "& \" < >"},
};
- auto T = Template::createTemplate(
- "These characters should be HTML escaped: {{forbidden}}\n");
+ auto T = Template("These characters should be HTML escaped: {{forbidden}}\n");
auto Out = T.render(D);
EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
Out);
@@ -60,8 +59,8 @@ TEST(MustacheInterpolation, Ampersand) {
Value D = Object{
{"forbidden", "& \" < >"},
};
- auto T = Template::createTemplate(
- "These characters should not be HTML escaped: {{&forbidden}}\n");
+ auto T =
+ Template("These characters should not be HTML escaped: {{&forbidden}}\n");
auto Out = T.render(D);
EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
}
@@ -69,7 +68,7 @@ TEST(MustacheInterpolation, Ampersand) {
TEST(MustacheInterpolation, BasicIntegerInterpolation) {
// Integers should interpolate seamlessly.
Value D = Object{{"mph", 85}};
- auto T = Template::createTemplate("{{mph}} miles an hour!");
+ auto T = Template("{{mph}} miles an hour!");
auto Out = T.render(D);
EXPECT_EQ("85 miles an hour!", Out);
}
@@ -77,7 +76,7 @@ TEST(MustacheInterpolation, BasicIntegerInterpolation) {
TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
// Integers should interpolate seamlessly.
Value D = Object{{"mph", 85}};
- auto T = Template::createTemplate("{{&mph}} miles an hour!");
+ auto T = Template("{{&mph}} miles an hour!");
auto Out = T.render(D);
EXPECT_EQ("85 miles an hour!", Out);
}
@@ -85,7 +84,7 @@ TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
TEST(MustacheInterpolation, BasicDecimalInterpolation) {
// Decimals should interpolate seamlessly with proper significance.
Value D = Object{{"power", 1.21}};
- auto T = Template::createTemplate("{{power}} jiggawatts!");
+ auto T = Template("{{power}} jiggawatts!");
auto Out = T.render(D);
EXPECT_EQ("1.21 jiggawatts!", Out);
}
@@ -93,7 +92,7 @@ TEST(MustacheInterpolation, BasicDecimalInterpolation) {
TEST(MustacheInterpolation, BasicNullInterpolation) {
// Nulls should interpolate as the empty string.
Value D = Object{{"cannot", nullptr}};
- auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+ auto T = Template("I ({{cannot}}) be seen!");
auto Out = T.render(D);
EXPECT_EQ("I () be seen!", Out);
}
@@ -101,7 +100,7 @@ TEST(MustacheInterpolation, BasicNullInterpolation) {
TEST(MustacheInterpolation, AmpersandNullInterpolation) {
// Nulls should interpolate as the empty string.
Value D = Object{{"cannot", nullptr}};
- auto T = Template::createTemplate("I ({{&cannot}}) be seen!");
+ auto T = Template("I ({{&cannot}}) be seen!");
auto Out = T.render(D);
EXPECT_EQ("I () be seen!", Out);
}
@@ -109,7 +108,7 @@ TEST(MustacheInterpolation, AmpersandNullInterpolation) {
TEST(MustacheInterpolation, BasicContextMissInterpolation) {
// Failed context lookups should default to empty strings.
Value D = Object{};
- auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+ auto T = Template("I ({{cannot}}) be seen!");
auto Out = T.render(D);
EXPECT_EQ("I () be seen!", Out);
}
@@ -117,8 +116,7 @@ TEST(MustacheInterpolation, BasicContextMissInterpolation) {
TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
// Dotted names should be considered a form of shorthand for sections.
Value D = Object{{"person", Object{{"name", "Joe"}}}};
- auto T = Template::createTemplate(
- "{{person.name}} == {{#person}}{{name}}{{/person}}");
+ auto T = Template("{{person.name}} == {{#person}}{{name}}{{/person}}");
auto Out = T.render(D);
EXPECT_EQ("Joe == Joe", Out);
}
@@ -126,8 +124,7 @@ TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {
// Dotted names should be considered a form of shorthand for sections.
Value D = Object{{"person", Object{{"name", "Joe"}}}};
- auto T = Template::createTemplate(
- "{{&person.name}} == {{#person}}{{&name}}{{/person}}");
+ auto T = Template("{{&person.name}} == {{#person}}{{&name}}{{/person}}");
auto Out = T.render(D);
EXPECT_EQ("Joe == Joe", Out);
}
@@ -140,7 +137,7 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
Object{{"c",
Object{{"d",
Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
- auto T = Template::createTemplate("{{a.b.c.d.e.name}}");
+ auto T = Template("{{a.b.c.d.e.name}}");
auto Out = T.render(D);
EXPECT_EQ("Phil", Out);
}
@@ -148,7 +145,7 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
TEST(MustacheInterpolation, DottedNamesBrokenChains) {
// Any falsey value prior to the last part of the name should yield ''.
Value D = Object{{"a", Object{}}};
- auto T = Template::createTemplate("{{a.b.c}} == ");
+ auto T = Template("{{a.b.c}} == ");
auto Out = T.render(D);
EXPECT_EQ(" == ", Out);
}
@@ -157,7 +154,7 @@ TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {
// Each part of a dotted name should resolve only against its parent.
Value D =
Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};
- auto T = Template::createTemplate("{{a.b.c.name}} == ");
+ auto T = Template("{{a.b.c.name}} == ");
auto Out = T.render(D);
EXPECT_EQ(" == ", Out);
}
@@ -172,7 +169,7 @@ TEST(MustacheInterpolation, DottedNamesInitialResolution) {
Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},
{"b",
Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
- auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}}");
+ auto T = Template("{{#a}}{{b.c.d.e.name}}{{/a}}");
auto Out = T.render(D);
EXPECT_EQ("Phil", Out);
}
@@ -181,7 +178,7 @@ TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
// Dotted names should be resolved against former resolutions.
Value D =
Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};
- auto T = Template::createTemplate("{{#a}}{{b.c}}{{/a}}");
+ auto T = Template("{{#a}}{{b.c}}{{/a}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
@@ -189,7 +186,7 @@ TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
// Dotted names shall not be parsed as single, atomic keys
Value D = Object{{"a.b", "c"}};
- auto T = Template::createTemplate("{{a.b}}");
+ auto T = Template("{{a.b}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
@@ -197,7 +194,7 @@ TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
TEST(MustacheInterpolation, DottedNamesNoMasking) {
// Dotted Names in a given context are unavailable due to dot splitting
Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};
- auto T = Template::createTemplate("{{a.b}}");
+ auto T = Template("{{a.b}}");
auto Out = T.render(D);
EXPECT_EQ("d", Out);
}
@@ -205,7 +202,7 @@ TEST(MustacheInterpolation, DottedNamesNoMasking) {
TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
// Unadorned tags should interpolate content into the template.
Value D = "world";
- auto T = Template::createTemplate("Hello, {{.}}!\n");
+ auto T = Template("Hello, {{.}}!\n");
auto Out = T.render(D);
EXPECT_EQ("Hello, world!\n", Out);
}
@@ -213,8 +210,7 @@ TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
// Basic interpolation should be HTML escaped.
Value D = "& \" < >";
- auto T = Template::createTemplate(
- "These characters should not be HTML escaped: {{&.}}\n");
+ auto T = Template("These characters should not be HTML escaped: {{&.}}\n");
auto Out = T.render(D);
EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
}
@@ -222,7 +218,7 @@ TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
// Integers should interpolate seamlessly.
Value D = 85;
- auto T = Template::createTemplate("{{.}} miles an hour!\n");
+ auto T = Template("{{.}} miles an hour!\n");
auto Out = T.render(D);
EXPECT_EQ("85 miles an hour!\n", Out);
}
@@ -230,7 +226,7 @@ TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
// Interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
- auto T = Template::createTemplate("| {{string}} |");
+ auto T = Template("| {{string}} |");
auto Out = T.render(D);
EXPECT_EQ("| --- |", Out);
}
@@ -238,7 +234,7 @@ TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
// Interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
- auto T = Template::createTemplate("| {{&string}} |");
+ auto T = Template("| {{&string}} |");
auto Out = T.render(D);
EXPECT_EQ("| --- |", Out);
}
@@ -246,7 +242,7 @@ TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
// Standalone interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
- auto T = Template::createTemplate(" {{string}}\n");
+ auto T = Template(" {{string}}\n");
auto Out = T.render(D);
EXPECT_EQ(" ---\n", Out);
}
@@ -254,7 +250,7 @@ TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
// Standalone interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
- auto T = Template::createTemplate(" {{&string}}\n");
+ auto T = Template(" {{&string}}\n");
auto Out = T.render(D);
EXPECT_EQ(" ---\n", Out);
}
@@ -262,7 +258,7 @@ TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
TEST(MustacheInterpolation, InterpolationWithPadding) {
// Superfluous in-tag whitespace should be ignored.
Value D = Object{{"string", "---"}};
- auto T = Template::createTemplate("|{{ string }}|");
+ auto T = Template("|{{ string }}|");
auto Out = T.render(D);
EXPECT_EQ("|---|", Out);
}
@@ -270,7 +266,7 @@ TEST(MustacheInterpolation, InterpolationWithPadding) {
TEST(MustacheInterpolation, AmpersandWithPadding) {
// Superfluous in-tag whitespace should be ignored.
Value D = Object{{"string", "---"}};
- auto T = Template::createTemplate("|{{& string }}|");
+ auto T = Template("|{{& string }}|");
auto Out = T.render(D);
EXPECT_EQ("|---|", Out);
}
@@ -278,38 +274,35 @@ TEST(MustacheInterpolation, AmpersandWithPadding) {
TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
// Superfluous in-tag whitespace should be ignored.
Value D = Object{{"string", "---"}};
- auto T = Template::createTemplate("|{{ string \n\n\n }}|");
+ auto T = Template("|{{ string \n\n\n }}|");
auto Out = T.render(D);
EXPECT_EQ("|---|", Out);
}
TEST(MustacheSections, Truthy) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(
- "{{#boolean}}This should be rendered.{{/boolean}}");
+ auto T = Template("{{#boolean}}This should be rendered.{{/boolean}}");
auto Out = T.render(D);
EXPECT_EQ("This should be rendered.", Out);
}
TEST(MustacheSections, Falsey) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(
- "{{#boolean}}This should not be rendered.{{/boolean}}");
+ auto T = Template("{{#boolean}}This should not be rendered.{{/boolean}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
TEST(MustacheSections, NullIsFalsey) {
Value D = Object{{"null", nullptr}};
- auto T = Template::createTemplate(
- "{{#null}}This should not be rendered.{{/null}}");
+ auto T = Template("{{#null}}This should not be rendered.{{/null}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
TEST(MustacheSections, Context) {
Value D = Object{{"context", Object{{"name", "Joe"}}}};
- auto T = Template::createTemplate("{{#context}}Hi {{name}}.{{/context}}");
+ auto T = Template("{{#context}}Hi {{name}}.{{/context}}");
auto Out = T.render(D);
EXPECT_EQ("Hi Joe.", Out);
}
@@ -319,14 +312,14 @@ TEST(MustacheSections, ParentContexts) {
{"b", "wrong"},
{"sec", Object{{"b", "bar"}}},
{"c", Object{{"d", "baz"}}}};
- auto T = Template::createTemplate("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
+ auto T = Template("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
auto Out = T.render(D);
EXPECT_EQ("foo, bar, baz", Out);
}
TEST(MustacheSections, VariableTest) {
Value D = Object{{"foo", "bar"}};
- auto T = Template::createTemplate("{{#foo}}{{.}} is {{foo}}{{/foo}}");
+ auto T = Template("{{#foo}}{{.}} is {{foo}}{{/foo}}");
auto Out = T.render(D);
EXPECT_EQ("bar is bar", Out);
}
@@ -340,14 +333,14 @@ TEST(MustacheSections, ListContexts) {
Array{Object{{"mname", "1"},
{"bottoms", Array{Object{{"bname", "x"}},
Object{{"bname", "y"}}}}}}}}}}};
- auto T = Template::createTemplate("{{#tops}}"
- "{{#middles}}"
- "{{tname.lower}}{{mname}}."
- "{{#bottoms}}"
- "{{tname.upper}}{{mname}}{{bname}}."
- "{{/bottoms}}"
- "{{/middles}}"
- "{{/tops}}");
+ auto T = Template("{{#tops}}"
+ "{{#middles}}"
+ "{{tname.lower}}{{mname}}."
+ "{{#bottoms}}"
+ "{{tname.upper}}{{mname}}{{bname}}."
+ "{{/bottoms}}"
+ "{{/middles}}"
+ "{{/tops}}");
auto Out = T.render(D);
EXPECT_EQ("a1.A1x.A1y.", Out);
}
@@ -357,7 +350,7 @@ TEST(MustacheSections, DeeplyNestedContexts) {
{"a", Object{{"one", 1}}},
{"b", Object{{"two", 2}}},
{"c", Object{{"three", 3}, {"d", Object{{"four", 4}, {"five", 5}}}}}};
- auto T = Template::createTemplate(
+ auto T = Template(
"{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{"
"three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{"
"{two}}{{one}}\n{{#five}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}"
@@ -376,123 +369,120 @@ TEST(MustacheSections, DeeplyNestedContexts) {
TEST(MustacheSections, List) {
Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
Object{{"item", 3}}}}};
- auto T = Template::createTemplate("{{#list}}{{item}}{{/list}}");
+ auto T = Template("{{#list}}{{item}}{{/list}}");
auto Out = T.render(D);
EXPECT_EQ("123", Out);
}
TEST(MustacheSections, EmptyList) {
Value D = Object{{"list", Array{}}};
- auto T = Template::createTemplate("{{#list}}Yay lists!{{/list}}");
+ auto T = Template("{{#list}}Yay lists!{{/list}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
TEST(MustacheSections, Doubled) {
Value D = Object{{"bool", true}, {"two", "second"}};
- auto T = Template::createTemplate("{{#bool}}\n* first\n{{/bool}}\n* "
- "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
+ auto T = Template("{{#bool}}\n* first\n{{/bool}}\n* "
+ "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
auto Out = T.render(D);
EXPECT_EQ("* first\n* second\n* third\n", Out);
}
TEST(MustacheSections, NestedTruthy) {
Value D = Object{{"bool", true}};
- auto T = Template::createTemplate(
- "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+ auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
auto Out = T.render(D);
EXPECT_EQ("| A B C D E |", Out);
}
TEST(MustacheSections, NestedFalsey) {
Value D = Object{{"bool", false}};
- auto T = Template::createTemplate(
- "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+ auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
auto Out = T.render(D);
EXPECT_EQ("| A E |", Out);
}
TEST(MustacheSections, ContextMisses) {
Value D = Object{};
- auto T = Template::createTemplate(
- "[{{#missing}}Found key 'missing'!{{/missing}}]");
+ auto T = Template("[{{#missing}}Found key 'missing'!{{/missing}}]");
auto Out = T.render(D);
EXPECT_EQ("[]", Out);
}
TEST(MustacheSections, ImplicitIteratorString) {
Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};
- auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+ auto T = Template("{{#list}}({{.}}){{/list}}");
auto Out = T.render(D);
EXPECT_EQ("(a)(b)(c)(d)(e)", Out);
}
TEST(MustacheSections, ImplicitIteratorInteger) {
Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};
- auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+ auto T = Template("{{#list}}({{.}}){{/list}}");
auto Out = T.render(D);
EXPECT_EQ("(1)(2)(3)(4)(5)", Out);
}
TEST(MustacheSections, ImplicitIteratorArray) {
Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};
- auto T = Template::createTemplate("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
+ auto T = Template("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
auto Out = T.render(D);
EXPECT_EQ("(123)(abc)", Out);
}
TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {
Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
- auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+ auto T = Template("{{#list}}({{.}}){{/list}}");
auto Out = T.render(D);
EXPECT_EQ("(&)(")(<)(>)", Out);
}
TEST(MustacheSections, ImplicitIteratorAmpersand) {
Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
- auto T = Template::createTemplate("{{#list}}({{&.}}){{/list}}");
+ auto T = Template("{{#list}}({{&.}}){{/list}}");
auto Out = T.render(D);
EXPECT_EQ("(&)(\")(<)(>)", Out);
}
TEST(MustacheSections, ImplicitIteratorRootLevel) {
Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};
- auto T = Template::createTemplate("{{#.}}({{value}}){{/.}}");
+ auto T = Template("{{#.}}({{value}}){{/.}}");
auto Out = T.render(D);
EXPECT_EQ("(a)(b)", Out);
}
TEST(MustacheSections, DottedNamesTruthy) {
Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
- auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == Here");
+ auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == Here");
auto Out = T.render(D);
EXPECT_EQ("Here == Here", Out);
}
TEST(MustacheSections, DottedNamesFalsey) {
Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
- auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+ auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
auto Out = T.render(D);
EXPECT_EQ(" == ", Out);
}
TEST(MustacheSections, DottedNamesBrokenChains) {
Value D = Object{{"a", Object{}}};
- auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+ auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
auto Out = T.render(D);
EXPECT_EQ(" == ", Out);
}
TEST(MustacheSections, SurroundingWhitespace) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(" | {{#boolean}}\t|\t{{/boolean}} | \n");
+ auto T = Template(" | {{#boolean}}\t|\t{{/boolean}} | \n");
auto Out = T.render(D);
EXPECT_EQ(" | \t|\t | \n", Out);
}
TEST(MustacheSections, InternalWhitespace) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(
+ auto T = Template(
" | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
auto Out = T.render(D);
EXPECT_EQ(" | \n | \n", Out);
@@ -500,83 +490,78 @@ TEST(MustacheSections, InternalWhitespace) {
TEST(MustacheSections, IndentedInlineSections) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(
- " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
+ auto T =
+ Template(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
auto Out = T.render(D);
EXPECT_EQ(" YES\n GOOD\n", Out);
}
TEST(MustacheSections, StandaloneLines) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(
- "| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
+ auto T = Template("| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
auto Out = T.render(D);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheSections, IndentedStandaloneLines) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(
- "| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n");
+ auto T = Template("| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n");
auto Out = T.render(D);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheSections, StandaloneLineEndings) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
+ auto T = Template("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
auto Out = T.render(D);
EXPECT_EQ("|\r\n|", Out);
}
TEST(MustacheSections, StandaloneWithoutPreviousLine) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(" {{#boolean}}\n#{{/boolean}}\n/");
+ auto T = Template(" {{#boolean}}\n#{{/boolean}}\n/");
auto Out = T.render(D);
EXPECT_EQ("#\n/", Out);
}
TEST(MustacheSections, StandaloneWithoutNewline) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate("#{{#boolean}}\n/\n {{/boolean}}");
+ auto T = Template("#{{#boolean}}\n/\n {{/boolean}}");
auto Out = T.render(D);
EXPECT_EQ("#\n/\n", Out);
}
TEST(MustacheSections, Padding) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate("|{{# boolean }}={{/ boolean }}|");
+ auto T = Template("|{{# boolean }}={{/ boolean }}|");
auto Out = T.render(D);
EXPECT_EQ("|=|", Out);
}
TEST(MustacheInvertedSections, Falsey) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(
- "{{^boolean}}This should be rendered.{{/boolean}}");
+ auto T = Template("{{^boolean}}This should be rendered.{{/boolean}}");
auto Out = T.render(D);
EXPECT_EQ("This should be rendered.", Out);
}
TEST(MustacheInvertedSections, Truthy) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate(
- "{{^boolean}}This should not be rendered.{{/boolean}}");
+ auto T = Template("{{^boolean}}This should not be rendered.{{/boolean}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
TEST(MustacheInvertedSections, NullIsFalsey) {
Value D = Object{{"null", nullptr}};
- auto T =
- Template::createTemplate("{{^null}}This should be rendered.{{/null}}");
+ auto T = Template("{{^null}}This should be rendered.{{/null}}");
auto Out = T.render(D);
EXPECT_EQ("This should be rendered.", Out);
}
TEST(MustacheInvertedSections, Context) {
Value D = Object{{"context", Object{{"name", "Joe"}}}};
- auto T = Template::createTemplate("{{^context}}Hi {{name}}.{{/context}}");
+ auto T = Template("{{^context}}Hi {{name}}.{{/context}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
@@ -584,81 +569,78 @@ TEST(MustacheInvertedSections, Context) {
TEST(MustacheInvertedSections, List) {
Value D = Object{
{"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
- auto T = Template::createTemplate("{{^list}}{{n}}{{/list}}");
+ auto T = Template("{{^list}}{{n}}{{/list}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
TEST(MustacheInvertedSections, EmptyList) {
Value D = Object{{"list", Array{}}};
- auto T = Template::createTemplate("{{^list}}Yay lists!{{/list}}");
+ auto T = Template("{{^list}}Yay lists!{{/list}}");
auto Out = T.render(D);
EXPECT_EQ("Yay lists!", Out);
}
TEST(MustacheInvertedSections, Doubled) {
Value D = Object{{"bool", false}, {"two", "second"}};
- auto T = Template::createTemplate("{{^bool}}\n* first\n{{/bool}}\n* "
- "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
+ auto T = Template("{{^bool}}\n* first\n{{/bool}}\n* "
+ "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
auto Out = T.render(D);
EXPECT_EQ("* first\n* second\n* third\n", Out);
}
TEST(MustacheInvertedSections, NestedFalsey) {
Value D = Object{{"bool", false}};
- auto T = Template::createTemplate(
- "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+ auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
auto Out = T.render(D);
EXPECT_EQ("| A B C D E |", Out);
}
TEST(MustacheInvertedSections, NestedTruthy) {
Value D = Object{{"bool", true}};
- auto T = Template::createTemplate(
- "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+ auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
auto Out = T.render(D);
EXPECT_EQ("| A E |", Out);
}
TEST(MustacheInvertedSections, ContextMisses) {
Value D = Object{};
- auto T = Template::createTemplate(
- "[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
+ auto T = Template("[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
auto Out = T.render(D);
EXPECT_EQ("[Cannot find key 'missing'!]", Out);
}
TEST(MustacheInvertedSections, DottedNamesTruthy) {
Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
- auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == ");
+ auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == ");
auto Out = T.render(D);
EXPECT_EQ(" == ", Out);
}
TEST(MustacheInvertedSections, DottedNamesFalsey) {
Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
- auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+ auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
auto Out = T.render(D);
EXPECT_EQ("Not Here == Not Here", Out);
}
TEST(MustacheInvertedSections, DottedNamesBrokenChains) {
Value D = Object{{"a", Object{}}};
- auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+ auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
auto Out = T.render(D);
EXPECT_EQ("Not Here == Not Here", Out);
}
TEST(MustacheInvertedSections, SurroundingWhitespace) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(" | {{^boolean}}\t|\t{{/boolean}} | \n");
+ auto T = Template(" | {{^boolean}}\t|\t{{/boolean}} | \n");
auto Out = T.render(D);
EXPECT_EQ(" | \t|\t | \n", Out);
}
TEST(MustacheInvertedSections, InternalWhitespace) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(
+ auto T = Template(
" | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
auto Out = T.render(D);
EXPECT_EQ(" | \n | \n", Out);
@@ -666,59 +648,57 @@ TEST(MustacheInvertedSections, InternalWhitespace) {
TEST(MustacheInvertedSections, IndentedInlineSections) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(
- " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
+ auto T =
+ Template(" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
auto Out = T.render(D);
EXPECT_EQ(" NO\n WAY\n", Out);
}
TEST(MustacheInvertedSections, StandaloneLines) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(
- "| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
+ auto T = Template("| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
auto Out = T.render(D);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheInvertedSections, StandaloneIndentedLines) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(
- "| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n");
+ auto T = Template("| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n");
auto Out = T.render(D);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheInvertedSections, StandaloneLineEndings) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
+ auto T = Template("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
auto Out = T.render(D);
EXPECT_EQ("|\r\n|", Out);
}
TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate(" {{^boolean}}\n^{{/boolean}}\n/");
+ auto T = Template(" {{^boolean}}\n^{{/boolean}}\n/");
auto Out = T.render(D);
EXPECT_EQ("^\n/", Out);
}
TEST(MustacheInvertedSections, StandaloneWithoutNewline) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate("^{{^boolean}}\n/\n {{/boolean}}");
+ auto T = Template("^{{^boolean}}\n/\n {{/boolean}}");
auto Out = T.render(D);
EXPECT_EQ("^\n/\n", Out);
}
TEST(MustacheInvertedSections, Padding) {
Value D = Object{{"boolean", false}};
- auto T = Template::createTemplate("|{{^ boolean }}={{/ boolean }}|");
+ auto T = Template("|{{^ boolean }}={{/ boolean }}|");
auto Out = T.render(D);
EXPECT_EQ("|=|", Out);
}
TEST(MustachePartials, BasicBehavior) {
Value D = Object{};
- auto T = Template::createTemplate("{{>text}}");
+ auto T = Template("{{>text}}");
T.registerPartial("text", "from partial");
auto Out = T.render(D);
EXPECT_EQ("from partial", Out);
@@ -726,14 +706,14 @@ TEST(MustachePartials, BasicBehavior) {
TEST(MustachePartials, FailedLookup) {
Value D = Object{};
- auto T = Template::createTemplate("{{>text}}");
+ auto T = Template("{{>text}}");
auto Out = T.render(D);
EXPECT_EQ("", Out);
}
TEST(MustachePartials, Context) {
Value D = Object{{"text", "content"}};
- auto T = Template::createTemplate("{{>partial}}");
+ auto T = Template("{{>partial}}");
T.registerPartial("partial", "*{{text}}*");
auto Out = T.render(D);
EXPECT_EQ("*content*", Out);
@@ -743,7 +723,7 @@ TEST(MustachePartials, Recursion) {
Value D =
Object{{"content", "X"},
{"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
- auto T = Template::createTemplate("{{>node}}");
+ auto T = Template("{{>node}}");
T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
auto Out = T.render(D);
EXPECT_EQ("X(Y())", Out);
@@ -751,7 +731,7 @@ TEST(MustachePartials, Recursion) {
TEST(MustachePartials, Nested) {
Value D = Object{{"a", "hello"}, {"b", "world"}};
- auto T = Template::createTemplate("{{>outer}}");
+ auto T = Template("{{>outer}}");
T.registerPartial("outer", "*{{a}} {{>inner}}*");
T.registerPartial("inner", "{{b}}!");
auto Out = T.render(D);
@@ -760,7 +740,7 @@ TEST(MustachePartials, Nested) {
TEST(MustachePartials, SurroundingWhitespace) {
Value D = Object{};
- auto T = Template::createTemplate("| {{>partial}} |");
+ auto T = Template("| {{>partial}} |");
T.registerPartial("partial", "\t|\t");
auto Out = T.render(D);
EXPECT_EQ("| \t|\t |", Out);
@@ -768,7 +748,7 @@ TEST(MustachePartials, SurroundingWhitespace) {
TEST(MustachePartials, InlineIndentation) {
Value D = Object{{"data", "|"}};
- auto T = Template::createTemplate(" {{data}} {{> partial}}\n");
+ auto T = Template(" {{data}} {{> partial}}\n");
T.registerPartial("partial", "<\n<");
auto Out = T.render(D);
EXPECT_EQ(" | <\n<\n", Out);
@@ -776,7 +756,7 @@ TEST(MustachePartials, InlineIndentation) {
TEST(MustachePartials, PaddingWhitespace) {
Value D = Object{{"boolean", true}};
- auto T = Template::createTemplate("|{{> partial }}|");
+ auto T = Template("|{{> partial }}|");
T.registerPartial("partial", "[]");
auto Out = T.render(D);
EXPECT_EQ("|[]|", Out);
@@ -784,7 +764,7 @@ TEST(MustachePartials, PaddingWhitespace) {
TEST(MustacheLambdas, BasicInterpolation) {
Value D = Object{};
- auto T = Template::createTemplate("Hello, {{lambda}}!");
+ auto T = Template("Hello, {{lambda}}!");
Lambda L = []() -> llvm::json::Value { return "World"; };
T.registerLambda("lambda", L);
auto Out = T.render(D);
@@ -793,7 +773,7 @@ TEST(MustacheLambdas, BasicInterpolation) {
TEST(MustacheLambdas, InterpolationExpansion) {
Value D = Object{{"planet", "World"}};
- auto T = Template::createTemplate("Hello, {{lambda}}!");
+ auto T = Template("Hello, {{lambda}}!");
Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
T.registerLambda("lambda", L);
auto Out = T.render(D);
@@ -802,7 +782,7 @@ TEST(MustacheLambdas, InterpolationExpansion) {
TEST(MustacheLambdas, BasicMultipleCalls) {
Value D = Object{};
- auto T = Template::createTemplate("{{lambda}} == {{lambda}} == {{lambda}}");
+ auto T = Template("{{lambda}} == {{lambda}} == {{lambda}}");
int I = 0;
Lambda L = [&I]() -> llvm::json::Value {
I += 1;
@@ -815,7 +795,7 @@ TEST(MustacheLambdas, BasicMultipleCalls) {
TEST(MustacheLambdas, Escaping) {
Value D = Object{};
- auto T = Template::createTemplate("<{{lambda}}{{&lambda}}");
+ auto T = Template("<{{lambda}}{{&lambda}}");
Lambda L = []() -> llvm::json::Value { return ">"; };
T.registerLambda("lambda", L);
auto Out = T.render(D);
@@ -824,7 +804,7 @@ TEST(MustacheLambdas, Escaping) {
TEST(MustacheLambdas, Sections) {
Value D = Object{};
- auto T = Template::createTemplate("<{{#lambda}}{{x}}{{/lambda}}>");
+ auto T = Template("<{{#lambda}}{{x}}{{/lambda}}>");
SectionLambda L = [](StringRef Text) -> llvm::json::Value {
if (Text == "{{x}}") {
return "yes";
@@ -840,7 +820,7 @@ TEST(MustacheLambdas, SectionExpansion) {
Value D = Object{
{"planet", "Earth"},
};
- auto T = Template::createTemplate("<{{#lambda}}-{{/lambda}}>");
+ auto T = Template("<{{#lambda}}-{{/lambda}}>");
SectionLambda L = [](StringRef Text) -> llvm::json::Value {
SmallString<128> Result;
Result += Text;
@@ -855,8 +835,7 @@ TEST(MustacheLambdas, SectionExpansion) {
TEST(MustacheLambdas, SectionsMultipleCalls) {
Value D = Object{};
- auto T = Template::createTemplate(
- "{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}");
+ auto T = Template("{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}");
SectionLambda L = [](StringRef Text) -> llvm::json::Value {
SmallString<128> Result;
Result += "__";
@@ -871,7 +850,7 @@ TEST(MustacheLambdas, SectionsMultipleCalls) {
TEST(MustacheLambdas, InvertedSections) {
Value D = Object{{"static", "static"}};
- auto T = Template::createTemplate("<{{^lambda}}{{static}}{{/lambda}}>");
+ auto T = Template("<{{^lambda}}{{static}}{{/lambda}}>");
SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
T.registerLambda("lambda", L);
auto Out = T.render(D);
@@ -881,7 +860,7 @@ TEST(MustacheLambdas, InvertedSections) {
TEST(MustacheComments, Inline) {
// Comment blocks should be removed from the template.
Value D = {};
- auto T = Template::createTemplate("12345{{! Comment Block! }}67890");
+ auto T = Template("12345{{! Comment Block! }}67890");
auto Out = T.render(D);
EXPECT_EQ("1234567890", Out);
}
@@ -889,8 +868,8 @@ TEST(MustacheComments, Inline) {
TEST(MustacheComments, Multiline) {
// Multiline comments should be permitted.
Value D = {};
- auto T = Template::createTemplate(
- "12345{{!\n This is a\n multi-line comment...\n}}67890\n");
+ auto T =
+ Template("12345{{!\n This is a\n multi-line comment...\n}}67890\n");
auto Out = T.render(D);
EXPECT_EQ("1234567890\n", Out);
}
@@ -898,7 +877,7 @@ TEST(MustacheComments, Multiline) {
TEST(MustacheComments, Standalone) {
// All standalone comment lines should be removed.
Value D = {};
- auto T = Template::createTemplate("Begin.\n{{! Comment Block! }}\nEnd.\n");
+ auto T = Template("Begin.\n{{! Comment Block! }}\nEnd.\n");
auto Out = T.render(D);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -906,8 +885,7 @@ TEST(MustacheComments, Standalone) {
TEST(MustacheComments, IndentedStandalone) {
// All standalone comment lines should be removed.
Value D = {};
- auto T = Template::createTemplate(
- "Begin.\n {{! Indented Comment Block! }}\nEnd.\n");
+ auto T = Template("Begin.\n {{! Indented Comment Block! }}\nEnd.\n");
auto Out = T.render(D);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -915,7 +893,7 @@ TEST(MustacheComments, IndentedStandalone) {
TEST(MustacheComments, StandaloneLineEndings) {
// "\r\n" should be considered a newline for standalone tags.
Value D = {};
- auto T = Template::createTemplate("|\r\n{{! Standalone Comment }}\r\n|");
+ auto T = Template("|\r\n{{! Standalone Comment }}\r\n|");
auto Out = T.render(D);
EXPECT_EQ("|\r\n|", Out);
}
@@ -923,7 +901,7 @@ TEST(MustacheComments, StandaloneLineEndings) {
TEST(MustacheComments, StandaloneWithoutPreviousLine) {
// Standalone tags should not require a newline to precede them.
Value D = {};
- auto T = Template::createTemplate(" {{! I'm Still Standalone }}\n!");
+ auto T = Template(" {{! I'm Still Standalone }}\n!");
auto Out = T.render(D);
EXPECT_EQ("!", Out);
}
@@ -931,7 +909,7 @@ TEST(MustacheComments, StandaloneWithoutPreviousLine) {
TEST(MustacheComments, StandaloneWithoutNewline) {
// Standalone tags should not require a newline to follow them.
Value D = {};
- auto T = Template::createTemplate("!\n {{! I'm Still Standalone }}");
+ auto T = Template("!\n {{! I'm Still Standalone }}");
auto Out = T.render(D);
EXPECT_EQ("!\n", Out);
}
@@ -939,8 +917,7 @@ TEST(MustacheComments, StandaloneWithoutNewline) {
TEST(MustacheComments, MultilineStandalone) {
// All standalone comment lines should be removed.
Value D = {};
- auto T = Template::createTemplate(
- "Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
+ auto T = Template("Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
auto Out = T.render(D);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -948,8 +925,8 @@ TEST(MustacheComments, MultilineStandalone) {
TEST(MustacheComments, IndentedMultilineStandalone) {
// All standalone comment lines should be removed.
Value D = {};
- auto T = Template::createTemplate(
- "Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n");
+ auto T =
+ Template("Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n");
auto Out = T.render(D);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -957,7 +934,7 @@ TEST(MustacheComments, IndentedMultilineStandalone) {
TEST(MustacheComments, IndentedInline) {
// Inline comments should not strip whitespace.
Value D = {};
- auto T = Template::createTemplate(" 12 {{! 34 }}\n");
+ auto T = Template(" 12 {{! 34 }}\n");
auto Out = T.render(D);
EXPECT_EQ(" 12 \n", Out);
}
@@ -965,7 +942,7 @@ TEST(MustacheComments, IndentedInline) {
TEST(MustacheComments, SurroundingWhitespace) {
// Comment removal should preserve surrounding whitespace.
Value D = {};
- auto T = Template::createTemplate("12345 {{! Comment Block! }} 67890");
+ auto T = Template("12345 {{! Comment Block! }} 67890");
auto Out = T.render(D);
EXPECT_EQ("12345 67890", Out);
}
@@ -974,7 +951,7 @@ TEST(MustacheComments, VariableNameCollision) {
// Comments must never render, even if a variable with the same name exists.
Value D = Object{
{"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};
- auto T = Template::createTemplate("comments never show: >{{! comment }}<");
+ auto T = Template("comments never show: >{{! comment }}<");
auto Out = T.render(D);
EXPECT_EQ("comments never show: ><", Out);
}
\ No newline at end of file
>From d8aa85c2e87b9ae9c40ea0758969f5bc5720820e Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 06:24:27 -0400
Subject: [PATCH 23/62] [llvm][support] clang-format
---
llvm/include/llvm/Support/Mustache.h | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 607e33b4ec2c9b..98843897707a82 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -28,10 +28,10 @@
//
// The Template class is container class outputs the Mustache template string
// and is main class for users. It stores all the lambdas and the ASTNode Tree.
-// When the Template is instantiated it calls tokenize the Template String into
-// the Token class and calls a basic recursive descent parser to construct the
-// ASTNode Tree. The ASTNodes are all stored in an arena allocator which is
-// freed once the template class goes out of scope
+// When the Template is instantiated it tokenize the Template String and creates
+// a vector of Tokens. Then it calls a basic recursive descent parser to
+// construct the ASTNode Tree. The ASTNodes are all stored in an arena
+// allocator which is freed once the template class goes out of scope
//
// Usage:
// \code
@@ -121,15 +121,15 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), LocalContext(nullptr){};
+ ASTNode() : T(Type::Root), LocalContext(nullptr) {};
ASTNode(StringRef Body, ASTNode *Parent)
- : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
+ : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- LocalContext(nullptr){};
+ LocalContext(nullptr) {};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
>From 0534a054574ec18980e130a1d54a98af2bc59d5d Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 06:54:19 -0400
Subject: [PATCH 24/62] [llvm][support] clang-format
---
llvm/include/llvm/Support/Mustache.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 98843897707a82..88a2cb0d988097 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -28,9 +28,9 @@
//
// The Template class is container class outputs the Mustache template string
// and is main class for users. It stores all the lambdas and the ASTNode Tree.
-// When the Template is instantiated it tokenize the Template String and creates
-// a vector of Tokens. Then it calls a basic recursive descent parser to
-// construct the ASTNode Tree. The ASTNodes are all stored in an arena
+// When the Template is instantiated it tokenize the Template String and
+// creates a vector of Tokens. Then it calls a basic recursive descent parser
+// to construct the ASTNode Tree. The ASTNodes are all stored in an arena
// allocator which is freed once the template class goes out of scope
//
// Usage:
>From 06da7a5897347f2d22e406e2bec7800cbb29fd1d Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 07:46:30 -0400
Subject: [PATCH 25/62] [llvm][support] clang-format
---
llvm/include/llvm/Support/Mustache.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 88a2cb0d988097..073e3d0148dcc4 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -28,7 +28,7 @@
//
// The Template class is container class outputs the Mustache template string
// and is main class for users. It stores all the lambdas and the ASTNode Tree.
-// When the Template is instantiated it tokenize the Template String and
+// When the Template is instantiated it tokenize the Template String and
// creates a vector of Tokens. Then it calls a basic recursive descent parser
// to construct the ASTNode Tree. The ASTNodes are all stored in an arena
// allocator which is freed once the template class goes out of scope
>From f713198d1c4dbc286fa180973ccf604f2656c287 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:08:00 -0400
Subject: [PATCH 26/62] [llvm] mustache address comments
---
llvm/include/llvm/Support/Mustache.h | 67 ++++--
llvm/lib/Support/Mustache.cpp | 331 +++++++++++++++++----------
2 files changed, 251 insertions(+), 147 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 073e3d0148dcc4..4cd66d08dd39fa 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -64,12 +64,13 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/JSON.h"
+#include "llvm/Support/StringSaver.h"
#include <vector>
namespace llvm {
namespace mustache {
-using Accessor = SmallVector<SmallString<128>>;
+using Accessor = SmallVector<SmallString<0>>;
using Lambda = std::function<llvm::json::Value()>;
using SectionLambda = std::function<llvm::json::Value(StringRef)>;
@@ -100,13 +101,20 @@ class Token {
Type getType() const { return TokenType; };
+ void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+ size_t getIndentation() const { return Indentation; };
+
static Type getTokenType(char Identifier);
private:
+ size_t Indentation;
Type TokenType;
+ // RawBody is the original string that was tokenized
SmallString<128> RawBody;
Accessor Accessor;
- SmallString<128> TokenBody;
+ // TokenBody is the original string with the identifier removed
+ SmallString<0> TokenBody;
};
class ASTNode {
@@ -121,36 +129,55 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), LocalContext(nullptr) {};
+ ASTNode() : T(Type::Root), LocalContext(nullptr){};
ASTNode(StringRef Body, ASTNode *Parent)
- : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
+ : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr),
+ Indentation(0){};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- LocalContext(nullptr) {};
+ LocalContext(nullptr), Indentation(0){};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
- SmallString<128> getBody() const { return Body; };
-
void setBody(StringRef NewBody) { Body = NewBody; };
void setRawBody(StringRef NewBody) { RawBody = NewBody; };
- SmallString<128> render(llvm::json::Value Data,
- llvm::BumpPtrAllocator &Allocator,
- DenseMap<StringRef, ASTNode *> &Partials,
- DenseMap<StringRef, Lambda> &Lambdas,
- DenseMap<StringRef, SectionLambda> &SectionLambdas,
- DenseMap<char, StringRef> &Escapes);
+ void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+ void render(llvm::json::Value Data, SmallString<0> &Output);
+
+ void setUpNode(llvm::BumpPtrAllocator &Allocator,
+ StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
+ StringMap<SectionLambda> &SectionLambdas,
+ DenseMap<char, StringRef> &Escapes);
private:
+ void renderLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
+ Lambda &L);
+
+ void renderSectionLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
+ SectionLambda &L);
+
+ void renderPartial(llvm::json::Value &Contexts, SmallString<0> &Output,
+ ASTNode *Partial);
+
+ void renderChild(llvm::json::Value &Context, SmallString<0> &Output);
+
llvm::json::Value findContext();
+
+ llvm::BumpPtrAllocator *Allocator;
+ StringMap<ASTNode *> *Partials;
+ StringMap<Lambda> *Lambdas;
+ StringMap<SectionLambda> *SectionLambdas;
+ DenseMap<char, StringRef> *Escapes;
Type T;
- SmallString<128> RawBody;
- SmallString<128> Body;
+ size_t Indentation;
+ SmallString<0> RawBody;
+ SmallString<0> Body;
ASTNode *Parent;
std::vector<ASTNode *> Children;
const Accessor Accessor;
@@ -163,7 +190,7 @@ class Template {
public:
Template(StringRef TemplateStr);
- SmallString<128> render(llvm::json::Value Data);
+ StringRef render(llvm::json::Value Data);
void registerPartial(StringRef Name, StringRef Partial);
@@ -177,14 +204,14 @@ class Template {
void registerEscape(DenseMap<char, StringRef> Escapes);
private:
- DenseMap<StringRef, ASTNode *> Partials;
- DenseMap<StringRef, Lambda> Lambdas;
- DenseMap<StringRef, SectionLambda> SectionLambdas;
+ SmallString<0> Output;
+ StringMap<ASTNode *> Partials;
+ StringMap<Lambda> Lambdas;
+ StringMap<SectionLambda> SectionLambdas;
DenseMap<char, StringRef> Escapes;
llvm::BumpPtrAllocator Allocator;
ASTNode *Tree;
};
-
} // namespace mustache
} // end namespace llvm
#endif // LLVM_SUPPORT_MUSTACHE
\ No newline at end of file
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index dd6e5ebb19209c..76532161fd18b9 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -8,21 +8,23 @@
#include "llvm/Support/Mustache.h"
#include "llvm/Support/Error.h"
+#include <sstream>
using namespace llvm;
using namespace llvm::json;
using namespace llvm::mustache;
-SmallString<128> escapeString(StringRef Input,
- DenseMap<char, StringRef> &Escape) {
- SmallString<128> EscapedString("");
+SmallString<0> escapeString(StringRef Input,
+ DenseMap<char, StringRef> &Escape) {
+
+ SmallString<0> Output;
for (char C : Input) {
if (Escape.find(C) != Escape.end())
- EscapedString += Escape[C];
+ Output += Escape[C];
else
- EscapedString += C;
+ Output += C;
}
- return EscapedString;
+ return Output;
}
Accessor split(StringRef Str, char Delimiter) {
@@ -40,8 +42,19 @@ Accessor split(StringRef Str, char Delimiter) {
return Tokens;
}
+void addIndentation(llvm::SmallString<0> &PartialStr, size_t IndentationSize) {
+ std::string Indent(IndentationSize, ' ');
+ llvm::SmallString<0> Result;
+ for (size_t I = 0; I < PartialStr.size(); ++I) {
+ Result.push_back(PartialStr[I]);
+ if (PartialStr[I] == '\n' && I < PartialStr.size() - 1)
+ Result.append(Indent);
+ }
+ PartialStr = Result;
+}
+
Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
- : RawBody(RawBody), TokenBody(InnerBody) {
+ : RawBody(RawBody), TokenBody(InnerBody), Indentation(0) {
TokenType = getTokenType(Identifier);
if (TokenType == Type::Comment)
return;
@@ -53,7 +66,8 @@ Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
}
Token::Token(StringRef Str)
- : TokenType(Type::Text), RawBody(Str), Accessor({}), TokenBody(Str) {}
+ : TokenType(Type::Text), RawBody(Str), Accessor({}), TokenBody(Str),
+ Indentation(0) {}
Token::Type Token::getTokenType(char Identifier) {
switch (Identifier) {
@@ -80,8 +94,11 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
return false;
const Token &PrevToken = Tokens[Idx - 1];
StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
- return TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
- (TokenBody.empty() && Idx == 1);
+ // Check if the token body ends with a newline
+ // or if the previous token is empty and the current token is the first token
+ // eg. " {{#section}}A{{#section}}" would be considered as no text behind
+ // and should be render as "A" instead of " A"
+ return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
}
// Function to check if there's no meaningful text ahead
bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
@@ -94,6 +111,13 @@ bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
}
+bool requiresCleanUp(Token::Type T) {
+ // We must clean up all the tokens that could contain child nodes
+ return T == Token::Type::SectionOpen || T == Token::Type::InvertSectionOpen ||
+ T == Token::Type::SectionClose || T == Token::Type::Comment ||
+ T == Token::Type::Partial;
+}
+
// Simple tokenizer that splits the template into tokens
// the mustache spec allows {{{ }}} to unescape variables
// but we don't support that here unescape variable
@@ -102,8 +126,8 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
SmallVector<Token, 0> Tokens;
StringRef Open("{{");
StringRef Close("}}");
- std::size_t Start = 0;
- std::size_t DelimiterStart = Template.find(Open);
+ size_t Start = 0;
+ size_t DelimiterStart = Template.find(Open);
if (DelimiterStart == StringRef::npos) {
Tokens.emplace_back(Template);
return Tokens;
@@ -121,13 +145,9 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
// Extract the Interpolated variable without {{ and }}
size_t InterpolatedStart = DelimiterStart + Open.size();
size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
- SmallString<128> Interpolated =
+ SmallString<0> Interpolated =
Template.substr(InterpolatedStart, InterpolatedEnd);
- SmallString<128> RawBody;
- RawBody += Open;
- RawBody += Interpolated;
- RawBody += Close;
-
+ SmallString<0> RawBody({Open, Interpolated, Close});
Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
Start = DelimiterEnd + Close.size();
DelimiterStart = Template.find(Open, Start);
@@ -136,46 +156,66 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
if (Start < Template.size())
Tokens.emplace_back(Template.substr(Start));
- // fix up white spaces for
- // open sections/inverted sections/close section/comment
+ // Fix up white spaces for:
+ // open sections/inverted sections/close section/comment
// This loop attempts to find standalone tokens and tries to trim out
- // the whitespace around them
- // for example:
+ // the surrounding whitespace.
+ // For example:
// if you have the template string
// "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
// The output would be
// "Line 1\n Line 2\n Line 3"
size_t LastIdx = Tokens.size() - 1;
for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
- Token::Type CurrentType = Tokens[Idx].getType();
+ Token &CurrentToken = Tokens[Idx];
+ Token::Type CurrentType = CurrentToken.getType();
// Check if token type requires cleanup
- bool RequiresCleanUp = (CurrentType == Token::Type::SectionOpen ||
- CurrentType == Token::Type::InvertSectionOpen ||
- CurrentType == Token::Type::SectionClose ||
- CurrentType == Token::Type::Comment ||
- CurrentType == Token::Type::Partial);
+ bool RequiresCleanUp = requiresCleanUp(CurrentType);
if (!RequiresCleanUp)
continue;
+ // We adjust the token body if there's no text behind or ahead a token is
+ // considered surrounded by no text if the right of the previous token
+ // is a newline followed by spaces or if the left of the next token
+ // is spaces followed by a newline
+ // eg.
+ // "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
+
bool NoTextBehind = noTextBehind(Idx, Tokens);
bool NoTextAhead = noTextAhead(Idx, Tokens);
- // Adjust next token body if no text ahead
+ // Adjust next token body if there is no text ahead
+ // eg.
+ // The template string
+ // "{{! Comment }} \nLine 2"
+ // would be considered as no text ahead and should be render as
+ // " Line 2"
if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
Token &NextToken = Tokens[Idx + 1];
StringRef NextTokenBody = NextToken.getTokenBody();
- if (NextTokenBody.starts_with("\r\n")) {
+ // cut off the leading newline which could be \n or \r\n
+ if (NextTokenBody.starts_with("\r\n"))
NextToken.setTokenBody(NextTokenBody.substr(2));
- } else if (NextTokenBody.starts_with("\n")) {
+ else if (NextTokenBody.starts_with("\n"))
NextToken.setTokenBody(NextTokenBody.substr(1));
- }
}
- // Adjust previous token body if no text behind
- if ((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx)) {
+ // Adjust previous token body if there no text behind
+ // eg.
+ // The template string
+ // " \t{{#section}}A{{/section}}"
+ // would be considered as no text ahead and should be render as
+ // "A"
+ // The exception for this is partial tag which requires us to
+ // keep track of the indentation once it's rendered
+ if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
Token &PrevToken = Tokens[Idx - 1];
StringRef PrevTokenBody = PrevToken.getTokenBody();
- PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
+ StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
+ size_t Indentation = PrevTokenBody.size() - Unindented.size();
+ if (CurrentType != Token::Type::Partial)
+ PrevToken.setTokenBody(Unindented);
+ CurrentToken.setIndentation(Indentation);
}
}
return Tokens;
@@ -233,6 +273,7 @@ void Parser::parseMustache(ASTNode *Parent) {
}
case Token::Type::Partial: {
CurrentNode = new (Node) ASTNode(ASTNode::Partial, A, Parent);
+ CurrentNode->setIndentation(CurrentToken.getIndentation());
Parent->addChild(CurrentNode);
break;
}
@@ -241,12 +282,12 @@ void Parser::parseMustache(ASTNode *Parent) {
size_t Start = CurrentPtr;
parseMustache(CurrentNode);
size_t End = CurrentPtr;
- SmallString<128> RawBody;
+ SmallString<0> RawBody;
if (Start + 1 < End - 1) {
for (std::size_t I = Start + 1; I < End - 1; I++)
RawBody += Tokens[I].getRawBody();
} else if (Start + 1 == End - 1) {
- RawBody = Tokens[Start].getRawBody();
+ RawBody = Tokens[Start + 1].getRawBody();
}
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
@@ -257,12 +298,12 @@ void Parser::parseMustache(ASTNode *Parent) {
size_t Start = CurrentPtr;
parseMustache(CurrentNode);
size_t End = CurrentPtr;
- SmallString<128> RawBody;
+ SmallString<0> RawBody;
if (Start + 1 < End - 1) {
for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
RawBody += Tokens[Idx].getRawBody();
} else if (Start + 1 == End - 1) {
- RawBody = Tokens[Start].getRawBody();
+ RawBody = Tokens[Start + 1].getRawBody();
}
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
@@ -276,16 +317,17 @@ void Parser::parseMustache(ASTNode *Parent) {
}
}
-SmallString<128> Template::render(Value Data) {
+StringRef Template::render(Value Data) {
BumpPtrAllocator LocalAllocator;
- return Tree->render(Data, LocalAllocator, Partials, Lambdas, SectionLambdas,
- Escapes);
+ Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
+ Tree->render(Data, Output);
+ return Output.str();
}
void Template::registerPartial(StringRef Name, StringRef Partial) {
Parser P = Parser(Partial, Allocator);
ASTNode *PartialTree = P.parse();
- Partials[Name] = PartialTree;
+ Partials.insert(std::make_pair(Name, PartialTree));
}
void Template::registerLambda(StringRef Name, Lambda L) { Lambdas[Name] = L; }
@@ -308,19 +350,23 @@ Template::Template(StringRef TemplateStr) {
registerEscape(HtmlEntities);
}
-SmallString<128> printJson(Value &Data) {
-
- SmallString<128> Result;
+void toJsonString(Value &Data, SmallString<0> &Output) {
if (Data.getAsNull())
- return Result;
+ return;
if (auto *Arr = Data.getAsArray())
if (Arr->empty())
- return Result;
+ return;
if (Data.getAsString()) {
- Result += Data.getAsString()->str();
- return Result;
+ Output = Data.getAsString()->str();
+ return;
+ }
+ if (auto Num = Data.getAsNumber()) {
+ std::ostringstream Oss;
+ Oss << *Num;
+ Output = Oss.str();
+ return;
}
- return formatv("{0:2}", Data);
+ Output = formatv("{0:2}", Data);
}
bool isFalsey(Value &V) {
@@ -329,97 +375,70 @@ bool isFalsey(Value &V) {
(V.getAsObject() && V.getAsObject()->empty());
}
-SmallString<128>
-ASTNode::render(Value Data, BumpPtrAllocator &Allocator,
- DenseMap<StringRef, ASTNode *> &Partials,
- DenseMap<StringRef, Lambda> &Lambdas,
- DenseMap<StringRef, SectionLambda> &SectionLambdas,
- DenseMap<char, StringRef> &Escapes) {
+void ASTNode::render(Value Data, SmallString<0> &Output) {
LocalContext = Data;
Value Context = T == Root ? Data : findContext();
- SmallString<128> Result;
switch (T) {
- case Root: {
- for (ASTNode *Child : Children)
- Result += Child->render(Context, Allocator, Partials, Lambdas,
- SectionLambdas, Escapes);
- return Result;
- }
+ case Root:
+ renderChild(Data, Output);
+ return;
case Text:
- return Body;
+ Output = Body;
+ return;
case Partial: {
- if (Partials.find(Accessor[0]) != Partials.end()) {
- ASTNode *Partial = Partials[Accessor[0]];
- Result += Partial->render(Data, Allocator, Partials, Lambdas,
- SectionLambdas, Escapes);
- return Result;
- }
- return Result;
+ auto Partial = Partials->find(Accessor[0]);
+ if (Partial != Partials->end())
+ renderPartial(Context, Output, Partial->getValue());
+ return;
}
case Variable: {
- if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
- Lambda &L = Lambdas[Accessor[0]];
- Value LambdaResult = L();
- StringRef LambdaStr = printJson(LambdaResult);
- Parser P = Parser(LambdaStr, Allocator);
- ASTNode *LambdaNode = P.parse();
- SmallString<128> RenderStr = LambdaNode->render(
- Data, Allocator, Partials, Lambdas, SectionLambdas, Escapes);
- return escapeString(RenderStr, Escapes);
+ auto Lambda = Lambdas->find(Accessor[0]);
+ if (Lambda != Lambdas->end())
+ renderLambdas(Data, Output, Lambda->getValue());
+ else {
+ toJsonString(Context, Output);
+ Output = escapeString(Output, *Escapes);
}
- return escapeString(printJson(Context), Escapes);
+ return;
}
case UnescapeVariable: {
- if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
- Lambda &L = Lambdas[Accessor[0]];
- Value LambdaResult = L();
- StringRef LambdaStr = printJson(LambdaResult);
- Parser P = Parser(LambdaStr, Allocator);
- ASTNode *LambdaNode = P.parse();
- DenseMap<char, StringRef> EmptyEscapes;
- return LambdaNode->render(Data, Allocator, Partials, Lambdas,
- SectionLambdas, EmptyEscapes);
- }
- return printJson(Context);
+ auto Lambda = Lambdas->find(Accessor[0]);
+ if (Lambda != Lambdas->end())
+ renderLambdas(Data, Output, Lambda->getValue());
+ else
+ toJsonString(Context, Output);
+ return;
}
case Section: {
// Sections are not rendered if the context is falsey
- bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+ auto SectionLambda = SectionLambdas->find(Accessor[0]);
+ bool IsLambda = SectionLambda != SectionLambdas->end();
if (isFalsey(Context) && !IsLambda)
- return Result;
+ return;
+
if (IsLambda) {
- SectionLambda &Lambda = SectionLambdas[Accessor[0]];
- Value Return = Lambda(RawBody);
- if (isFalsey(Return))
- return Result;
- StringRef LambdaStr = printJson(Return);
- Parser P = Parser(LambdaStr.str(), Allocator);
- ASTNode *LambdaNode = P.parse();
- return LambdaNode->render(Data, Allocator, Partials, Lambdas,
- SectionLambdas, Escapes);
+ renderSectionLambdas(Data, Output, SectionLambda->getValue());
+ return;
}
+
if (Context.getAsArray()) {
+ SmallString<0> Result;
json::Array *Arr = Context.getAsArray();
- for (Value &V : *Arr) {
- for (ASTNode *Child : Children)
- Result += Child->render(V, Allocator, Partials, Lambdas,
- SectionLambdas, Escapes);
- }
- return Result;
+ for (Value &V : *Arr)
+ renderChild(V, Result);
+ Output = Result;
+ return;
}
- for (ASTNode *Child : Children)
- Result += Child->render(Context, Allocator, Partials, Lambdas,
- SectionLambdas, Escapes);
- return Result;
+
+ renderChild(Context, Output);
}
case InvertSection: {
- bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+ bool IsLambda = SectionLambdas->find(Accessor[0]) != SectionLambdas->end();
+
if (!isFalsey(Context) || IsLambda)
- return Result;
- for (ASTNode *Child : Children)
- Result += Child->render(Context, Allocator, Partials, Lambdas,
- SectionLambdas, Escapes);
- return Result;
+ return;
+
+ renderChild(Context, Output);
}
}
llvm_unreachable("Invalid ASTNode type");
@@ -427,10 +446,10 @@ ASTNode::render(Value Data, BumpPtrAllocator &Allocator,
Value ASTNode::findContext() {
// The mustache spec allows for dot notation to access nested values
- // a single dot refers to the current context
- // We attempt to find the JSON context in the current node if it is not found
- // we traverse the parent nodes to find the context until we reach the root
- // node or the context is found
+ // a single dot refers to the current context.
+ // We attempt to find the JSON context in the current node, if it is not
+ // found, then we traverse the parent nodes to find the context until we
+ // reach the root node or the context is found
if (Accessor.empty())
return nullptr;
if (Accessor[0] == ".")
@@ -448,11 +467,11 @@ Value ASTNode::findContext() {
return nullptr;
}
Value Context = nullptr;
- for (auto E : enumerate(Accessor)) {
- Value *CurrentValue = CurrentContext->get(E.value());
+ for (auto [Idx, Acc] : enumerate(Accessor)) {
+ Value *CurrentValue = CurrentContext->get(Acc);
if (!CurrentValue)
return nullptr;
- if (E.index() < Accessor.size() - 1) {
+ if (Idx < Accessor.size() - 1) {
CurrentContext = CurrentValue->getAsObject();
if (!CurrentContext)
return nullptr;
@@ -461,3 +480,61 @@ Value ASTNode::findContext() {
}
return Context;
}
+
+void ASTNode::renderChild(Value &Context, SmallString<0> &Output) {
+ for (ASTNode *Child : Children) {
+ SmallString<0> ChildResult;
+ Child->render(Context, ChildResult);
+ Output += ChildResult;
+ }
+}
+
+void ASTNode::renderPartial(Value &Context, SmallString<0> &Output,
+ ASTNode *Partial) {
+ Partial->render(Context, Output);
+ addIndentation(Output, Indentation);
+}
+
+void ASTNode::renderLambdas(Value &Context, SmallString<0> &Output, Lambda &L) {
+ Value LambdaResult = L();
+ SmallString<0> LambdaStr;
+ toJsonString(LambdaResult, LambdaStr);
+ Parser P = Parser(LambdaStr, *Allocator);
+ ASTNode *LambdaNode = P.parse();
+ LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+ *Escapes);
+ LambdaNode->render(Context, Output);
+ if (T == Variable)
+ Output = escapeString(Output, *Escapes);
+ return;
+}
+
+void ASTNode::renderSectionLambdas(Value &Contexts, SmallString<0> &Output,
+ SectionLambda &L) {
+ Value Return = L(RawBody);
+ if (isFalsey(Return))
+ return;
+ SmallString<0> LambdaStr;
+ toJsonString(Return, LambdaStr);
+ Parser P = Parser(LambdaStr, *Allocator);
+ ASTNode *LambdaNode = P.parse();
+ LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+ *Escapes);
+ LambdaNode->render(Contexts, Output);
+ return;
+}
+
+void ASTNode::setUpNode(BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Par,
+ StringMap<Lambda> &L, StringMap<SectionLambda> &SC,
+ DenseMap<char, StringRef> &E) {
+
+ // Passed down datastructures needed for rendering to
+ // the children nodes
+ Allocator = &Alloc;
+ Partials = &Par;
+ Lambdas = &L;
+ SectionLambdas = &SC;
+ Escapes = &E;
+ for (ASTNode *Child : Children)
+ Child->setUpNode(Alloc, Par, L, SC, E);
+}
>From 6b4f5cd29a687974f346c46bc02425854a14589b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:21:47 -0400
Subject: [PATCH 27/62] [llvm] clang-format
---
llvm/include/llvm/Support/Mustache.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 4cd66d08dd39fa..21e233eb12d25c 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -129,16 +129,16 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), LocalContext(nullptr){};
+ ASTNode() : T(Type::Root), LocalContext(nullptr) {};
ASTNode(StringRef Body, ASTNode *Parent)
: T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr),
- Indentation(0){};
+ Indentation(0) {};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- LocalContext(nullptr), Indentation(0){};
+ LocalContext(nullptr), Indentation(0) {};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
>From d400c29c76f05f29cc5a84c616b926876c9ed085 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 03:01:32 -0400
Subject: [PATCH 28/62] [llvm] fix errors
---
llvm/lib/Support/Mustache.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 76532161fd18b9..d4d26245433175 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -287,7 +287,7 @@ void Parser::parseMustache(ASTNode *Parent) {
for (std::size_t I = Start + 1; I < End - 1; I++)
RawBody += Tokens[I].getRawBody();
} else if (Start + 1 == End - 1) {
- RawBody = Tokens[Start + 1].getRawBody();
+ RawBody = Tokens[Start].getRawBody();
}
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
@@ -303,7 +303,7 @@ void Parser::parseMustache(ASTNode *Parent) {
for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
RawBody += Tokens[Idx].getRawBody();
} else if (Start + 1 == End - 1) {
- RawBody = Tokens[Start + 1].getRawBody();
+ RawBody = Tokens[Start].getRawBody();
}
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
@@ -388,7 +388,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
case Partial: {
auto Partial = Partials->find(Accessor[0]);
if (Partial != Partials->end())
- renderPartial(Context, Output, Partial->getValue());
+ renderPartial(Data, Output, Partial->getValue());
return;
}
case Variable: {
@@ -439,6 +439,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
return;
renderChild(Context, Output);
+ return;
}
}
llvm_unreachable("Invalid ASTNode type");
@@ -491,6 +492,8 @@ void ASTNode::renderChild(Value &Context, SmallString<0> &Output) {
void ASTNode::renderPartial(Value &Context, SmallString<0> &Output,
ASTNode *Partial) {
+ Partial->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+ *Escapes);
Partial->render(Context, Output);
addIndentation(Output, Indentation);
}
>From 8fa5fd78b0e870d128da0dfb0f235ae9cb62bf20 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 10:23:51 -0400
Subject: [PATCH 29/62] [llvm] fix more bugs
---
llvm/lib/Support/Mustache.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index d4d26245433175..4bf31db15d634a 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -94,12 +94,14 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
return false;
const Token &PrevToken = Tokens[Idx - 1];
StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
- // Check if the token body ends with a newline
- // or if the previous token is empty and the current token is the first token
- // eg. " {{#section}}A{{#section}}" would be considered as no text behind
- // and should be render as "A" instead of " A"
+ // We make a special exception for when previous token is empty
+ // and the current token is the second token
+ // ex.
+ // " {{#section}}A{{/section}}"
+ // that's why we check if the token body is empty and the index is 1
return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
}
+
// Function to check if there's no meaningful text ahead
bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
if (Idx >= Tokens.size() - 1 ||
@@ -431,6 +433,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
}
renderChild(Context, Output);
+ return;
}
case InvertSection: {
bool IsLambda = SectionLambdas->find(Accessor[0]) != SectionLambdas->end();
>From fd7c106b30289ab5966799c4524535e6aa9393bb Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 14:40:28 -0400
Subject: [PATCH 30/62] [llvm] change mustache to pass by reference
---
llvm/include/llvm/Support/Mustache.h | 32 ++++----
llvm/lib/Support/Mustache.cpp | 107 ++++++++++++++-------------
2 files changed, 73 insertions(+), 66 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 21e233eb12d25c..433e2dfe4103b9 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -129,16 +129,16 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), LocalContext(nullptr) {};
+ ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr){};
ASTNode(StringRef Body, ASTNode *Parent)
- : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr),
- Indentation(0) {};
+ : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
+ LocalContext(nullptr), Indentation(0){};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- LocalContext(nullptr), Indentation(0) {};
+ ParentContext(nullptr), LocalContext(nullptr), Indentation(0){};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
@@ -148,26 +148,27 @@ class ASTNode {
void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
- void render(llvm::json::Value Data, SmallString<0> &Output);
+ void render(const llvm::json::Value &Data, SmallString<0> &Output);
- void setUpNode(llvm::BumpPtrAllocator &Allocator,
- StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
+ void setUpNode(StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
StringMap<SectionLambda> &SectionLambdas,
DenseMap<char, StringRef> &Escapes);
+ void setUpContext(llvm::BumpPtrAllocator *Alloc);
+
private:
- void renderLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
+ void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
Lambda &L);
- void renderSectionLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
- SectionLambda &L);
+ void renderSectionLambdas(const llvm::json::Value &Contexts,
+ SmallString<0> &Output, SectionLambda &L);
- void renderPartial(llvm::json::Value &Contexts, SmallString<0> &Output,
+ void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
ASTNode *Partial);
- void renderChild(llvm::json::Value &Context, SmallString<0> &Output);
+ void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
- llvm::json::Value findContext();
+ const llvm::json::Value *findContext();
llvm::BumpPtrAllocator *Allocator;
StringMap<ASTNode *> *Partials;
@@ -181,7 +182,8 @@ class ASTNode {
ASTNode *Parent;
std::vector<ASTNode *> Children;
const Accessor Accessor;
- llvm::json::Value LocalContext;
+ const llvm::json::Value *ParentContext;
+ const llvm::json::Value *LocalContext;
};
// A Template represents the container for the AST and the partials
@@ -190,7 +192,7 @@ class Template {
public:
Template(StringRef TemplateStr);
- StringRef render(llvm::json::Value Data);
+ StringRef render(llvm::json::Value &Data);
void registerPartial(StringRef Name, StringRef Partial);
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 4bf31db15d634a..87cded1ff0822b 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -94,8 +94,8 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
return false;
const Token &PrevToken = Tokens[Idx - 1];
StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
- // We make a special exception for when previous token is empty
- // and the current token is the second token
+ // We make a special exception for when previous token is empty
+ // and the current token is the second token
// ex.
// " {{#section}}A{{/section}}"
// that's why we check if the token body is empty and the index is 1
@@ -144,7 +144,7 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
break;
}
- // Extract the Interpolated variable without {{ and }}
+ // Extract the Interpolated variable without delimiters {{ and }}
size_t InterpolatedStart = DelimiterStart + Open.size();
size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
SmallString<0> Interpolated =
@@ -285,12 +285,8 @@ void Parser::parseMustache(ASTNode *Parent) {
parseMustache(CurrentNode);
size_t End = CurrentPtr;
SmallString<0> RawBody;
- if (Start + 1 < End - 1) {
- for (std::size_t I = Start + 1; I < End - 1; I++)
- RawBody += Tokens[I].getRawBody();
- } else if (Start + 1 == End - 1) {
- RawBody = Tokens[Start].getRawBody();
- }
+ for (std::size_t I = Start; I < End - 1; I++)
+ RawBody += Tokens[I].getRawBody();
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
break;
@@ -301,12 +297,8 @@ void Parser::parseMustache(ASTNode *Parent) {
parseMustache(CurrentNode);
size_t End = CurrentPtr;
SmallString<0> RawBody;
- if (Start + 1 < End - 1) {
- for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
- RawBody += Tokens[Idx].getRawBody();
- } else if (Start + 1 == End - 1) {
- RawBody = Tokens[Start].getRawBody();
- }
+ for (size_t Idx = Start; Idx < End - 1; Idx++)
+ RawBody += Tokens[Idx].getRawBody();
CurrentNode->setRawBody(RawBody);
Parent->addChild(CurrentNode);
break;
@@ -319,9 +311,9 @@ void Parser::parseMustache(ASTNode *Parent) {
}
}
-StringRef Template::render(Value Data) {
+StringRef Template::render(Value &Data) {
BumpPtrAllocator LocalAllocator;
- Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
+ Tree->setUpContext(&LocalAllocator);
Tree->render(Data, Output);
return Output.str();
}
@@ -329,6 +321,7 @@ StringRef Template::render(Value Data) {
void Template::registerPartial(StringRef Name, StringRef Partial) {
Parser P = Parser(Partial, Allocator);
ASTNode *PartialTree = P.parse();
+ PartialTree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
Partials.insert(std::make_pair(Name, PartialTree));
}
@@ -350,9 +343,10 @@ Template::Template(StringRef TemplateStr) {
{'"', """},
{'\'', "'"}};
registerEscape(HtmlEntities);
+ Tree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
}
-void toJsonString(Value &Data, SmallString<0> &Output) {
+void toJsonString(const Value &Data, SmallString<0> &Output) {
if (Data.getAsNull())
return;
if (auto *Arr = Data.getAsArray())
@@ -371,15 +365,18 @@ void toJsonString(Value &Data, SmallString<0> &Output) {
Output = formatv("{0:2}", Data);
}
-bool isFalsey(Value &V) {
+bool isFalsey(const Value &V) {
return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
(V.getAsArray() && V.getAsArray()->empty()) ||
(V.getAsObject() && V.getAsObject()->empty());
}
-void ASTNode::render(Value Data, SmallString<0> &Output) {
- LocalContext = Data;
- Value Context = T == Root ? Data : findContext();
+void ASTNode::render(const Value &Data, SmallString<0> &Output) {
+
+ ParentContext = &Data;
+ const Value *ContextPtr = T == Root ? ParentContext : findContext();
+ const Value &Context = ContextPtr ? *ContextPtr : nullptr;
+
switch (T) {
case Root:
renderChild(Data, Output);
@@ -425,8 +422,8 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
if (Context.getAsArray()) {
SmallString<0> Result;
- json::Array *Arr = Context.getAsArray();
- for (Value &V : *Arr)
+ const json::Array *Arr = Context.getAsArray();
+ for (const Value &V : *Arr)
renderChild(V, Result);
Output = Result;
return;
@@ -448,7 +445,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
llvm_unreachable("Invalid ASTNode type");
}
-Value ASTNode::findContext() {
+const Value *ASTNode::findContext() {
// The mustache spec allows for dot notation to access nested values
// a single dot refers to the current context.
// We attempt to find the JSON context in the current node, if it is not
@@ -457,22 +454,23 @@ Value ASTNode::findContext() {
if (Accessor.empty())
return nullptr;
if (Accessor[0] == ".")
- return LocalContext;
- json::Object *CurrentContext = LocalContext.getAsObject();
+ return ParentContext;
+
+ const json::Object *CurrentContext = ParentContext->getAsObject();
StringRef CurrentAccessor = Accessor[0];
ASTNode *CurrentParent = Parent;
while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
if (CurrentParent->T != Root) {
- CurrentContext = CurrentParent->LocalContext.getAsObject();
+ CurrentContext = CurrentParent->ParentContext->getAsObject();
CurrentParent = CurrentParent->Parent;
continue;
}
return nullptr;
}
- Value Context = nullptr;
+ const Value *Context = nullptr;
for (auto [Idx, Acc] : enumerate(Accessor)) {
- Value *CurrentValue = CurrentContext->get(Acc);
+ const Value *CurrentValue = CurrentContext->get(Acc);
if (!CurrentValue)
return nullptr;
if (Idx < Accessor.size() - 1) {
@@ -480,43 +478,43 @@ Value ASTNode::findContext() {
if (!CurrentContext)
return nullptr;
} else
- Context = *CurrentValue;
+ Context = CurrentValue;
}
return Context;
}
-void ASTNode::renderChild(Value &Context, SmallString<0> &Output) {
+void ASTNode::renderChild(const Value &Contexts, SmallString<0> &Output) {
for (ASTNode *Child : Children) {
SmallString<0> ChildResult;
- Child->render(Context, ChildResult);
+ Child->render(Contexts, ChildResult);
Output += ChildResult;
}
}
-void ASTNode::renderPartial(Value &Context, SmallString<0> &Output,
+void ASTNode::renderPartial(const Value &Contexts, SmallString<0> &Output,
ASTNode *Partial) {
- Partial->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
- *Escapes);
- Partial->render(Context, Output);
+ Partial->setUpContext(Allocator);
+ Partial->render(Contexts, Output);
addIndentation(Output, Indentation);
}
-void ASTNode::renderLambdas(Value &Context, SmallString<0> &Output, Lambda &L) {
+void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
+ Lambda &L) {
Value LambdaResult = L();
SmallString<0> LambdaStr;
toJsonString(LambdaResult, LambdaStr);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
- LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
- *Escapes);
- LambdaNode->render(Context, Output);
+ LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
+ LambdaNode->setUpContext(Allocator);
+ LambdaNode->render(Contexts, Output);
if (T == Variable)
Output = escapeString(Output, *Escapes);
return;
}
-void ASTNode::renderSectionLambdas(Value &Contexts, SmallString<0> &Output,
- SectionLambda &L) {
+void ASTNode::renderSectionLambdas(const Value &Contexts,
+ SmallString<0> &Output, SectionLambda &L) {
Value Return = L(RawBody);
if (isFalsey(Return))
return;
@@ -524,23 +522,30 @@ void ASTNode::renderSectionLambdas(Value &Contexts, SmallString<0> &Output,
toJsonString(Return, LambdaStr);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
- LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
- *Escapes);
+ LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
+ LambdaNode->setUpContext(Allocator);
LambdaNode->render(Contexts, Output);
return;
}
-void ASTNode::setUpNode(BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Par,
- StringMap<Lambda> &L, StringMap<SectionLambda> &SC,
+void ASTNode::setUpNode(StringMap<ASTNode *> &Par, StringMap<Lambda> &L,
+ StringMap<SectionLambda> &SC,
DenseMap<char, StringRef> &E) {
-
// Passed down datastructures needed for rendering to
- // the children nodes
- Allocator = &Alloc;
+ // the children nodes. This must be called before rendering
Partials = &Par;
Lambdas = &L;
SectionLambdas = &SC;
Escapes = &E;
for (ASTNode *Child : Children)
- Child->setUpNode(Alloc, Par, L, SC, E);
+ Child->setUpNode(Par, L, SC, E);
+}
+
+void ASTNode::setUpContext(llvm::BumpPtrAllocator *Alloc) {
+ // Passed down the allocator to the children nodes.
+ // Each render call requires a allocator for generating
+ // Partial/Section nodes
+ Allocator = Alloc;
+ for (ASTNode *Child : Children)
+ Child->setUpContext(Alloc);
}
>From 29bba68935ef7f8cc5dff0477853ac25b8cf65bc Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 14:58:04 -0400
Subject: [PATCH 31/62] [llvm] fix failing mustache regression test
---
llvm/include/llvm/Support/Mustache.h | 6 +++---
llvm/lib/Support/Mustache.cpp | 5 +++--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 433e2dfe4103b9..6d9cc8393a2f1e 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -129,16 +129,16 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr){};
+ ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr) {};
ASTNode(StringRef Body, ASTNode *Parent)
: T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
- LocalContext(nullptr), Indentation(0){};
+ LocalContext(nullptr), Indentation(0) {};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- ParentContext(nullptr), LocalContext(nullptr), Indentation(0){};
+ ParentContext(nullptr), LocalContext(nullptr), Indentation(0) {};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 87cded1ff0822b..422ab60a672e57 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -19,8 +19,9 @@ SmallString<0> escapeString(StringRef Input,
SmallString<0> Output;
for (char C : Input) {
- if (Escape.find(C) != Escape.end())
- Output += Escape[C];
+ auto It = Escape.find(C);
+ if (It != Escape.end())
+ Output += It->getSecond();
else
Output += C;
}
>From 7eed82f343478e317b89dd9e48faeff2c9ce65f1 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 15:03:12 -0400
Subject: [PATCH 32/62] [llvm] format
---
llvm/include/llvm/Support/Mustache.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 6d9cc8393a2f1e..1b6a8c76f5218a 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -55,6 +55,7 @@
// StringRef Rendered = Template.render(Data);
// // Rendered == "Hello, World!"
// \endcode
+//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_MUSTACHE
@@ -157,6 +158,7 @@ class ASTNode {
void setUpContext(llvm::BumpPtrAllocator *Alloc);
private:
+
void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
Lambda &L);
>From e73454d198155a595efde5401ea0d344a8970765 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 15:05:24 -0400
Subject: [PATCH 33/62] [llvm] remove unused mustache member
---
llvm/include/llvm/Support/Mustache.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 1b6a8c76f5218a..8b22fdfb74cf26 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -185,7 +185,6 @@ class ASTNode {
std::vector<ASTNode *> Children;
const Accessor Accessor;
const llvm::json::Value *ParentContext;
- const llvm::json::Value *LocalContext;
};
// A Template represents the container for the AST and the partials
>From b58fbcbbcbf93c077d63af64e67977e53f17cb9c Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 15:08:47 -0400
Subject: [PATCH 34/62] [llvm] remove unused mustache member
---
llvm/include/llvm/Support/Mustache.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 8b22fdfb74cf26..50435d4a04cd0f 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -134,12 +134,12 @@ class ASTNode {
ASTNode(StringRef Body, ASTNode *Parent)
: T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
- LocalContext(nullptr), Indentation(0) {};
+ Indentation(0) {};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- ParentContext(nullptr), LocalContext(nullptr), Indentation(0) {};
+ ParentContext(nullptr), Indentation(0) {};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
>From bb4ba4096446218b33e232a87d5309a174858092 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:03:11 -0400
Subject: [PATCH 35/62] [llvm] address more comments
---
llvm/include/llvm/Support/Mustache.h | 15 +--
llvm/lib/Support/Mustache.cpp | 153 +++++++++++++++------------
2 files changed, 95 insertions(+), 73 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 50435d4a04cd0f..62c15e05850e60 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -130,7 +130,7 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr) {};
+ ASTNode() : T(Type::Root), ParentContext(nullptr) {};
ASTNode(StringRef Body, ASTNode *Parent)
: T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
@@ -151,12 +151,12 @@ class ASTNode {
void render(const llvm::json::Value &Data, SmallString<0> &Output);
- void setUpNode(StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
+ void setUpNode(llvm::BumpPtrAllocator &Alloc,
+ StringMap<ASTNode *> &Partials,
+ StringMap<Lambda> &Lambdas,
StringMap<SectionLambda> &SectionLambdas,
DenseMap<char, StringRef> &Escapes);
-
- void setUpContext(llvm::BumpPtrAllocator *Alloc);
-
+
private:
void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
@@ -212,9 +212,12 @@ class Template {
StringMap<Lambda> Lambdas;
StringMap<SectionLambda> SectionLambdas;
DenseMap<char, StringRef> Escapes;
+ // The allocator for the ASTNode Tree
llvm::BumpPtrAllocator Allocator;
+ // Allocator for each render call resets after each render
+ llvm::BumpPtrAllocator LocalAllocator;
ASTNode *Tree;
};
} // namespace mustache
} // end namespace llvm
-#endif // LLVM_SUPPORT_MUSTACHE
\ No newline at end of file
+#endif // LLVM_SUPPORT_MUSTACHE
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 422ab60a672e57..0fa88c0896f6d3 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -16,8 +16,8 @@ using namespace llvm::mustache;
SmallString<0> escapeString(StringRef Input,
DenseMap<char, StringRef> &Escape) {
-
SmallString<0> Output;
+ Output.reserve(Input.size());
for (char C : Input) {
auto It = Escape.find(C);
if (It != Escape.end())
@@ -91,24 +91,33 @@ Token::Type Token::getTokenType(char Identifier) {
// Function to check if there's no meaningful text behind
bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
- if (Idx == 0 || Tokens[Idx - 1].getType() != Token::Type::Text)
+ if(Idx == 0)
+ return false;
+
+ int PrevIdx = Idx - 1;
+ if (Tokens[PrevIdx].getType() != Token::Type::Text)
return false;
+
const Token &PrevToken = Tokens[Idx - 1];
StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
- // We make a special exception for when previous token is empty
+ // We make an exception for when previous token is empty
// and the current token is the second token
// ex.
// " {{#section}}A{{/section}}"
- // that's why we check if the token body is empty and the index is 1
+ // the output of this is
+ // "A"
return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
}
// Function to check if there's no meaningful text ahead
bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
- if (Idx >= Tokens.size() - 1 ||
- Tokens[Idx + 1].getType() != Token::Type::Text)
+ if (Idx >= Tokens.size() - 1)
return false;
-
+
+ int PrevIdx = Idx + 1;
+ if (Tokens[Idx + 1].getType() != Token::Type::Text)
+ return false;
+
const Token &NextToken = Tokens[Idx + 1];
StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
@@ -121,10 +130,47 @@ bool requiresCleanUp(Token::Type T) {
T == Token::Type::Partial;
}
-// Simple tokenizer that splits the template into tokens
-// the mustache spec allows {{{ }}} to unescape variables
-// but we don't support that here unescape variable
-// is represented only by {{& variable}}
+// Adjust next token body if there is no text ahead
+// eg.
+// The template string
+// "{{! Comment }} \nLine 2"
+// would be considered as no text ahead and should be render as
+// " Line 2"
+void stripTokenAhead(SmallVector<Token, 0>& Tokens, size_t Idx) {
+ Token &NextToken = Tokens[Idx + 1];
+ StringRef NextTokenBody = NextToken.getTokenBody();
+ // cut off the leading newline which could be \n or \r\n
+ if (NextTokenBody.starts_with("\r\n"))
+ NextToken.setTokenBody(NextTokenBody.substr(2));
+ else if (NextTokenBody.starts_with("\n"))
+ NextToken.setTokenBody(NextTokenBody.substr(1));
+}
+
+// Adjust previous token body if there no text behind
+// eg.
+// The template string
+// " \t{{#section}}A{{/section}}"
+// would be considered as no text ahead and should be render as
+// "A"
+// The exception for this is partial tag which requires us to
+// keep track of the indentation once it's rendered
+void stripTokenBefore(SmallVector<Token, 0>& Tokens,
+ size_t Idx,
+ Token& CurrentToken,
+ Token::Type CurrentType) {
+ Token &PrevToken = Tokens[Idx - 1];
+ StringRef PrevTokenBody = PrevToken.getTokenBody();
+ StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
+ size_t Indentation = PrevTokenBody.size() - Unindented.size();
+ if (CurrentType != Token::Type::Partial)
+ PrevToken.setTokenBody(Unindented);
+ CurrentToken.setIndentation(Indentation);
+}
+
+// Simple tokenizer that splits the template into tokens.
+// The mustache spec allows {{{ }}} to unescape variables
+// but we don't support that here. An unescape variable
+// is represented only by {{& variable}}.
SmallVector<Token, 0> tokenize(StringRef Template) {
SmallVector<Token, 0> Tokens;
StringRef Open("{{");
@@ -160,7 +206,11 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
Tokens.emplace_back(Template.substr(Start));
// Fix up white spaces for:
- // open sections/inverted sections/close section/comment
+ // - open sections
+ // - inverted sections
+ // - close sections
+ // - comments
+ //
// This loop attempts to find standalone tokens and tries to trim out
// the surrounding whitespace.
// For example:
@@ -178,47 +228,22 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
if (!RequiresCleanUp)
continue;
- // We adjust the token body if there's no text behind or ahead a token is
- // considered surrounded by no text if the right of the previous token
- // is a newline followed by spaces or if the left of the next token
- // is spaces followed by a newline
+ // We adjust the token body if there's no text behind or ahead.
+ // A token is considered to have no text ahead if the right of the previous
+ // token is a newline followed by spaces.
+ // A token is considered to have no text behind if the left of the next
+ // token is spaces followed by a newline.
// eg.
// "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
-
bool NoTextBehind = noTextBehind(Idx, Tokens);
bool NoTextAhead = noTextAhead(Idx, Tokens);
-
- // Adjust next token body if there is no text ahead
- // eg.
- // The template string
- // "{{! Comment }} \nLine 2"
- // would be considered as no text ahead and should be render as
- // " Line 2"
+
if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
- Token &NextToken = Tokens[Idx + 1];
- StringRef NextTokenBody = NextToken.getTokenBody();
- // cut off the leading newline which could be \n or \r\n
- if (NextTokenBody.starts_with("\r\n"))
- NextToken.setTokenBody(NextTokenBody.substr(2));
- else if (NextTokenBody.starts_with("\n"))
- NextToken.setTokenBody(NextTokenBody.substr(1));
+ stripTokenAhead(Tokens, Idx);
}
- // Adjust previous token body if there no text behind
- // eg.
- // The template string
- // " \t{{#section}}A{{/section}}"
- // would be considered as no text ahead and should be render as
- // "A"
- // The exception for this is partial tag which requires us to
- // keep track of the indentation once it's rendered
+
if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
- Token &PrevToken = Tokens[Idx - 1];
- StringRef PrevTokenBody = PrevToken.getTokenBody();
- StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
- size_t Indentation = PrevTokenBody.size() - Unindented.size();
- if (CurrentType != Token::Type::Partial)
- PrevToken.setTokenBody(Unindented);
- CurrentToken.setIndentation(Indentation);
+ stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
}
}
return Tokens;
@@ -304,25 +329,25 @@ void Parser::parseMustache(ASTNode *Parent) {
Parent->addChild(CurrentNode);
break;
}
+ case Token::Type::Comment:
+ break;
case Token::Type::SectionClose:
return;
- default:
- break;
}
}
}
StringRef Template::render(Value &Data) {
- BumpPtrAllocator LocalAllocator;
- Tree->setUpContext(&LocalAllocator);
Tree->render(Data, Output);
+ LocalAllocator.Reset();
return Output.str();
}
void Template::registerPartial(StringRef Name, StringRef Partial) {
Parser P = Parser(Partial, Allocator);
ASTNode *PartialTree = P.parse();
- PartialTree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
+ PartialTree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas,
+ Escapes);
Partials.insert(std::make_pair(Name, PartialTree));
}
@@ -344,7 +369,8 @@ Template::Template(StringRef TemplateStr) {
{'"', """},
{'\'', "'"}};
registerEscape(HtmlEntities);
- Tree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
+ Tree->setUpNode(LocalAllocator,
+ Partials, Lambdas, SectionLambdas, Escapes);
}
void toJsonString(const Value &Data, SmallString<0> &Output) {
@@ -494,7 +520,6 @@ void ASTNode::renderChild(const Value &Contexts, SmallString<0> &Output) {
void ASTNode::renderPartial(const Value &Contexts, SmallString<0> &Output,
ASTNode *Partial) {
- Partial->setUpContext(Allocator);
Partial->render(Contexts, Output);
addIndentation(Output, Indentation);
}
@@ -506,8 +531,8 @@ void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
toJsonString(LambdaResult, LambdaStr);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
- LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
- LambdaNode->setUpContext(Allocator);
+ LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+ *Escapes);
LambdaNode->render(Contexts, Output);
if (T == Variable)
Output = escapeString(Output, *Escapes);
@@ -523,30 +548,24 @@ void ASTNode::renderSectionLambdas(const Value &Contexts,
toJsonString(Return, LambdaStr);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
- LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
- LambdaNode->setUpContext(Allocator);
+ LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+ *Escapes);
LambdaNode->render(Contexts, Output);
return;
}
-void ASTNode::setUpNode(StringMap<ASTNode *> &Par, StringMap<Lambda> &L,
+void ASTNode::setUpNode(llvm::BumpPtrAllocator &Alloc,
+ StringMap<ASTNode *> &Par, StringMap<Lambda> &L,
StringMap<SectionLambda> &SC,
DenseMap<char, StringRef> &E) {
// Passed down datastructures needed for rendering to
// the children nodes. This must be called before rendering
+ Allocator = &Alloc;
Partials = &Par;
Lambdas = &L;
SectionLambdas = &SC;
Escapes = &E;
for (ASTNode *Child : Children)
- Child->setUpNode(Par, L, SC, E);
+ Child->setUpNode(Alloc, Par, L, SC, E);
}
-void ASTNode::setUpContext(llvm::BumpPtrAllocator *Alloc) {
- // Passed down the allocator to the children nodes.
- // Each render call requires a allocator for generating
- // Partial/Section nodes
- Allocator = Alloc;
- for (ASTNode *Child : Children)
- Child->setUpContext(Alloc);
-}
>From b0ce196c46d0b488afd013861889f1eaf8cfffc6 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:16:49 -0400
Subject: [PATCH 36/62] [llvm] address comments
---
llvm/include/llvm/Support/Mustache.h | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 62c15e05850e60..e475d05e108a75 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -96,7 +96,7 @@ class Token {
StringRef getRawBody() const { return RawBody; };
- void setTokenBody(SmallString<128> NewBody) { TokenBody = NewBody; };
+ void setTokenBody(StringRef NewBody) { TokenBody = NewBody.str(); };
Accessor getAccessor() const { return Accessor; };
@@ -112,7 +112,7 @@ class Token {
size_t Indentation;
Type TokenType;
// RawBody is the original string that was tokenized
- SmallString<128> RawBody;
+ SmallString<0> RawBody;
Accessor Accessor;
// TokenBody is the original string with the identifier removed
SmallString<0> TokenBody;
@@ -130,16 +130,16 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), ParentContext(nullptr) {};
+ ASTNode() : T(Type::Root), ParentContext(nullptr){};
ASTNode(StringRef Body, ASTNode *Parent)
: T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
- Indentation(0) {};
+ Indentation(0){};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- ParentContext(nullptr), Indentation(0) {};
+ ParentContext(nullptr), Indentation(0){};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
@@ -151,14 +151,12 @@ class ASTNode {
void render(const llvm::json::Value &Data, SmallString<0> &Output);
- void setUpNode(llvm::BumpPtrAllocator &Alloc,
- StringMap<ASTNode *> &Partials,
+ void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
StringMap<Lambda> &Lambdas,
StringMap<SectionLambda> &SectionLambdas,
DenseMap<char, StringRef> &Escapes);
-
+
private:
-
void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
Lambda &L);
>From 561c7eb095baf4648b0756d19fdd392f07a9586f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:28:09 -0400
Subject: [PATCH 37/62] clang-format
---
llvm/lib/Support/Mustache.cpp | 36 ++++++++++++++++-------------------
1 file changed, 16 insertions(+), 20 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 0fa88c0896f6d3..2af4307fae02a3 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -91,13 +91,13 @@ Token::Type Token::getTokenType(char Identifier) {
// Function to check if there's no meaningful text behind
bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
- if(Idx == 0)
+ if (Idx == 0)
return false;
-
+
int PrevIdx = Idx - 1;
if (Tokens[PrevIdx].getType() != Token::Type::Text)
return false;
-
+
const Token &PrevToken = Tokens[Idx - 1];
StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
// We make an exception for when previous token is empty
@@ -113,11 +113,11 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
if (Idx >= Tokens.size() - 1)
return false;
-
+
int PrevIdx = Idx + 1;
if (Tokens[Idx + 1].getType() != Token::Type::Text)
return false;
-
+
const Token &NextToken = Tokens[Idx + 1];
StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
@@ -136,7 +136,7 @@ bool requiresCleanUp(Token::Type T) {
// "{{! Comment }} \nLine 2"
// would be considered as no text ahead and should be render as
// " Line 2"
-void stripTokenAhead(SmallVector<Token, 0>& Tokens, size_t Idx) {
+void stripTokenAhead(SmallVector<Token, 0> &Tokens, size_t Idx) {
Token &NextToken = Tokens[Idx + 1];
StringRef NextTokenBody = NextToken.getTokenBody();
// cut off the leading newline which could be \n or \r\n
@@ -154,10 +154,8 @@ void stripTokenAhead(SmallVector<Token, 0>& Tokens, size_t Idx) {
// "A"
// The exception for this is partial tag which requires us to
// keep track of the indentation once it's rendered
-void stripTokenBefore(SmallVector<Token, 0>& Tokens,
- size_t Idx,
- Token& CurrentToken,
- Token::Type CurrentType) {
+void stripTokenBefore(SmallVector<Token, 0> &Tokens, size_t Idx,
+ Token &CurrentToken, Token::Type CurrentType) {
Token &PrevToken = Tokens[Idx - 1];
StringRef PrevTokenBody = PrevToken.getTokenBody();
StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
@@ -228,20 +226,20 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
if (!RequiresCleanUp)
continue;
- // We adjust the token body if there's no text behind or ahead.
+ // We adjust the token body if there's no text behind or ahead.
// A token is considered to have no text ahead if the right of the previous
// token is a newline followed by spaces.
- // A token is considered to have no text behind if the left of the next
+ // A token is considered to have no text behind if the left of the next
// token is spaces followed by a newline.
// eg.
// "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
bool NoTextBehind = noTextBehind(Idx, Tokens);
bool NoTextAhead = noTextAhead(Idx, Tokens);
-
+
if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
stripTokenAhead(Tokens, Idx);
}
-
+
if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
}
@@ -346,7 +344,7 @@ StringRef Template::render(Value &Data) {
void Template::registerPartial(StringRef Name, StringRef Partial) {
Parser P = Parser(Partial, Allocator);
ASTNode *PartialTree = P.parse();
- PartialTree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas,
+ PartialTree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas,
Escapes);
Partials.insert(std::make_pair(Name, PartialTree));
}
@@ -369,8 +367,7 @@ Template::Template(StringRef TemplateStr) {
{'"', """},
{'\'', "'"}};
registerEscape(HtmlEntities);
- Tree->setUpNode(LocalAllocator,
- Partials, Lambdas, SectionLambdas, Escapes);
+ Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
}
void toJsonString(const Value &Data, SmallString<0> &Output) {
@@ -531,7 +528,7 @@ void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
toJsonString(LambdaResult, LambdaStr);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
- LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+ LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
*Escapes);
LambdaNode->render(Contexts, Output);
if (T == Variable)
@@ -548,7 +545,7 @@ void ASTNode::renderSectionLambdas(const Value &Contexts,
toJsonString(Return, LambdaStr);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
- LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+ LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
*Escapes);
LambdaNode->render(Contexts, Output);
return;
@@ -568,4 +565,3 @@ void ASTNode::setUpNode(llvm::BumpPtrAllocator &Alloc,
for (ASTNode *Child : Children)
Child->setUpNode(Alloc, Par, L, SC, E);
}
-
>From 96f69907c1be3418f617ecc10d192538e61633ec Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:28:56 -0400
Subject: [PATCH 38/62] clang-format
---
llvm/include/llvm/Support/Mustache.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index e475d05e108a75..1130434a265e42 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -130,16 +130,16 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), ParentContext(nullptr){};
+ ASTNode() : T(Type::Root), ParentContext(nullptr) {};
ASTNode(StringRef Body, ASTNode *Parent)
: T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
- Indentation(0){};
+ Indentation(0) {};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- ParentContext(nullptr), Indentation(0){};
+ ParentContext(nullptr), Indentation(0) {};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
>From a247423a1be655c5fb432f56929361149ddbea66 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 10 Oct 2024 13:02:53 -0400
Subject: [PATCH 39/62] [llvm] factor out internal classes
---
llvm/include/llvm/Support/Mustache.h | 110 +------------------------
llvm/lib/Support/Mustache.cpp | 116 ++++++++++++++++++++++++++-
2 files changed, 116 insertions(+), 110 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 1130434a265e42..ab8c02fa732ab8 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -75,115 +75,7 @@ using Accessor = SmallVector<SmallString<0>>;
using Lambda = std::function<llvm::json::Value()>;
using SectionLambda = std::function<llvm::json::Value(StringRef)>;
-class Token {
-public:
- enum class Type {
- Text,
- Variable,
- Partial,
- SectionOpen,
- SectionClose,
- InvertSectionOpen,
- UnescapeVariable,
- Comment,
- };
-
- Token(StringRef Str);
-
- Token(StringRef RawBody, StringRef Str, char Identifier);
-
- StringRef getTokenBody() const { return TokenBody; };
-
- StringRef getRawBody() const { return RawBody; };
-
- void setTokenBody(StringRef NewBody) { TokenBody = NewBody.str(); };
-
- Accessor getAccessor() const { return Accessor; };
-
- Type getType() const { return TokenType; };
-
- void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
-
- size_t getIndentation() const { return Indentation; };
-
- static Type getTokenType(char Identifier);
-
-private:
- size_t Indentation;
- Type TokenType;
- // RawBody is the original string that was tokenized
- SmallString<0> RawBody;
- Accessor Accessor;
- // TokenBody is the original string with the identifier removed
- SmallString<0> TokenBody;
-};
-
-class ASTNode {
-public:
- enum Type {
- Root,
- Text,
- Partial,
- Variable,
- UnescapeVariable,
- Section,
- InvertSection,
- };
-
- ASTNode() : T(Type::Root), ParentContext(nullptr) {};
-
- ASTNode(StringRef Body, ASTNode *Parent)
- : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
- Indentation(0) {};
-
- // Constructor for Section/InvertSection/Variable/UnescapeVariable
- ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
- : T(T), Parent(Parent), Children({}), Accessor(Accessor),
- ParentContext(nullptr), Indentation(0) {};
-
- void addChild(ASTNode *Child) { Children.emplace_back(Child); };
-
- void setBody(StringRef NewBody) { Body = NewBody; };
-
- void setRawBody(StringRef NewBody) { RawBody = NewBody; };
-
- void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
-
- void render(const llvm::json::Value &Data, SmallString<0> &Output);
-
- void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
- StringMap<Lambda> &Lambdas,
- StringMap<SectionLambda> &SectionLambdas,
- DenseMap<char, StringRef> &Escapes);
-
-private:
- void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
- Lambda &L);
-
- void renderSectionLambdas(const llvm::json::Value &Contexts,
- SmallString<0> &Output, SectionLambda &L);
-
- void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
- ASTNode *Partial);
-
- void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
-
- const llvm::json::Value *findContext();
-
- llvm::BumpPtrAllocator *Allocator;
- StringMap<ASTNode *> *Partials;
- StringMap<Lambda> *Lambdas;
- StringMap<SectionLambda> *SectionLambdas;
- DenseMap<char, StringRef> *Escapes;
- Type T;
- size_t Indentation;
- SmallString<0> RawBody;
- SmallString<0> Body;
- ASTNode *Parent;
- std::vector<ASTNode *> Children;
- const Accessor Accessor;
- const llvm::json::Value *ParentContext;
-};
+class ASTNode;
// A Template represents the container for the AST and the partials
// and Lambdas that are registered with it.
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 2af4307fae02a3..031481955feabb 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -12,7 +12,119 @@
using namespace llvm;
using namespace llvm::json;
-using namespace llvm::mustache;
+
+namespace llvm {
+namespace mustache {
+
+class Token {
+public:
+ enum class Type {
+ Text,
+ Variable,
+ Partial,
+ SectionOpen,
+ SectionClose,
+ InvertSectionOpen,
+ UnescapeVariable,
+ Comment,
+ };
+
+ Token(StringRef Str);
+
+ Token(StringRef RawBody, StringRef Str, char Identifier);
+
+ StringRef getTokenBody() const { return TokenBody; };
+
+ StringRef getRawBody() const { return RawBody; };
+
+ void setTokenBody(StringRef NewBody) { TokenBody = NewBody.str(); };
+
+ Accessor getAccessor() const { return Accessor; };
+
+ Type getType() const { return TokenType; };
+
+ void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+ size_t getIndentation() const { return Indentation; };
+
+ static Type getTokenType(char Identifier);
+
+private:
+ size_t Indentation;
+ Type TokenType;
+ // RawBody is the original string that was tokenized
+ SmallString<0> RawBody;
+ Accessor Accessor;
+ // TokenBody is the original string with the identifier removed
+ SmallString<0> TokenBody;
+};
+
+class ASTNode {
+public:
+ enum Type {
+ Root,
+ Text,
+ Partial,
+ Variable,
+ UnescapeVariable,
+ Section,
+ InvertSection,
+ };
+
+ ASTNode() : T(Type::Root), ParentContext(nullptr) {};
+
+ ASTNode(StringRef Body, ASTNode *Parent)
+ : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
+ Indentation(0) {};
+
+ // Constructor for Section/InvertSection/Variable/UnescapeVariable
+ ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
+ : T(T), Parent(Parent), Children({}), Accessor(Accessor),
+ ParentContext(nullptr), Indentation(0) {};
+
+ void addChild(ASTNode *Child) { Children.emplace_back(Child); };
+
+ void setBody(StringRef NewBody) { Body = NewBody; };
+
+ void setRawBody(StringRef NewBody) { RawBody = NewBody; };
+
+ void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+ void render(const llvm::json::Value &Data, SmallString<0> &Output);
+
+ void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
+ StringMap<Lambda> &Lambdas,
+ StringMap<SectionLambda> &SectionLambdas,
+ DenseMap<char, StringRef> &Escapes);
+
+private:
+ void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
+ Lambda &L);
+
+ void renderSectionLambdas(const llvm::json::Value &Contexts,
+ SmallString<0> &Output, SectionLambda &L);
+
+ void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
+ ASTNode *Partial);
+
+ void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
+
+ const llvm::json::Value *findContext();
+
+ llvm::BumpPtrAllocator *Allocator;
+ StringMap<ASTNode *> *Partials;
+ StringMap<Lambda> *Lambdas;
+ StringMap<SectionLambda> *SectionLambdas;
+ DenseMap<char, StringRef> *Escapes;
+ Type T;
+ size_t Indentation;
+ SmallString<0> RawBody;
+ SmallString<0> Body;
+ ASTNode *Parent;
+ std::vector<ASTNode *> Children;
+ const Accessor Accessor;
+ const llvm::json::Value *ParentContext;
+};
SmallString<0> escapeString(StringRef Input,
DenseMap<char, StringRef> &Escape) {
@@ -565,3 +677,5 @@ void ASTNode::setUpNode(llvm::BumpPtrAllocator &Alloc,
for (ASTNode *Child : Children)
Child->setUpNode(Alloc, Par, L, SC, E);
}
+} // namespace mustache
+} // namespace llvm
>From a0e7d48e3247f596ab0103d52fe207bc59cb827e Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 10 Oct 2024 13:17:12 -0400
Subject: [PATCH 40/62] [llvm] clang-format
---
llvm/lib/Support/Mustache.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 031481955feabb..bf0c14eb6e5142 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -28,7 +28,7 @@ class Token {
UnescapeVariable,
Comment,
};
-
+
Token(StringRef Str);
Token(StringRef RawBody, StringRef Str, char Identifier);
>From 4165fec73d6b329b33b106471bffd00a2922b8a3 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 11 Oct 2024 21:29:15 -0400
Subject: [PATCH 41/62] [llvm] refactor mustache to raw_ostream
---
llvm/include/llvm/Support/Mustache.h | 2 +-
llvm/lib/Support/Mustache.cpp | 176 ++++++----
llvm/unittests/Support/MustacheTest.cpp | 442 ++++++++++++++++++------
3 files changed, 438 insertions(+), 182 deletions(-)
diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index ab8c02fa732ab8..110cacde22ca1e 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -83,7 +83,7 @@ class Template {
public:
Template(StringRef TemplateStr);
- StringRef render(llvm::json::Value &Data);
+ void render(llvm::json::Value &Data, llvm::raw_ostream &OS);
void registerPartial(StringRef Name, StringRef Partial);
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index bf0c14eb6e5142..2d7a707cc50b4f 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -8,6 +8,7 @@
#include "llvm/Support/Mustache.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
#include <sstream>
using namespace llvm;
@@ -90,7 +91,7 @@ class ASTNode {
void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
- void render(const llvm::json::Value &Data, SmallString<0> &Output);
+ void render(const llvm::json::Value &Data, llvm::raw_ostream &OS);
void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
StringMap<Lambda> &Lambdas,
@@ -98,16 +99,19 @@ class ASTNode {
DenseMap<char, StringRef> &Escapes);
private:
- void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
+ void renderLambdas(const llvm::json::Value &Contexts,
+ llvm::raw_ostream &OS,
Lambda &L);
void renderSectionLambdas(const llvm::json::Value &Contexts,
- SmallString<0> &Output, SectionLambda &L);
+ llvm::raw_ostream &OS, SectionLambda &L);
- void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
+ void renderPartial(const llvm::json::Value &Contexts,
+ llvm::raw_ostream &OS,
ASTNode *Partial);
- void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
+ void renderChild(const llvm::json::Value &Context,
+ llvm::raw_ostream &OS);
const llvm::json::Value *findContext();
@@ -126,19 +130,62 @@ class ASTNode {
const llvm::json::Value *ParentContext;
};
-SmallString<0> escapeString(StringRef Input,
- DenseMap<char, StringRef> &Escape) {
- SmallString<0> Output;
- Output.reserve(Input.size());
- for (char C : Input) {
- auto It = Escape.find(C);
- if (It != Escape.end())
- Output += It->getSecond();
- else
- Output += C;
+// Custom stream to escape strings
+class EscapeStringStream : public raw_ostream {
+public:
+ explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
+ DenseMap<char, StringRef> &Escape)
+ : Escape(Escape), WrappedStream(WrappedStream) {}
+
+protected:
+ // This is where data gets written
+ void write_impl(const char *Ptr, size_t Size) override {
+ llvm::StringRef Data(Ptr, Size);
+ for (char C : Data) {
+ auto It = Escape.find(C);
+ if (It != Escape.end())
+ WrappedStream << It->getSecond();
+ else
+ WrappedStream << C;
+ }
}
- return Output;
-}
+
+ uint64_t current_pos() const override {
+ return WrappedStream.tell();
+ }
+
+private:
+ DenseMap<char, StringRef> &Escape;
+ llvm::raw_ostream &WrappedStream;
+};
+
+
+class AddIndentationStringStream : public raw_ostream {
+public:
+ explicit AddIndentationStringStream(llvm::raw_ostream &WrappedStream,
+ size_t Indentation)
+ : Indentation(Indentation), WrappedStream(WrappedStream) {}
+
+protected:
+ // This is where data gets written
+ void write_impl(const char *Ptr, size_t Size) override {
+ llvm::StringRef Data(Ptr, Size);
+ std::string Indent(Indentation, ' ');
+ for (char C : Data) {
+ WrappedStream << C;
+ if (C == '\n')
+ WrappedStream << Indent;
+ }
+ }
+
+ uint64_t current_pos() const override {
+ return WrappedStream.tell();
+ }
+
+private:
+ size_t Indentation;
+ llvm::raw_ostream &WrappedStream;
+};
Accessor split(StringRef Str, char Delimiter) {
Accessor Tokens;
@@ -155,17 +202,6 @@ Accessor split(StringRef Str, char Delimiter) {
return Tokens;
}
-void addIndentation(llvm::SmallString<0> &PartialStr, size_t IndentationSize) {
- std::string Indent(IndentationSize, ' ');
- llvm::SmallString<0> Result;
- for (size_t I = 0; I < PartialStr.size(); ++I) {
- Result.push_back(PartialStr[I]);
- if (PartialStr[I] == '\n' && I < PartialStr.size() - 1)
- Result.append(Indent);
- }
- PartialStr = Result;
-}
-
Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
: RawBody(RawBody), TokenBody(InnerBody), Indentation(0) {
TokenType = getTokenType(Identifier);
@@ -226,8 +262,8 @@ bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
if (Idx >= Tokens.size() - 1)
return false;
- int PrevIdx = Idx + 1;
- if (Tokens[Idx + 1].getType() != Token::Type::Text)
+ int NextIdx = Idx + 1;
+ if (Tokens[NextIdx].getType() != Token::Type::Text)
return false;
const Token &NextToken = Tokens[Idx + 1];
@@ -447,10 +483,9 @@ void Parser::parseMustache(ASTNode *Parent) {
}
}
-StringRef Template::render(Value &Data) {
- Tree->render(Data, Output);
+void Template::render(Value &Data, llvm::raw_ostream &OS) {
+ Tree->render(Data, OS);
LocalAllocator.Reset();
- return Output.str();
}
void Template::registerPartial(StringRef Name, StringRef Partial) {
@@ -482,23 +517,23 @@ Template::Template(StringRef TemplateStr) {
Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
}
-void toJsonString(const Value &Data, SmallString<0> &Output) {
+void toJsonString(const Value &Data, raw_ostream &OS) {
if (Data.getAsNull())
return;
if (auto *Arr = Data.getAsArray())
if (Arr->empty())
return;
if (Data.getAsString()) {
- Output = Data.getAsString()->str();
+ OS << Data.getAsString()->str();
return;
}
if (auto Num = Data.getAsNumber()) {
std::ostringstream Oss;
Oss << *Num;
- Output = Oss.str();
+ OS << Oss.str();
return;
}
- Output = formatv("{0:2}", Data);
+ OS << formatv("{0:2}", Data);
}
bool isFalsey(const Value &V) {
@@ -507,7 +542,7 @@ bool isFalsey(const Value &V) {
(V.getAsObject() && V.getAsObject()->empty());
}
-void ASTNode::render(const Value &Data, SmallString<0> &Output) {
+void ASTNode::render(const Value &Data, raw_ostream &OS) {
ParentContext = &Data;
const Value *ContextPtr = T == Root ? ParentContext : findContext();
@@ -515,33 +550,33 @@ void ASTNode::render(const Value &Data, SmallString<0> &Output) {
switch (T) {
case Root:
- renderChild(Data, Output);
+ renderChild(Data, OS);
return;
case Text:
- Output = Body;
+ OS << Body;
return;
case Partial: {
auto Partial = Partials->find(Accessor[0]);
if (Partial != Partials->end())
- renderPartial(Data, Output, Partial->getValue());
+ renderPartial(Data, OS, Partial->getValue());
return;
}
case Variable: {
auto Lambda = Lambdas->find(Accessor[0]);
if (Lambda != Lambdas->end())
- renderLambdas(Data, Output, Lambda->getValue());
+ renderLambdas(Data, OS, Lambda->getValue());
else {
- toJsonString(Context, Output);
- Output = escapeString(Output, *Escapes);
+ EscapeStringStream ES(OS, *Escapes);
+ toJsonString(Context, ES);
}
return;
}
case UnescapeVariable: {
auto Lambda = Lambdas->find(Accessor[0]);
if (Lambda != Lambdas->end())
- renderLambdas(Data, Output, Lambda->getValue());
+ renderLambdas(Data, OS, Lambda->getValue());
else
- toJsonString(Context, Output);
+ toJsonString(Context, OS);
return;
}
case Section: {
@@ -552,29 +587,24 @@ void ASTNode::render(const Value &Data, SmallString<0> &Output) {
return;
if (IsLambda) {
- renderSectionLambdas(Data, Output, SectionLambda->getValue());
+ renderSectionLambdas(Data, OS, SectionLambda->getValue());
return;
}
if (Context.getAsArray()) {
- SmallString<0> Result;
const json::Array *Arr = Context.getAsArray();
for (const Value &V : *Arr)
- renderChild(V, Result);
- Output = Result;
+ renderChild(V, OS);
return;
}
-
- renderChild(Context, Output);
+ renderChild(Context, OS);
return;
}
case InvertSection: {
bool IsLambda = SectionLambdas->find(Accessor[0]) != SectionLambdas->end();
-
if (!isFalsey(Context) || IsLambda)
return;
-
- renderChild(Context, Output);
+ renderChild(Context, OS);
return;
}
}
@@ -619,47 +649,51 @@ const Value *ASTNode::findContext() {
return Context;
}
-void ASTNode::renderChild(const Value &Contexts, SmallString<0> &Output) {
+void ASTNode::renderChild(const Value &Contexts, llvm::raw_ostream &OS) {
for (ASTNode *Child : Children) {
- SmallString<0> ChildResult;
- Child->render(Contexts, ChildResult);
- Output += ChildResult;
+ Child->render(Contexts, OS);
}
}
-void ASTNode::renderPartial(const Value &Contexts, SmallString<0> &Output,
+void ASTNode::renderPartial(const Value &Contexts, llvm::raw_ostream &OS,
ASTNode *Partial) {
- Partial->render(Contexts, Output);
- addIndentation(Output, Indentation);
+ AddIndentationStringStream IS(OS, Partial->Indentation);
+ Partial->render(Contexts, IS);
}
-void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
+void ASTNode::renderLambdas(const Value &Contexts, llvm::raw_ostream &OS,
Lambda &L) {
Value LambdaResult = L();
- SmallString<0> LambdaStr;
- toJsonString(LambdaResult, LambdaStr);
+ std::string LambdaStr;
+ raw_string_ostream Output(LambdaStr);
+ toJsonString(LambdaResult, Output);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
*Escapes);
- LambdaNode->render(Contexts, Output);
+
+ EscapeStringStream ES(OS, *Escapes);
if (T == Variable)
- Output = escapeString(Output, *Escapes);
+ LambdaNode->render(Contexts, ES);
+ else
+ LambdaNode->render(Contexts, OS);
+
return;
}
void ASTNode::renderSectionLambdas(const Value &Contexts,
- SmallString<0> &Output, SectionLambda &L) {
+ llvm::raw_ostream &OS, SectionLambda &L) {
Value Return = L(RawBody);
if (isFalsey(Return))
return;
- SmallString<0> LambdaStr;
- toJsonString(Return, LambdaStr);
+ std::string LambdaStr;
+ raw_string_ostream Output(LambdaStr);
+ toJsonString(Return, Output);
Parser P = Parser(LambdaStr, *Allocator);
ASTNode *LambdaNode = P.parse();
LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
*Escapes);
- LambdaNode->render(Contexts, Output);
+ LambdaNode->render(Contexts, OS);
return;
}
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index d120736be40856..afa47562c5b3d6 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -23,7 +23,9 @@ TEST(MustacheInterpolation, NoInterpolation) {
// Mustache-free templates should render as-is.
Value D = {};
auto T = Template("Hello from {Mustache}!\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Hello from {Mustache}!\n", Out);
}
@@ -31,7 +33,9 @@ TEST(MustacheInterpolation, BasicInterpolation) {
// Unadorned tags should interpolate content into the template.
Value D = Object{{"subject", "World"}};
auto T = Template("Hello, {{subject}}!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Hello, World!", Out);
}
@@ -39,7 +43,9 @@ TEST(MustacheInterpolation, NoReinterpolation) {
// Interpolated tag output should not be re-interpolated.
Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
auto T = Template("{{template}}: {{planet}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("{{planet}}: Earth", Out);
}
@@ -49,7 +55,9 @@ TEST(MustacheInterpolation, HTMLEscaping) {
{"forbidden", "& \" < >"},
};
auto T = Template("These characters should be HTML escaped: {{forbidden}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
Out);
}
@@ -61,7 +69,9 @@ TEST(MustacheInterpolation, Ampersand) {
};
auto T =
Template("These characters should not be HTML escaped: {{&forbidden}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
}
@@ -69,7 +79,9 @@ TEST(MustacheInterpolation, BasicIntegerInterpolation) {
// Integers should interpolate seamlessly.
Value D = Object{{"mph", 85}};
auto T = Template("{{mph}} miles an hour!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("85 miles an hour!", Out);
}
@@ -77,7 +89,9 @@ TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
// Integers should interpolate seamlessly.
Value D = Object{{"mph", 85}};
auto T = Template("{{&mph}} miles an hour!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("85 miles an hour!", Out);
}
@@ -85,7 +99,9 @@ TEST(MustacheInterpolation, BasicDecimalInterpolation) {
// Decimals should interpolate seamlessly with proper significance.
Value D = Object{{"power", 1.21}};
auto T = Template("{{power}} jiggawatts!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("1.21 jiggawatts!", Out);
}
@@ -93,7 +109,9 @@ TEST(MustacheInterpolation, BasicNullInterpolation) {
// Nulls should interpolate as the empty string.
Value D = Object{{"cannot", nullptr}};
auto T = Template("I ({{cannot}}) be seen!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("I () be seen!", Out);
}
@@ -101,7 +119,9 @@ TEST(MustacheInterpolation, AmpersandNullInterpolation) {
// Nulls should interpolate as the empty string.
Value D = Object{{"cannot", nullptr}};
auto T = Template("I ({{&cannot}}) be seen!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("I () be seen!", Out);
}
@@ -109,7 +129,9 @@ TEST(MustacheInterpolation, BasicContextMissInterpolation) {
// Failed context lookups should default to empty strings.
Value D = Object{};
auto T = Template("I ({{cannot}}) be seen!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("I () be seen!", Out);
}
@@ -117,7 +139,9 @@ TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
// Dotted names should be considered a form of shorthand for sections.
Value D = Object{{"person", Object{{"name", "Joe"}}}};
auto T = Template("{{person.name}} == {{#person}}{{name}}{{/person}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Joe == Joe", Out);
}
@@ -125,7 +149,9 @@ TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {
// Dotted names should be considered a form of shorthand for sections.
Value D = Object{{"person", Object{{"name", "Joe"}}}};
auto T = Template("{{&person.name}} == {{#person}}{{&name}}{{/person}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Joe == Joe", Out);
}
@@ -138,7 +164,9 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
Object{{"d",
Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
auto T = Template("{{a.b.c.d.e.name}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Phil", Out);
}
@@ -146,7 +174,9 @@ TEST(MustacheInterpolation, DottedNamesBrokenChains) {
// Any falsey value prior to the last part of the name should yield ''.
Value D = Object{{"a", Object{}}};
auto T = Template("{{a.b.c}} == ");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" == ", Out);
}
@@ -155,7 +185,9 @@ TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {
Value D =
Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};
auto T = Template("{{a.b.c.name}} == ");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" == ", Out);
}
@@ -170,7 +202,9 @@ TEST(MustacheInterpolation, DottedNamesInitialResolution) {
{"b",
Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
auto T = Template("{{#a}}{{b.c.d.e.name}}{{/a}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Phil", Out);
}
@@ -179,7 +213,9 @@ TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
Value D =
Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};
auto T = Template("{{#a}}{{b.c}}{{/a}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
@@ -187,7 +223,9 @@ TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
// Dotted names shall not be parsed as single, atomic keys
Value D = Object{{"a.b", "c"}};
auto T = Template("{{a.b}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
@@ -195,7 +233,9 @@ TEST(MustacheInterpolation, DottedNamesNoMasking) {
// Dotted Names in a given context are unavailable due to dot splitting
Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};
auto T = Template("{{a.b}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("d", Out);
}
@@ -203,7 +243,9 @@ TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
// Unadorned tags should interpolate content into the template.
Value D = "world";
auto T = Template("Hello, {{.}}!\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Hello, world!\n", Out);
}
@@ -211,7 +253,9 @@ TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
// Basic interpolation should be HTML escaped.
Value D = "& \" < >";
auto T = Template("These characters should not be HTML escaped: {{&.}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
}
@@ -219,7 +263,9 @@ TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
// Integers should interpolate seamlessly.
Value D = 85;
auto T = Template("{{.}} miles an hour!\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("85 miles an hour!\n", Out);
}
@@ -227,7 +273,9 @@ TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
// Interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
auto T = Template("| {{string}} |");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| --- |", Out);
}
@@ -235,7 +283,9 @@ TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
// Interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
auto T = Template("| {{&string}} |");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| --- |", Out);
}
@@ -243,7 +293,9 @@ TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
// Standalone interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
auto T = Template(" {{string}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" ---\n", Out);
}
@@ -251,7 +303,9 @@ TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
// Standalone interpolation should not alter surrounding whitespace.
Value D = Object{{"string", "---"}};
auto T = Template(" {{&string}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" ---\n", Out);
}
@@ -259,7 +313,9 @@ TEST(MustacheInterpolation, InterpolationWithPadding) {
// Superfluous in-tag whitespace should be ignored.
Value D = Object{{"string", "---"}};
auto T = Template("|{{ string }}|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|---|", Out);
}
@@ -267,7 +323,9 @@ TEST(MustacheInterpolation, AmpersandWithPadding) {
// Superfluous in-tag whitespace should be ignored.
Value D = Object{{"string", "---"}};
auto T = Template("|{{& string }}|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|---|", Out);
}
@@ -275,35 +333,45 @@ TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
// Superfluous in-tag whitespace should be ignored.
Value D = Object{{"string", "---"}};
auto T = Template("|{{ string \n\n\n }}|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|---|", Out);
}
TEST(MustacheSections, Truthy) {
Value D = Object{{"boolean", true}};
auto T = Template("{{#boolean}}This should be rendered.{{/boolean}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("This should be rendered.", Out);
}
TEST(MustacheSections, Falsey) {
Value D = Object{{"boolean", false}};
auto T = Template("{{#boolean}}This should not be rendered.{{/boolean}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
TEST(MustacheSections, NullIsFalsey) {
Value D = Object{{"null", nullptr}};
auto T = Template("{{#null}}This should not be rendered.{{/null}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
TEST(MustacheSections, Context) {
Value D = Object{{"context", Object{{"name", "Joe"}}}};
auto T = Template("{{#context}}Hi {{name}}.{{/context}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Hi Joe.", Out);
}
@@ -313,14 +381,18 @@ TEST(MustacheSections, ParentContexts) {
{"sec", Object{{"b", "bar"}}},
{"c", Object{{"d", "baz"}}}};
auto T = Template("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("foo, bar, baz", Out);
}
TEST(MustacheSections, VariableTest) {
Value D = Object{{"foo", "bar"}};
auto T = Template("{{#foo}}{{.}} is {{foo}}{{/foo}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("bar is bar", Out);
}
@@ -341,7 +413,9 @@ TEST(MustacheSections, ListContexts) {
"{{/bottoms}}"
"{{/middles}}"
"{{/tops}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("a1.A1x.A1y.", Out);
}
@@ -370,14 +444,18 @@ TEST(MustacheSections, List) {
Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
Object{{"item", 3}}}}};
auto T = Template("{{#list}}{{item}}{{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("123", Out);
}
TEST(MustacheSections, EmptyList) {
Value D = Object{{"list", Array{}}};
auto T = Template("{{#list}}Yay lists!{{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
@@ -385,98 +463,126 @@ TEST(MustacheSections, Doubled) {
Value D = Object{{"bool", true}, {"two", "second"}};
auto T = Template("{{#bool}}\n* first\n{{/bool}}\n* "
"{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("* first\n* second\n* third\n", Out);
}
TEST(MustacheSections, NestedTruthy) {
Value D = Object{{"bool", true}};
auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| A B C D E |", Out);
}
TEST(MustacheSections, NestedFalsey) {
Value D = Object{{"bool", false}};
auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| A E |", Out);
}
TEST(MustacheSections, ContextMisses) {
Value D = Object{};
auto T = Template("[{{#missing}}Found key 'missing'!{{/missing}}]");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("[]", Out);
}
TEST(MustacheSections, ImplicitIteratorString) {
Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};
auto T = Template("{{#list}}({{.}}){{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("(a)(b)(c)(d)(e)", Out);
}
TEST(MustacheSections, ImplicitIteratorInteger) {
Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};
auto T = Template("{{#list}}({{.}}){{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("(1)(2)(3)(4)(5)", Out);
}
TEST(MustacheSections, ImplicitIteratorArray) {
Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};
auto T = Template("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("(123)(abc)", Out);
}
TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {
Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
auto T = Template("{{#list}}({{.}}){{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("(&)(")(<)(>)", Out);
}
TEST(MustacheSections, ImplicitIteratorAmpersand) {
Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
auto T = Template("{{#list}}({{&.}}){{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("(&)(\")(<)(>)", Out);
}
TEST(MustacheSections, ImplicitIteratorRootLevel) {
Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};
auto T = Template("{{#.}}({{value}}){{/.}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("(a)(b)", Out);
}
TEST(MustacheSections, DottedNamesTruthy) {
Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == Here");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Here == Here", Out);
}
TEST(MustacheSections, DottedNamesFalsey) {
Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" == ", Out);
}
TEST(MustacheSections, DottedNamesBrokenChains) {
Value D = Object{{"a", Object{}}};
auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" == ", Out);
}
TEST(MustacheSections, SurroundingWhitespace) {
Value D = Object{{"boolean", true}};
auto T = Template(" | {{#boolean}}\t|\t{{/boolean}} | \n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" | \t|\t | \n", Out);
}
@@ -492,77 +598,99 @@ TEST(MustacheSections, IndentedInlineSections) {
Value D = Object{{"boolean", true}};
auto T =
Template(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" YES\n GOOD\n", Out);
}
TEST(MustacheSections, StandaloneLines) {
Value D = Object{{"boolean", true}};
auto T = Template("| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheSections, IndentedStandaloneLines) {
Value D = Object{{"boolean", true}};
auto T = Template("| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheSections, StandaloneLineEndings) {
Value D = Object{{"boolean", true}};
auto T = Template("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|\r\n|", Out);
}
TEST(MustacheSections, StandaloneWithoutPreviousLine) {
Value D = Object{{"boolean", true}};
auto T = Template(" {{#boolean}}\n#{{/boolean}}\n/");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("#\n/", Out);
}
TEST(MustacheSections, StandaloneWithoutNewline) {
Value D = Object{{"boolean", true}};
auto T = Template("#{{#boolean}}\n/\n {{/boolean}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("#\n/\n", Out);
}
TEST(MustacheSections, Padding) {
Value D = Object{{"boolean", true}};
auto T = Template("|{{# boolean }}={{/ boolean }}|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|=|", Out);
}
TEST(MustacheInvertedSections, Falsey) {
Value D = Object{{"boolean", false}};
auto T = Template("{{^boolean}}This should be rendered.{{/boolean}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("This should be rendered.", Out);
}
TEST(MustacheInvertedSections, Truthy) {
Value D = Object{{"boolean", true}};
auto T = Template("{{^boolean}}This should not be rendered.{{/boolean}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
TEST(MustacheInvertedSections, NullIsFalsey) {
Value D = Object{{"null", nullptr}};
auto T = Template("{{^null}}This should be rendered.{{/null}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("This should be rendered.", Out);
}
TEST(MustacheInvertedSections, Context) {
Value D = Object{{"context", Object{{"name", "Joe"}}}};
auto T = Template("{{^context}}Hi {{name}}.{{/context}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
@@ -570,14 +698,18 @@ TEST(MustacheInvertedSections, List) {
Value D = Object{
{"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
auto T = Template("{{^list}}{{n}}{{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
TEST(MustacheInvertedSections, EmptyList) {
Value D = Object{{"list", Array{}}};
auto T = Template("{{^list}}Yay lists!{{/list}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Yay lists!", Out);
}
@@ -585,56 +717,72 @@ TEST(MustacheInvertedSections, Doubled) {
Value D = Object{{"bool", false}, {"two", "second"}};
auto T = Template("{{^bool}}\n* first\n{{/bool}}\n* "
"{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("* first\n* second\n* third\n", Out);
}
TEST(MustacheInvertedSections, NestedFalsey) {
Value D = Object{{"bool", false}};
auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| A B C D E |", Out);
}
TEST(MustacheInvertedSections, NestedTruthy) {
Value D = Object{{"bool", true}};
auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| A E |", Out);
}
TEST(MustacheInvertedSections, ContextMisses) {
Value D = Object{};
auto T = Template("[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("[Cannot find key 'missing'!]", Out);
}
TEST(MustacheInvertedSections, DottedNamesTruthy) {
Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == ");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" == ", Out);
}
TEST(MustacheInvertedSections, DottedNamesFalsey) {
Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Not Here == Not Here", Out);
}
TEST(MustacheInvertedSections, DottedNamesBrokenChains) {
Value D = Object{{"a", Object{}}};
auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Not Here == Not Here", Out);
}
TEST(MustacheInvertedSections, SurroundingWhitespace) {
Value D = Object{{"boolean", false}};
auto T = Template(" | {{^boolean}}\t|\t{{/boolean}} | \n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" | \t|\t | \n", Out);
}
@@ -642,7 +790,9 @@ TEST(MustacheInvertedSections, InternalWhitespace) {
Value D = Object{{"boolean", false}};
auto T = Template(
" | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" | \n | \n", Out);
}
@@ -650,49 +800,63 @@ TEST(MustacheInvertedSections, IndentedInlineSections) {
Value D = Object{{"boolean", false}};
auto T =
Template(" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" NO\n WAY\n", Out);
}
TEST(MustacheInvertedSections, StandaloneLines) {
Value D = Object{{"boolean", false}};
auto T = Template("| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheInvertedSections, StandaloneIndentedLines) {
Value D = Object{{"boolean", false}};
auto T = Template("| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
}
TEST(MustacheInvertedSections, StandaloneLineEndings) {
Value D = Object{{"boolean", false}};
auto T = Template("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|\r\n|", Out);
}
TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {
Value D = Object{{"boolean", false}};
auto T = Template(" {{^boolean}}\n^{{/boolean}}\n/");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("^\n/", Out);
}
TEST(MustacheInvertedSections, StandaloneWithoutNewline) {
Value D = Object{{"boolean", false}};
auto T = Template("^{{^boolean}}\n/\n {{/boolean}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("^\n/\n", Out);
}
TEST(MustacheInvertedSections, Padding) {
Value D = Object{{"boolean", false}};
auto T = Template("|{{^ boolean }}={{/ boolean }}|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|=|", Out);
}
@@ -700,14 +864,18 @@ TEST(MustachePartials, BasicBehavior) {
Value D = Object{};
auto T = Template("{{>text}}");
T.registerPartial("text", "from partial");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("from partial", Out);
}
TEST(MustachePartials, FailedLookup) {
Value D = Object{};
auto T = Template("{{>text}}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("", Out);
}
@@ -715,7 +883,9 @@ TEST(MustachePartials, Context) {
Value D = Object{{"text", "content"}};
auto T = Template("{{>partial}}");
T.registerPartial("partial", "*{{text}}*");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("*content*", Out);
}
@@ -725,7 +895,9 @@ TEST(MustachePartials, Recursion) {
{"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
auto T = Template("{{>node}}");
T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("X(Y())", Out);
}
@@ -734,7 +906,9 @@ TEST(MustachePartials, Nested) {
auto T = Template("{{>outer}}");
T.registerPartial("outer", "*{{a}} {{>inner}}*");
T.registerPartial("inner", "{{b}}!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("*hello world!*", Out);
}
@@ -742,7 +916,9 @@ TEST(MustachePartials, SurroundingWhitespace) {
Value D = Object{};
auto T = Template("| {{>partial}} |");
T.registerPartial("partial", "\t|\t");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("| \t|\t |", Out);
}
@@ -750,7 +926,9 @@ TEST(MustachePartials, InlineIndentation) {
Value D = Object{{"data", "|"}};
auto T = Template(" {{data}} {{> partial}}\n");
T.registerPartial("partial", "<\n<");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" | <\n<\n", Out);
}
@@ -758,7 +936,9 @@ TEST(MustachePartials, PaddingWhitespace) {
Value D = Object{{"boolean", true}};
auto T = Template("|{{> partial }}|");
T.registerPartial("partial", "[]");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|[]|", Out);
}
@@ -767,7 +947,9 @@ TEST(MustacheLambdas, BasicInterpolation) {
auto T = Template("Hello, {{lambda}}!");
Lambda L = []() -> llvm::json::Value { return "World"; };
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Hello, World!", Out);
}
@@ -776,7 +958,9 @@ TEST(MustacheLambdas, InterpolationExpansion) {
auto T = Template("Hello, {{lambda}}!");
Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Hello, World!", Out);
}
@@ -789,7 +973,9 @@ TEST(MustacheLambdas, BasicMultipleCalls) {
return I;
};
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("1 == 2 == 3", Out);
}
@@ -798,7 +984,9 @@ TEST(MustacheLambdas, Escaping) {
auto T = Template("<{{lambda}}{{&lambda}}");
Lambda L = []() -> llvm::json::Value { return ">"; };
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("<>>", Out);
}
@@ -812,7 +1000,9 @@ TEST(MustacheLambdas, Sections) {
return "no";
};
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("<yes>", Out);
}
@@ -829,7 +1019,9 @@ TEST(MustacheLambdas, SectionExpansion) {
return Result;
};
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("<-Earth->", Out);
}
@@ -844,7 +1036,9 @@ TEST(MustacheLambdas, SectionsMultipleCalls) {
return Result;
};
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("__FILE__ != __LINE__", Out);
}
@@ -853,7 +1047,9 @@ TEST(MustacheLambdas, InvertedSections) {
auto T = Template("<{{^lambda}}{{static}}{{/lambda}}>");
SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
T.registerLambda("lambda", L);
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("<>", Out);
}
@@ -861,7 +1057,9 @@ TEST(MustacheComments, Inline) {
// Comment blocks should be removed from the template.
Value D = {};
auto T = Template("12345{{! Comment Block! }}67890");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("1234567890", Out);
}
@@ -870,7 +1068,9 @@ TEST(MustacheComments, Multiline) {
Value D = {};
auto T =
Template("12345{{!\n This is a\n multi-line comment...\n}}67890\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("1234567890\n", Out);
}
@@ -878,7 +1078,9 @@ TEST(MustacheComments, Standalone) {
// All standalone comment lines should be removed.
Value D = {};
auto T = Template("Begin.\n{{! Comment Block! }}\nEnd.\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -886,7 +1088,9 @@ TEST(MustacheComments, IndentedStandalone) {
// All standalone comment lines should be removed.
Value D = {};
auto T = Template("Begin.\n {{! Indented Comment Block! }}\nEnd.\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -894,7 +1098,9 @@ TEST(MustacheComments, StandaloneLineEndings) {
// "\r\n" should be considered a newline for standalone tags.
Value D = {};
auto T = Template("|\r\n{{! Standalone Comment }}\r\n|");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("|\r\n|", Out);
}
@@ -902,7 +1108,9 @@ TEST(MustacheComments, StandaloneWithoutPreviousLine) {
// Standalone tags should not require a newline to precede them.
Value D = {};
auto T = Template(" {{! I'm Still Standalone }}\n!");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("!", Out);
}
@@ -910,7 +1118,9 @@ TEST(MustacheComments, StandaloneWithoutNewline) {
// Standalone tags should not require a newline to follow them.
Value D = {};
auto T = Template("!\n {{! I'm Still Standalone }}");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("!\n", Out);
}
@@ -918,7 +1128,9 @@ TEST(MustacheComments, MultilineStandalone) {
// All standalone comment lines should be removed.
Value D = {};
auto T = Template("Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -927,7 +1139,9 @@ TEST(MustacheComments, IndentedMultilineStandalone) {
Value D = {};
auto T =
Template("Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("Begin.\nEnd.\n", Out);
}
@@ -935,7 +1149,9 @@ TEST(MustacheComments, IndentedInline) {
// Inline comments should not strip whitespace.
Value D = {};
auto T = Template(" 12 {{! 34 }}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" 12 \n", Out);
}
@@ -943,7 +1159,11 @@ TEST(MustacheComments, SurroundingWhitespace) {
// Comment removal should preserve surrounding whitespace.
Value D = {};
auto T = Template("12345 {{! Comment Block! }} 67890");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("12345 67890", Out);
}
@@ -952,6 +1172,8 @@ TEST(MustacheComments, VariableNameCollision) {
Value D = Object{
{"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};
auto T = Template("comments never show: >{{! comment }}<");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("comments never show: ><", Out);
}
\ No newline at end of file
>From 5b0a20ab9ef08882e9d83c6ab944c58bce2738cb Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 01:47:32 -0400
Subject: [PATCH 42/62] [llvm] refactor to use os_stream
---
llvm/lib/Support/Mustache.cpp | 98 ++++++++++++-------------
llvm/unittests/Support/MustacheTest.cpp | 10 ++-
2 files changed, 54 insertions(+), 54 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 2d7a707cc50b4f..0d989c74dbac66 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -72,16 +72,15 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), ParentContext(nullptr) {};
+ ASTNode() : T(Type::Root), ParentContext(nullptr){};
ASTNode(StringRef Body, ASTNode *Parent)
- : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
- Indentation(0) {};
+ : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr){};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- ParentContext(nullptr), Indentation(0) {};
+ ParentContext(nullptr){};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
@@ -99,19 +98,16 @@ class ASTNode {
DenseMap<char, StringRef> &Escapes);
private:
- void renderLambdas(const llvm::json::Value &Contexts,
- llvm::raw_ostream &OS,
+ void renderLambdas(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
Lambda &L);
void renderSectionLambdas(const llvm::json::Value &Contexts,
llvm::raw_ostream &OS, SectionLambda &L);
- void renderPartial(const llvm::json::Value &Contexts,
- llvm::raw_ostream &OS,
+ void renderPartial(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
ASTNode *Partial);
- void renderChild(const llvm::json::Value &Context,
- llvm::raw_ostream &OS);
+ void renderChild(const llvm::json::Value &Context, llvm::raw_ostream &OS);
const llvm::json::Value *findContext();
@@ -121,7 +117,7 @@ class ASTNode {
StringMap<SectionLambda> *SectionLambdas;
DenseMap<char, StringRef> *Escapes;
Type T;
- size_t Indentation;
+ size_t Indentation = 0;
SmallString<0> RawBody;
SmallString<0> Body;
ASTNode *Parent;
@@ -135,10 +131,11 @@ class EscapeStringStream : public raw_ostream {
public:
explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
DenseMap<char, StringRef> &Escape)
- : Escape(Escape), WrappedStream(WrappedStream) {}
-
+ : Escape(Escape), WrappedStream(WrappedStream) {
+ SetUnbuffered();
+ }
+
protected:
- // This is where data gets written
void write_impl(const char *Ptr, size_t Size) override {
llvm::StringRef Data(Ptr, Size);
for (char C : Data) {
@@ -150,24 +147,23 @@ class EscapeStringStream : public raw_ostream {
}
}
- uint64_t current_pos() const override {
- return WrappedStream.tell();
- }
+ uint64_t current_pos() const override { return WrappedStream.tell(); }
private:
DenseMap<char, StringRef> &Escape;
llvm::raw_ostream &WrappedStream;
};
-
+// Custom stream to add indentation used to for rendering partials
class AddIndentationStringStream : public raw_ostream {
public:
explicit AddIndentationStringStream(llvm::raw_ostream &WrappedStream,
size_t Indentation)
- : Indentation(Indentation), WrappedStream(WrappedStream) {}
-
+ : Indentation(Indentation), WrappedStream(WrappedStream) {
+ SetUnbuffered();
+ }
+
protected:
- // This is where data gets written
void write_impl(const char *Ptr, size_t Size) override {
llvm::StringRef Data(Ptr, Size);
std::string Indent(Indentation, ' ');
@@ -177,10 +173,8 @@ class AddIndentationStringStream : public raw_ostream {
WrappedStream << Indent;
}
}
-
- uint64_t current_pos() const override {
- return WrappedStream.tell();
- }
+
+ uint64_t current_pos() const override { return WrappedStream.tell(); }
private:
size_t Indentation;
@@ -237,7 +231,14 @@ Token::Type Token::getTokenType(char Identifier) {
}
}
-// Function to check if there's no meaningful text behind
+// Function to check if there's no meaningful text behind.
+// We determine if a token has no meaningful text behind
+// if the right of previous token is empty spaces or tabs followed
+// by a newline
+// eg. "Other Stuff\n {{#Section}}"
+// We make an exception for when previous token is empty
+// and the current token is the second token
+// eg. " {{#Section}}"
bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
if (Idx == 0)
return false;
@@ -247,17 +248,15 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
return false;
const Token &PrevToken = Tokens[Idx - 1];
- StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
- // We make an exception for when previous token is empty
- // and the current token is the second token
- // ex.
- // " {{#section}}A{{/section}}"
- // the output of this is
- // "A"
+ StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v");
return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
}
// Function to check if there's no meaningful text ahead
+// We determine if a token has no meaningful text behind
+// if the left of previous token is empty spaces or tabs followed
+// by a newline
+// eg. "{{#Section}} \n"
bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
if (Idx >= Tokens.size() - 1)
return false;
@@ -298,7 +297,7 @@ void stripTokenAhead(SmallVector<Token, 0> &Tokens, size_t Idx) {
// eg.
// The template string
// " \t{{#section}}A{{/section}}"
-// would be considered as no text ahead and should be render as
+// would be considered as having no text ahead and would be render as
// "A"
// The exception for this is partial tag which requires us to
// keep track of the indentation once it's rendered
@@ -361,9 +360,11 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
// the surrounding whitespace.
// For example:
// if you have the template string
- // "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
- // The output would be
- // "Line 1\n Line 2\n Line 3"
+ // {{#section}} \n Example \n{{/section}}
+ // The output should would be
+ // Example
+ // Not:
+ // \n Example \n
size_t LastIdx = Tokens.size() - 1;
for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
Token &CurrentToken = Tokens[Idx];
@@ -384,13 +385,11 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
bool NoTextBehind = noTextBehind(Idx, Tokens);
bool NoTextAhead = noTextAhead(Idx, Tokens);
- if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
+ if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0))
stripTokenAhead(Tokens, Idx);
- }
- if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
+ if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx)))
stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
- }
}
return Tokens;
}
@@ -650,9 +649,8 @@ const Value *ASTNode::findContext() {
}
void ASTNode::renderChild(const Value &Contexts, llvm::raw_ostream &OS) {
- for (ASTNode *Child : Children) {
+ for (ASTNode *Child : Children)
Child->render(Contexts, OS);
- }
}
void ASTNode::renderPartial(const Value &Contexts, llvm::raw_ostream &OS,
@@ -671,18 +669,18 @@ void ASTNode::renderLambdas(const Value &Contexts, llvm::raw_ostream &OS,
ASTNode *LambdaNode = P.parse();
LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
*Escapes);
-
+
EscapeStringStream ES(OS, *Escapes);
- if (T == Variable)
+ if (T == Variable) {
LambdaNode->render(Contexts, ES);
- else
- LambdaNode->render(Contexts, OS);
-
+ return;
+ }
+ LambdaNode->render(Contexts, OS);
return;
}
-void ASTNode::renderSectionLambdas(const Value &Contexts,
- llvm::raw_ostream &OS, SectionLambda &L) {
+void ASTNode::renderSectionLambdas(const Value &Contexts, llvm::raw_ostream &OS,
+ SectionLambda &L) {
Value Return = L(RawBody);
if (isFalsey(Return))
return;
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index afa47562c5b3d6..1883bd5b401fcc 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -434,7 +434,9 @@ TEST(MustacheSections, DeeplyNestedContexts) {
"five}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/"
"d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/"
"c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ("1\n121\n12321\n1234321\n123454321\n12345654321\n123454321\n1234321"
"\n12321\n121\n1\n",
Out);
@@ -590,7 +592,9 @@ TEST(MustacheSections, InternalWhitespace) {
Value D = Object{{"boolean", true}};
auto T = Template(
" | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
- auto Out = T.render(D);
+ std::string Out;
+ raw_string_ostream OS(Out);
+ T.render(D, OS);
EXPECT_EQ(" | \n | \n", Out);
}
@@ -1161,8 +1165,6 @@ TEST(MustacheComments, SurroundingWhitespace) {
auto T = Template("12345 {{! Comment Block! }} 67890");
std::string Out;
raw_string_ostream OS(Out);
- std::string Out;
- raw_string_ostream OS(Out);
T.render(D, OS);
EXPECT_EQ("12345 67890", Out);
}
>From ba4a6db09fb38f393d10775fd2e9163d2519a98b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 01:56:35 -0400
Subject: [PATCH 43/62] [llvm] clang-format
---
llvm/lib/Support/Mustache.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 0d989c74dbac66..f74877b695a3a2 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -72,15 +72,15 @@ class ASTNode {
InvertSection,
};
- ASTNode() : T(Type::Root), ParentContext(nullptr){};
+ ASTNode() : T(Type::Root), ParentContext(nullptr) {};
ASTNode(StringRef Body, ASTNode *Parent)
- : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr){};
+ : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr) {};
// Constructor for Section/InvertSection/Variable/UnescapeVariable
ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
: T(T), Parent(Parent), Children({}), Accessor(Accessor),
- ParentContext(nullptr){};
+ ParentContext(nullptr) {};
void addChild(ASTNode *Child) { Children.emplace_back(Child); };
>From 362728f8b029a9a404ecff2f7c3e6932f7da5424 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 03:45:32 -0400
Subject: [PATCH 44/62] [llvm] fix indentation bug
---
llvm/lib/Support/Mustache.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index f74877b695a3a2..29b40dd1e1f7b0 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -655,7 +655,7 @@ void ASTNode::renderChild(const Value &Contexts, llvm::raw_ostream &OS) {
void ASTNode::renderPartial(const Value &Contexts, llvm::raw_ostream &OS,
ASTNode *Partial) {
- AddIndentationStringStream IS(OS, Partial->Indentation);
+ AddIndentationStringStream IS(OS, Indentation);
Partial->render(Contexts, IS);
}
>From dca40c5e1f1c4361ecad64338be7cd316171c12d Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:10:26 -0400
Subject: [PATCH 45/62] [llvm] add mustache verification tool
---
llvm/tools/mustache/CMakeLists.txt | 3 +
llvm/tools/mustache/mustache.cpp | 102 +++++++++++++++++++++++++++++
2 files changed, 105 insertions(+)
create mode 100644 llvm/tools/mustache/CMakeLists.txt
create mode 100644 llvm/tools/mustache/mustache.cpp
diff --git a/llvm/tools/mustache/CMakeLists.txt b/llvm/tools/mustache/CMakeLists.txt
new file mode 100644
index 00000000000000..721b553741d05b
--- /dev/null
+++ b/llvm/tools/mustache/CMakeLists.txt
@@ -0,0 +1,3 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+ add_llvm_tool(mustache mustache.cpp)
\ No newline at end of file
diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
new file mode 100644
index 00000000000000..07b7c0dc528696
--- /dev/null
+++ b/llvm/tools/mustache/mustache.cpp
@@ -0,0 +1,102 @@
+//===- mustache.cpp - The LLVM Modular Optimizer
+//-------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Simple drivers to test the mustache spec found here
+// https://github.com/mustache/
+// It is used to verify that the current implementation conforms to the spec
+// simply download the spec and pass the test files to the driver
+//
+// Currently Triple Mustache is not supported we expect the following spec
+// test to fail:
+// Triple Mustache
+// Triple Mustache Integer Interpolation
+// Triple Mustache Decimal Interpolation
+// Triple Mustache Null Interpolation
+// Triple Mustache Context Miss Interpolation
+// Dotted Names - Triple Mustache Interpolation
+// Implicit Iterators - Triple Mustache
+// Triple Mustache - Surrounding Whitespace
+// Triple Mustache - Standalone
+// Triple Mustache With Padding
+// Standalone Indentation
+// Implicit Iterator - Triple mustache
+//
+// Usage:
+// mustache path/to/test/file/test.json path/to/test/file/test2.json ...
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Mustache.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <string>
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::mustache;
+
+cl::list<std::string> InputFiles(cl::Positional, cl::desc("<input files>"),
+ cl::OneOrMore);
+
+void runThroughTest(StringRef InputFile) {
+ llvm::outs() << "Running Tests: " << InputFile << "\n";
+ ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferOrError =
+ MemoryBuffer::getFile(InputFile);
+
+ if (auto EC = BufferOrError.getError()) {
+ return;
+ }
+ std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
+ llvm::StringRef FileContent = Buffer->getBuffer();
+ Expected<Value> Json = parse(FileContent);
+
+ if (auto E = Json.takeError()) {
+ errs() << "Parsing error: " << toString(std::move(E)) << "\n";
+ return;
+ }
+ // Get test
+ Array *Obj = (*Json).getAsObject()->getArray("tests");
+ size_t Total = 0;
+ size_t Success = 0;
+ for (Value V : *Obj) {
+ Object *TestCase = V.getAsObject();
+ StringRef TemplateStr = TestCase->getString("template").value();
+ StringRef ExpectedStr = TestCase->getString("expected").value();
+ StringRef Name = TestCase->getString("name").value();
+ Value *Data = TestCase->get("data");
+ Value *Partials = TestCase->get("partials");
+
+ if (!Data)
+ continue;
+
+ Template T = Template(TemplateStr);
+ if (Partials) {
+ for (auto PartialPairs : *Partials->getAsObject()) {
+ StringRef Partial = PartialPairs.getSecond().getAsString().value();
+ StringRef Str = llvm::StringRef(PartialPairs.getFirst());
+ T.registerPartial(Str, Partial);
+ }
+ }
+ StringRef ActualStr = T.render(*Data);
+ if (ExpectedStr == ActualStr) {
+ Success++;
+ } else {
+ llvm::outs() << "Test Failed: " << Name << "\n";
+ }
+ Total++;
+ }
+
+ llvm::outs() << "Result " << Success << "/" << Total << " succeeded\n";
+}
+int main(int argc, char **argv) {
+ llvm::cl::ParseCommandLineOptions(argc, argv);
+ for (const auto &FileName : InputFiles) {
+ runThroughTest(FileName);
+ }
+ return 0;
+}
\ No newline at end of file
>From 6fffeb23fc5fb0a153f51f02d05574751cb66590 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 03:41:22 -0400
Subject: [PATCH 46/62] [llvm] modify mustache tool
---
llvm/tools/mustache/CMakeLists.txt | 2 +-
llvm/tools/mustache/mustache.cpp | 10 ++++++----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/llvm/tools/mustache/CMakeLists.txt b/llvm/tools/mustache/CMakeLists.txt
index 721b553741d05b..81e42264d48308 100644
--- a/llvm/tools/mustache/CMakeLists.txt
+++ b/llvm/tools/mustache/CMakeLists.txt
@@ -1,3 +1,3 @@
set(LLVM_LINK_COMPONENTS Support)
- add_llvm_tool(mustache mustache.cpp)
\ No newline at end of file
+add_llvm_tool(mustache mustache.cpp)
\ No newline at end of file
diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
index 07b7c0dc528696..1fd106418d9cc4 100644
--- a/llvm/tools/mustache/mustache.cpp
+++ b/llvm/tools/mustache/mustache.cpp
@@ -82,12 +82,14 @@ void runThroughTest(StringRef InputFile) {
T.registerPartial(Str, Partial);
}
}
- StringRef ActualStr = T.render(*Data);
- if (ExpectedStr == ActualStr) {
+ std::string ActualStr;
+ llvm::raw_string_ostream Stream(ActualStr);
+ T.render(*Data, Stream);
+ if (ExpectedStr == ActualStr)
Success++;
- } else {
+ else
llvm::outs() << "Test Failed: " << Name << "\n";
- }
+
Total++;
}
>From 7337a99ce8027fd220451df96c1095958d91b5a4 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:10:26 -0400
Subject: [PATCH 47/62] [llvm] add mustache verification tool
---
llvm/tools/mustache/mustache.cpp | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
index 1fd106418d9cc4..4c893437419e0b 100644
--- a/llvm/tools/mustache/mustache.cpp
+++ b/llvm/tools/mustache/mustache.cpp
@@ -83,22 +83,19 @@ void runThroughTest(StringRef InputFile) {
}
}
std::string ActualStr;
- llvm::raw_string_ostream Stream(ActualStr);
- T.render(*Data, Stream);
+ llvm::raw_string_ostream OS(ActualStr);
+ T.render(*Data, OS);
if (ExpectedStr == ActualStr)
Success++;
else
llvm::outs() << "Test Failed: " << Name << "\n";
-
Total++;
}
-
llvm::outs() << "Result " << Success << "/" << Total << " succeeded\n";
}
int main(int argc, char **argv) {
llvm::cl::ParseCommandLineOptions(argc, argv);
- for (const auto &FileName : InputFiles) {
+ for (const auto &FileName : InputFiles)
runThroughTest(FileName);
- }
return 0;
}
\ No newline at end of file
>From 439955b5fecd3666a80b3f1a71ae63bafd101079 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:10:26 -0400
Subject: [PATCH 48/62] [llvm] add mustache verification tool
---
llvm/tools/mustache/mustache.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
index 4c893437419e0b..6d01d6b88ec9e6 100644
--- a/llvm/tools/mustache/mustache.cpp
+++ b/llvm/tools/mustache/mustache.cpp
@@ -85,17 +85,20 @@ void runThroughTest(StringRef InputFile) {
std::string ActualStr;
llvm::raw_string_ostream OS(ActualStr);
T.render(*Data, OS);
- if (ExpectedStr == ActualStr)
+ if (ExpectedStr == ActualStr) {
Success++;
- else
+ } else {
llvm::outs() << "Test Failed: " << Name << "\n";
+ }
Total++;
}
+
llvm::outs() << "Result " << Success << "/" << Total << " succeeded\n";
}
int main(int argc, char **argv) {
llvm::cl::ParseCommandLineOptions(argc, argv);
- for (const auto &FileName : InputFiles)
+ for (const auto &FileName : InputFiles) {
runThroughTest(FileName);
+ }
return 0;
}
\ No newline at end of file
>From 20045a90a3c3b758e63593fd3d08e04c673f28d0 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 02:26:22 -0400
Subject: [PATCH 49/62] [clang-doc] init mustache implementation
---
clang-tools-extra/clang-doc/CMakeLists.txt | 1 +
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 3 +-
.../clang-doc/HTMLMustacheGenerator.cpp | 349 ++++++++++++++++++
clang-tools-extra/clang-doc/Representation.h | 4 +-
.../clang-doc/assets/template.mustache | 23 ++
.../clang-doc/tool/CMakeLists.txt | 1 +
.../clang-doc/tool/ClangDocMain.cpp | 31 ++
7 files changed, 410 insertions(+), 2 deletions(-)
create mode 100644 clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
create mode 100644 clang-tools-extra/clang-doc/assets/template.mustache
diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 975ad8e242e490..82aded940222db 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -15,6 +15,7 @@ add_clang_library(clangDoc
Representation.cpp
Serialize.cpp
YAMLGenerator.cpp
+ HTMLMustacheGenerator.cpp
DEPENDS
omp_gen
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index e3532559a32fcc..ed65ac965114b5 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -1133,7 +1133,8 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
return llvm::Error::success();
}
-static llvm::Error copyFile(StringRef FilePath, StringRef OutDirectory) {
+static llvm::Error
+copyFile(StringRef FilePath, StringRef OutDirectory) {
llvm::SmallString<128> PathWrite;
llvm::sys::path::native(OutDirectory, PathWrite);
llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
new file mode 100644
index 00000000000000..63def07c5fa804
--- /dev/null
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -0,0 +1,349 @@
+//===-- HTMLMustacheGenerator.cpp - HTML Mustache 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"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Mustache.h"
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::mustache;
+
+namespace clang {
+namespace doc {
+
+
+class MustacheHTMLGenerator : public Generator {
+public:
+ static const char *Format;
+ llvm::Error generateDocs(StringRef RootDir,
+ llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
+ const ClangDocContext &CDCtx) override;
+ llvm::Error createResources(ClangDocContext &CDCtx) override;
+ llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
+ const ClangDocContext &CDCtx) override;
+
+};
+
+class MustacheTemplateFile : public Template {
+public:
+ static ErrorOr<std::unique_ptr<MustacheTemplateFile>> createMustacheFile
+ (StringRef FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
+ MemoryBuffer::getFile(FileName);
+
+ if (auto EC = BufferOrError.getError()) {
+ return EC;
+ }
+ std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
+ llvm::StringRef FileContent = Buffer->getBuffer();
+ return std::make_unique<MustacheTemplateFile>(FileContent);
+ }
+
+ Error registerPartialFile(StringRef Name, StringRef FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
+ MemoryBuffer::getFile(FileName);
+ if (auto EC = BufferOrError.getError()) {
+ return llvm::createFileError("cannot open file", EC);
+ }
+ std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
+ llvm::StringRef FileContent = Buffer->getBuffer();
+ registerPartial(Name, FileContent);
+ }
+
+ MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
+};
+
+static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
+
+static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
+
+
+llvm::Error
+setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
+ auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
+ auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
+ if (auto EC = Template.getError()) {
+ return llvm::createFileError("cannot open file", EC);
+ }
+ NamespaceTemplate = std::move(Template.get());
+}
+
+llvm::Error
+MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
+ llvm::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.
+ llvm::StringSet<> CreatedDirs;
+ // Collect all output by file name and create the necessary 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(""));
+ if (!CreatedDirs.contains(Path)) {
+ 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,
+ llvm::sys::fs::OF_None);
+ if (FileErr) {
+ return llvm::createStringError(FileErr, "Error opening file '%s'",
+ Group.getKey().str().c_str());
+ }
+ for (const auto &Info : Group.getValue()) {
+ if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+ return Err;
+ }
+ }
+ }
+}
+
+Value extractValue(const Location &L,
+ std::optional<StringRef> RepositoryUrl = std::nullopt) {
+ Object Obj = Object();
+ if (!L.IsFileInRootDir || !RepositoryUrl) {
+ Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"Filename", L.Filename});
+ }
+ SmallString<128> FileURL(*RepositoryUrl);
+ llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+ Obj.insert({"FileURL", FileURL});
+}
+
+Value extractValue(const Reference &I, StringRef CurrentDirectory) {
+ llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
+ llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
+ llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
+ Object Obj = Object();
+ Obj.insert({"Link", Path});
+ Obj.insert({"Name", I.Name});
+ return Obj;
+}
+
+
+Value extractValue(const TypedefInfo &I) {
+ // Not Supported
+ return nullptr;
+}
+
+Value extractValue(const CommentInfo &I) {
+ Object Obj = Object();
+ Value Child = Object();
+
+ if (I.Kind == "FullComment") {
+ Value ChildArr = Array();
+ for (const auto& C: I.Children)
+ ChildArr.getAsArray()->emplace_back(extractValue(*C));
+ Child.getAsObject()->insert({"Children", ChildArr});
+ Obj.insert({"FullComment", Child});
+ }
+ if (I.Kind == "ParagraphComment") {
+ Value ChildArr = Array();
+ for (const auto& C: I.Children)
+ ChildArr.getAsArray()->emplace_back(extractValue(*C));
+ Child.getAsObject()->insert({"Children", ChildArr});
+ Obj.insert({"ParagraphComment", Child});
+ }
+ if (I.Kind == "BlockCommandComment") {
+ Child.getAsObject()->insert({"Command", I.Name});
+ Value ChildArr = Array();
+ for (const auto& C: I.Children)
+ ChildArr.getAsArray()->emplace_back(extractValue(*C));
+ Child.getAsObject()->insert({"Children", ChildArr});
+ Obj.insert({"BlockCommandComment", Child});
+ }
+ if (I.Kind == "TextComment")
+ Obj.insert({"TextComment", I.Text});
+
+ return Obj;
+}
+
+Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
+ const ClangDocContext &CDCtx) {
+ Object Obj = Object();
+ Obj.insert({"Name", I.Name});
+ Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+ Obj.insert({"Access", getAccessSpelling(I.Access).str()});
+ Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
+
+ Value ParamArr = Array();
+ for (const auto &P : I.Params) {
+ ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+ }
+ Obj.insert({"Params", ParamArr});
+
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ Obj.insert({"FunctionComments", ArrDesc});
+ }
+ if (I.DefLoc) {
+ if (!CDCtx.RepositoryUrl)
+ Obj.insert({"Location", extractValue(*I.DefLoc)});
+ else
+ Obj.insert({"Location", extractValue(*I.DefLoc,
+ StringRef{*CDCtx.RepositoryUrl})});
+ }
+ return Obj;
+}
+
+Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
+ Object Obj = Object();
+ std::string EnumType = I.Scoped ? "enum class " : "enum ";
+ EnumType += I.Name;
+ bool HasComment = std::any_of(
+ I.Members.begin(), I.Members.end(),
+ [](const EnumValueInfo &M) { return !M.Description.empty(); });
+ Obj.insert({"EnumName", EnumType});
+ Obj.insert({"HasComment", HasComment});
+ Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+
+ Value Arr = Array();
+ for (const EnumValueInfo& M: I.Members) {
+ Value EnumValue = Object();
+ EnumValue.getAsObject()->insert({"Name", M.Name});
+ if (!M.ValueExpr.empty())
+ EnumValue.getAsObject()->insert({"ValueExpr", M.ValueExpr});
+ else
+ EnumValue.getAsObject()->insert({"Value", M.Value});
+
+ if (!M.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : M.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ EnumValue.getAsObject()->insert({"EnumValueComments", ArrDesc});
+ }
+ Arr.getAsArray()->emplace_back(EnumValue);
+ }
+ Obj.insert({"EnumValues", Arr});
+
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ Obj.insert({"EnumComments", ArrDesc});
+ }
+
+ if (I.DefLoc) {
+ if (!CDCtx.RepositoryUrl)
+ Obj.insert({"Location", extractValue(*I.DefLoc)});
+ else
+ Obj.insert({"Location", extractValue(*I.DefLoc,
+ StringRef{*CDCtx.RepositoryUrl})});
+ }
+
+ return Obj;
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+ Object NamespaceValue = Object();
+ std::string InfoTitle;
+ if (I.Name.str() == "")
+ InfoTitle = "Global Namespace";
+ else
+ InfoTitle = ("namespace " + I.Name).str();
+
+ StringRef BasePath = I.getRelativeFilePath("");
+ NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+ NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
+
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ NamespaceValue.insert({"NamespaceComments", ArrDesc });
+ }
+
+ Value ArrNamespace = Array();
+ for (const Reference& Child : I.Children.Namespaces)
+ ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ NamespaceValue.insert({"Namespace", ArrNamespace});
+
+ Value ArrRecord = Array();
+ for (const Reference& Child : I.Children.Records)
+ ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ NamespaceValue.insert({"Record", ArrRecord});
+
+ Value ArrFunction = Array();
+ for (const FunctionInfo& Child : I.Children.Functions)
+ ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
+ CDCtx));
+ NamespaceValue.insert({"Function", ArrRecord});
+
+ Value ArrEnum = Array();
+ for (const EnumInfo& Child : I.Children.Enums)
+ ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
+ NamespaceValue.insert({"Enums", ArrEnum });
+
+ Value ArrTypedefs = Array();
+ for (const TypedefInfo& Child : I.Children.Typedefs)
+ ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+ NamespaceValue.insert({"Typedefs", ArrTypedefs });
+
+}
+
+
+
+llvm::Error
+MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
+ const ClangDocContext &CDCtx) {
+ switch (I->IT) {
+ case InfoType::IT_namespace: {
+ Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+ OS << NamespaceTemplate->render(V);
+ break;
+ }
+ case InfoType::IT_record:
+ break;
+ case InfoType::IT_enum:
+ break;
+ case InfoType::IT_function:
+ break;
+ case InfoType::IT_typedef:
+ break;
+ case InfoType::IT_default:
+ return createStringError(llvm::inconvertibleErrorCode(),
+ "unexpected InfoType");
+ }
+ return llvm::Error::success();
+}
+
+llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+
+}
+
+const char *MustacheHTMLGenerator::Format = "mustache";
+
+
+static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
+ "Generator for mustache 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
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index bb0c534af7b740..3175b8209e3be3 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -521,8 +521,10 @@ struct ClangDocContext {
// Path of CSS stylesheets that will be copied to OutDirectory and used to
// style all HTML files.
std::vector<std::string> UserStylesheets;
- // JavaScript files that will be imported in allHTML file.
+ // JavaScript files that will be imported in all HTML file.
std::vector<std::string> JsScripts;
+ // Mustache Template files
+ llvm::StringMap<std::string> MustacheTemplates;
Index Idx;
};
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
new file mode 100644
index 00000000000000..af4c60182ae52c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -0,0 +1,23 @@
+{{!
+ 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
+
+ This file defines the template
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+ <head>
+ <meta charset="utf-8"/>
+ <title>{{NamespaceTitle}}</title>
+ </head>
+ {{#NamespaceComments}}
+ <p>Namespace Comment Present!</p>
+ {{/NamespaceComments}}
+ {{#Namespace}}
+ <p>Namespace Present!</p>
+ {{/Namespace}}
+ {{#Record}}
+ <p>Record Present!</p>
+ {{/Record}}
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 601a0460d76b38..8eb067dbe6de87 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,6 +21,7 @@ target_link_libraries(clang-doc
set(assets
index.js
+ template.mustache
clang-doc-default-stylesheet.css
)
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 04773fb8a6e721..fc5a9529111b16 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -205,6 +205,30 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
return getDefaultAssetFiles(Argv0, CDCtx);
}
+llvm::Error getMustacheHtmlFiles(const char *Argv0,
+ clang::doc::ClangDocContext &CDCtx) {
+ if (!UserAssetPath.empty() &&
+ !llvm::sys::fs::is_directory(std::string(UserAssetPath)))
+ llvm::outs() << "Asset path supply is not a directory: " << UserAssetPath
+ << " falling back to default\n";
+ if (llvm::sys::fs::is_directory(std::string(UserAssetPath)))
+ return getAssetFiles(CDCtx);
+
+ void *MainAddr = (void *)(intptr_t)getExecutablePath;
+ std::string ClangDocPath = getExecutablePath(Argv0, MainAddr);
+ llvm::SmallString<128> NativeClangDocPath;
+ llvm::sys::path::native(ClangDocPath, NativeClangDocPath);
+
+ llvm::SmallString<128> AssetsPath;
+ AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
+ llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
+
+ llvm::SmallString<128> MustacheTemplate;
+ llvm::sys::path::native(AssetsPath, MustacheTemplate);
+ llvm::sys::path::append(MustacheTemplate, "template.mustache");
+ CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+}
+
/// Make the output of clang-doc deterministic by sorting the children of
/// namespaces and records.
void sortUsrToInfo(llvm::StringMap<std::unique_ptr<doc::Info>> &USRToInfo) {
@@ -277,6 +301,13 @@ Example usage for a project using a compile commands database:
return 1;
}
}
+
+ if (Format == "mhtml") {
+ if (auto Err = getMustacheHtmlFiles(argv[0], CDCtx)) {
+ llvm::errs() << toString(std::move(Err)) << "\n";
+ return 1;
+ }
+ }
// Mapping phase
llvm::outs() << "Mapping decls...\n";
>From adda08b4b88889d9ec975ce94c54923985e41d7e Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 17:50:03 -0400
Subject: [PATCH 50/62] [clang-doc] add a mustache backend init implementation
---
clang-tools-extra/clang-doc/Generators.cpp | 4 +-
.../clang-doc/HTMLMustacheGenerator.cpp | 32 ++++++++++++----
.../clang-doc/assets/template.mustache | 37 +++++++++++++++++--
.../clang-doc/tool/ClangDocMain.cpp | 9 ++++-
.../Inputs/basic-project/include/Shape.h | 2 -
5 files changed, 68 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/clang-doc/Generators.cpp b/clang-tools-extra/clang-doc/Generators.cpp
index a3986b66f3c742..ae5b556d17063b 100644
--- a/clang-tools-extra/clang-doc/Generators.cpp
+++ b/clang-tools-extra/clang-doc/Generators.cpp
@@ -100,12 +100,14 @@ void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
extern volatile int YAMLGeneratorAnchorSource;
extern volatile int MDGeneratorAnchorSource;
extern volatile int HTMLGeneratorAnchorSource;
+extern volatile int MHTMLGeneratorAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest =
YAMLGeneratorAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest =
MDGeneratorAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED HTMLGeneratorAnchorDest =
HTMLGeneratorAnchorSource;
-
+static int LLVM_ATTRIBUTE_UNUSED MHTMLGeneratorAnchorDest =
+ MHTMLGeneratorAnchorSource;
} // namespace doc
} // namespace clang
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 63def07c5fa804..bfa563ebdc7b37 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -54,6 +54,7 @@ class MustacheTemplateFile : public Template {
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
+ return llvm::Error::success();
}
MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -72,6 +73,7 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
return llvm::createFileError("cannot open file", EC);
}
NamespaceTemplate = std::move(Template.get());
+ return llvm::Error::success();
}
llvm::Error
@@ -118,6 +120,7 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
}
}
}
+ return llvm::Error::success();
}
Value extractValue(const Location &L,
@@ -130,6 +133,8 @@ Value extractValue(const Location &L,
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
Obj.insert({"FileURL", FileURL});
+
+ return Obj;
}
Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -280,29 +285,39 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
Value ArrNamespace = Array();
for (const Reference& Child : I.Children.Namespaces)
ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
- NamespaceValue.insert({"Namespace", ArrNamespace});
+
+ if (!ArrNamespace.getAsArray()->empty())
+ NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
Value ArrRecord = Array();
for (const Reference& Child : I.Children.Records)
ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
- NamespaceValue.insert({"Record", ArrRecord});
+
+ if (!ArrRecord.getAsArray()->empty())
+ NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
Value ArrFunction = Array();
for (const FunctionInfo& Child : I.Children.Functions)
ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
CDCtx));
- NamespaceValue.insert({"Function", ArrRecord});
+ if (!ArrFunction.getAsArray()->empty())
+ NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
Value ArrEnum = Array();
for (const EnumInfo& Child : I.Children.Enums)
ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
- NamespaceValue.insert({"Enums", ArrEnum });
+
+ if (!ArrEnum.getAsArray()->empty())
+ NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
Value ArrTypedefs = Array();
for (const TypedefInfo& Child : I.Children.Typedefs)
ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
- NamespaceValue.insert({"Typedefs", ArrTypedefs });
+ if (!ArrTypedefs.getAsArray()->empty())
+ NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+
+ return NamespaceValue;
}
@@ -313,6 +328,7 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+ llvm::outs() << V << "\n";
OS << NamespaceTemplate->render(V);
break;
}
@@ -332,10 +348,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-
+ return llvm::Error::success();
}
-const char *MustacheHTMLGenerator::Format = "mustache";
+const char *MustacheHTMLGenerator::Format = "mhtml";
static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -343,7 +359,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
// This anchor is used to force the linker to link in the generated object
// file and thus register the generator.
-volatile int HTMLGeneratorAnchorSource = 0;
+volatile int MHTMLGeneratorAnchorSource = 0;
} // namespace doc
} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index af4c60182ae52c..1d3407f8b52920 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
See https://llvm.org/LICENSE.txt for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- This file defines the template
+ This file defines the template for generating Namespaces
}}
<!DOCTYPE html>
<html lang="en-US">
@@ -11,13 +11,42 @@
<meta charset="utf-8"/>
<title>{{NamespaceTitle}}</title>
</head>
+ <h1>{{NamespaceTitle}}</h1>
{{#NamespaceComments}}
- <p>Namespace Comment Present!</p>
+ <p>Namespace Comment</p>
{{/NamespaceComments}}
{{#Namespace}}
- <p>Namespace Present!</p>
+ <h2 id="Namespace">Namespace</h2>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
{{/Namespace}}
{{#Record}}
- <p>Record Present!</p>
+ <h2 id="Class">Class</h2>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
{{/Record}}
+ {{#Function}}
+ <h2 id="Function">Function</h2>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Function}}
+ {{#Enums}}
+ <h2 id="Enums">Enums</h2>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Enums}}
</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index fc5a9529111b16..eee8dbf093cc53 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,6 +103,7 @@ enum OutputFormatTy {
md,
yaml,
html,
+ mhtml
};
static llvm::cl::opt<OutputFormatTy>
@@ -112,7 +113,9 @@ static llvm::cl::opt<OutputFormatTy>
clEnumValN(OutputFormatTy::md, "md",
"Documentation in MD format."),
clEnumValN(OutputFormatTy::html, "html",
- "Documentation in HTML format.")),
+ "Documentation in HTML format."),
+ clEnumValN(OutputFormatTy::mhtml, "mhtml",
+ "Documentation in mHTML format")),
llvm::cl::init(OutputFormatTy::yaml),
llvm::cl::cat(ClangDocCategory));
@@ -124,6 +127,8 @@ std::string getFormatString() {
return "md";
case OutputFormatTy::html:
return "html";
+ case OutputFormatTy::mhtml:
+ return "mhtml";
}
llvm_unreachable("Unknown OutputFormatTy");
}
@@ -227,6 +232,8 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
llvm::sys::path::native(AssetsPath, MustacheTemplate);
llvm::sys::path::append(MustacheTemplate, "template.mustache");
CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+
+ return llvm::Error::success();
}
/// Make the output of clang-doc deterministic by sorting the children of
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index e5c5d4c9e4412b..5354032f4d8323 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,5 +26,3 @@ class Shape {
*/
virtual double perimeter() const = 0;
};
-
-
>From 587bb02d1e1c675c2970e91249d579e623e951ea Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 24 Sep 2024 16:48:29 -0400
Subject: [PATCH 51/62] [clang-doc] update
---
.../clang-doc/HTMLMustacheGenerator.cpp | 16 ++++++++---
.../clang-doc/assets/comments.mustache | 27 +++++++++++++++++++
.../clang-doc/assets/template.mustache | 7 +++--
.../Inputs/basic-project/include/Shape.h | 2 ++
4 files changed, 46 insertions(+), 6 deletions(-)
create mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index bfa563ebdc7b37..96a685dbe2ae87 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -194,8 +194,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
Value ParamArr = Array();
- for (const auto &P : I.Params) {
- ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+ for (const auto Val : llvm::enumerate(I.Params)) {
+ Value V = Object();
+ V.getAsObject()->insert({"Name", Val.value().Name});
+ V.getAsObject()->insert({"Type", Val.value().Type.Name});
+ V.getAsObject()->insert({"End", Val.index() + 1 == I.Params.size()});
+ ParamArr.getAsArray()->emplace_back(V);
}
Obj.insert({"Params", ParamArr});
@@ -328,17 +332,21 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- llvm::outs() << V << "\n";
- OS << NamespaceTemplate->render(V);
+ llvm::raw_ostream &OS = llvm::outs();
+ llvm::json::OStream J(OS, /*IndentSize=*/2);
+ J.value(V);
break;
}
case InfoType::IT_record:
break;
case InfoType::IT_enum:
+ llvm::outs() << "IT_enum\n";
break;
case InfoType::IT_function:
+ llvm::outs() << "IT_Function\n";
break;
case InfoType::IT_typedef:
+ llvm::outs() << "IT_typedef\n";
break;
case InfoType::IT_default:
return createStringError(llvm::inconvertibleErrorCode(),
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
new file mode 100644
index 00000000000000..1eac4de91836ab
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/comments.mustache
@@ -0,0 +1,27 @@
+{{!
+ 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
+
+ This file defines templates for generating
+}}
+
+{{#FullComments}}
+ {{#Children}}
+ {{>Comment}}
+ {{/Children}}
+{{/FullComments}}
+{{#ParagraphComment}}
+ {{#Children}}
+ {{>Comment}}
+ {{/Children}}
+{{/ParagraphComment}}
+{{#BlockCommandComment}}
+ <div>{{Command}}</div>
+ {{#Children}}
+ {{>Comment}}
+ {{/Children}}
+{{/BlockCommandComment}}
+{{#TextComment}}
+ <p>{{TextComment}}</p>
+{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index 1d3407f8b52920..3b77e0189f4e22 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
See https://llvm.org/LICENSE.txt for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- This file defines the template for generating Namespaces
+ This file defines the template for generating namespaces
}}
<!DOCTYPE html>
<html lang="en-US">
@@ -13,7 +13,9 @@
</head>
<h1>{{NamespaceTitle}}</h1>
{{#NamespaceComments}}
- <p>Namespace Comment</p>
+ <div>
+ {{>Comments}}
+ </div>
{{/NamespaceComments}}
{{#Namespace}}
<h2 id="Namespace">Namespace</h2>
@@ -39,6 +41,7 @@
<h2 id="Function">Function</h2>
<div>
{{#Obj}}
+
{{/Obj}}
</div>
{{/Function}}
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index 5354032f4d8323..e5c5d4c9e4412b 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,3 +26,5 @@ class Shape {
*/
virtual double perimeter() const = 0;
};
+
+
>From ab1c3a6ff20c645d2cf938d6d5572f7fd3c948e0 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 7 Oct 2024 15:07:41 -0400
Subject: [PATCH 52/62] [clang-doc] add more templates
---
clang-tools-extra/clang-doc/CMakeLists.txt | 5 +-
.../clang-doc/FileHelpersClangDoc.cpp | 75 +++
.../clang-doc/FileHelpersClangDoc.h | 26 +
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 59 +--
.../clang-doc/HTMLMustacheGenerator.cpp | 281 ++++++++---
.../clang-doc/assets/clang-doc-mustache.css | 476 ++++++++++++++++++
.../clang-doc/assets/class-template.mustache | 227 +++++++++
.../assets/comments-template.mustache | 34 ++
.../clang-doc/assets/comments.mustache | 27 -
.../clang-doc/assets/enum-template.mustache | 47 ++
.../assets/function-template.mustache | 23 +
.../clang-doc/assets/mustache-index.js | 33 ++
.../assets/namespace-template.mustache | 87 ++++
.../clang-doc/assets/template.mustache | 55 --
.../clang-doc/tool/CMakeLists.txt | 8 +-
.../clang-doc/tool/ClangDocMain.cpp | 53 +-
16 files changed, 1296 insertions(+), 220 deletions(-)
create mode 100644 clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp
create mode 100644 clang-tools-extra/clang-doc/FileHelpersClangDoc.h
create mode 100644 clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
create mode 100644 clang-tools-extra/clang-doc/assets/class-template.mustache
create mode 100644 clang-tools-extra/clang-doc/assets/comments-template.mustache
delete mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache
create mode 100644 clang-tools-extra/clang-doc/assets/enum-template.mustache
create mode 100644 clang-tools-extra/clang-doc/assets/function-template.mustache
create mode 100644 clang-tools-extra/clang-doc/assets/mustache-index.js
create mode 100644 clang-tools-extra/clang-doc/assets/namespace-template.mustache
delete mode 100644 clang-tools-extra/clang-doc/assets/template.mustache
diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 82aded940222db..0793c343305184 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,15 +8,16 @@ add_clang_library(clangDoc
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
+ FileHelpersClangDoc.cpp
Generators.cpp
HTMLGenerator.cpp
+ HTMLMustacheGenerator.cpp
Mapper.cpp
MDGenerator.cpp
Representation.cpp
Serialize.cpp
YAMLGenerator.cpp
- HTMLMustacheGenerator.cpp
-
+
DEPENDS
omp_gen
ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp b/clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp
new file mode 100644
index 00000000000000..50209cfac1ca3d
--- /dev/null
+++ b/clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp
@@ -0,0 +1,75 @@
+//===-- FileHelpersClangDoc.cpp - File Helpers -------------------*- 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 "FileHelpersClangDoc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace doc {
+
+llvm::Error
+copyFile(llvm::StringRef FilePath, llvm::StringRef OutDirectory) {
+ llvm::SmallString<128> PathWrite;
+ llvm::sys::path::native(OutDirectory, PathWrite);
+ llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
+ llvm::SmallString<128> PathRead;
+ llvm::sys::path::native(FilePath, PathRead);
+ std::error_code OK;
+ std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
+ if (FileErr != OK) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "error creating file " +
+ llvm::sys::path::filename(FilePath) +
+ ": " + FileErr.message() + "\n");
+ }
+ return llvm::Error::success();
+}
+
+
+llvm::SmallString<128> computeRelativePath(llvm::StringRef Destination,
+ llvm::StringRef Origin) {
+ // If Origin is empty, the relative path to the Destination is its complete
+ // path.
+ if (Origin.empty())
+ return Destination;
+
+ // The relative path is an empty path if both directories are the same.
+ if (Destination == Origin)
+ return {};
+
+ // These iterators iterate through each of their parent directories
+ llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
+ llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
+ llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
+ llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
+ // Advance both iterators until the paths differ. Example:
+ // Destination = A/B/C/D
+ // Origin = A/B/E/F
+ // FileI will point to C and DirI to E. The directories behind them is the
+ // directory they share (A/B).
+ while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
+ ++FileI;
+ ++DirI;
+ }
+ llvm::SmallString<128> Result; // This will hold the resulting path.
+ // Result has to go up one directory for each of the remaining directories in
+ // Origin
+ while (DirI != DirE) {
+ llvm::sys::path::append(Result, "..");
+ ++DirI;
+ }
+ // Result has to append each of the remaining directories in Destination
+ while (FileI != FileE) {
+ llvm::sys::path::append(Result, *FileI);
+ ++FileI;
+ }
+ return Result;
+}
+
+} // namespace doc
+} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/FileHelpersClangDoc.h b/clang-tools-extra/clang-doc/FileHelpersClangDoc.h
new file mode 100644
index 00000000000000..9072a7bd08a4f8
--- /dev/null
+++ b/clang-tools-extra/clang-doc/FileHelpersClangDoc.h
@@ -0,0 +1,26 @@
+//===-- FileHelpersClangDoc.h --- File Helpers -------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_FILEHELPER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_FILEHELPER_H
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace doc {
+
+llvm::Error
+copyFile(llvm::StringRef FilePath, llvm::StringRef OutDirectory);
+
+llvm::SmallString<128>
+computeRelativePath(llvm::StringRef Destination,llvm::StringRef Origin);
+
+} // namespace doc
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_FILEHELPER_H
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ed65ac965114b5..aa76d17d9411d6 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,6 +8,7 @@
#include "Generators.h"
#include "Representation.h"
+#include "FileHelpersClangDoc.h"
#include "clang/Basic/Version.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -251,46 +252,6 @@ static void appendVector(std::vector<Derived> &&New,
std::move(New.begin(), New.end(), std::back_inserter(Original));
}
-// Compute the relative path from an Origin directory to a Destination directory
-static SmallString<128> computeRelativePath(StringRef Destination,
- StringRef Origin) {
- // If Origin is empty, the relative path to the Destination is its complete
- // path.
- if (Origin.empty())
- return Destination;
-
- // The relative path is an empty path if both directories are the same.
- if (Destination == Origin)
- return {};
-
- // These iterators iterate through each of their parent directories
- llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
- llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
- llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
- llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
- // Advance both iterators until the paths differ. Example:
- // Destination = A/B/C/D
- // Origin = A/B/E/F
- // FileI will point to C and DirI to E. The directories behind them is the
- // directory they share (A/B).
- while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
- ++FileI;
- ++DirI;
- }
- SmallString<128> Result; // This will hold the resulting path.
- // Result has to go up one directory for each of the remaining directories in
- // Origin
- while (DirI != DirE) {
- llvm::sys::path::append(Result, "..");
- ++DirI;
- }
- // Result has to append each of the remaining directories in Destination
- while (FileI != FileE) {
- llvm::sys::path::append(Result, *FileI);
- ++FileI;
- }
- return Result;
-}
// HTML generation
@@ -1133,24 +1094,6 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
return llvm::Error::success();
}
-static llvm::Error
-copyFile(StringRef FilePath, StringRef OutDirectory) {
- llvm::SmallString<128> PathWrite;
- llvm::sys::path::native(OutDirectory, PathWrite);
- llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
- llvm::SmallString<128> PathRead;
- llvm::sys::path::native(FilePath, PathRead);
- std::error_code OK;
- std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
- if (FileErr != OK) {
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "error creating file " +
- llvm::sys::path::filename(FilePath) +
- ": " + FileErr.message() + "\n");
- }
- return llvm::Error::success();
-}
-
llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
auto Err = serializeIndex(CDCtx);
if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 96a685dbe2ae87..6be4c795a68654 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Generators.h"
#include "Representation.h"
+#include "FileHelpersClangDoc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mustache.h"
@@ -48,9 +49,8 @@ class MustacheTemplateFile : public Template {
Error registerPartialFile(StringRef Name, StringRef FileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
MemoryBuffer::getFile(FileName);
- if (auto EC = BufferOrError.getError()) {
+ if (auto EC = BufferOrError.getError())
return llvm::createFileError("cannot open file", EC);
- }
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
@@ -65,24 +65,54 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
+llvm::Error setupTemplate(
+ std::unique_ptr<MustacheTemplateFile> &Template,
+ StringRef TemplatePath,
+ std::vector<std::pair<StringRef, StringRef>> Partials) {
+ auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
+ if (auto EC = T.getError())
+ return llvm::createFileError("cannot open file", EC);
+ Template = std::move(T.get());
+ for (const auto &P : Partials) {
+ auto Err = Template->registerPartialFile(P.first, P.second);
+ if (Err)
+ return Err;
+ }
+ return llvm::Error::success();
+}
+
llvm::Error
setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
- auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
- auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
- if (auto EC = Template.getError()) {
- return llvm::createFileError("cannot open file", EC);
- }
- NamespaceTemplate = std::move(Template.get());
+ auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
+ auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
+ auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
+ auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
+ auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+ std::vector<std::pair<StringRef, StringRef>> Partials = {
+ {"Comments", CommentFilePath},
+ {"FunctionPartial", FunctionFilePath},
+ {"EnumPartial", EnumFilePath}
+ };
+
+ auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
+ if (Err)
+ return Err;
+
+ Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
+
+ if (Err)
+ return Err;
+
return llvm::Error::success();
}
+
llvm::Error
MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
const clang::doc::ClangDocContext &CDCtx) {
- if (auto Err = setupTemplateFiles(CDCtx)) {
+ if (auto Err = setupTemplateFiles(CDCtx))
return Err;
- }
// Track which directories we already tried to create.
llvm::StringSet<> CreatedDirs;
// Collect all output by file name and create the necessary directories.
@@ -95,10 +125,9 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
if (!CreatedDirs.contains(Path)) {
if (std::error_code Err = llvm::sys::fs::create_directories(Path);
- Err != std::error_code()) {
+ Err != std::error_code())
return llvm::createStringError(Err, "Failed to create directory '%s'.",
Path.c_str());
- }
CreatedDirs.insert(Path);
}
@@ -110,14 +139,13 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
std::error_code FileErr;
llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
llvm::sys::fs::OF_None);
- if (FileErr) {
+ if (FileErr)
return llvm::createStringError(FileErr, "Error opening file '%s'",
Group.getKey().str().c_str());
- }
+
for (const auto &Info : Group.getValue()) {
- if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+ if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
return Err;
- }
}
}
return llvm::Error::success();
@@ -126,12 +154,15 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
Value extractValue(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
Object Obj = Object();
+ Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"Filename", L.Filename});
+
if (!L.IsFileInRootDir || !RepositoryUrl) {
- Obj.insert({"LineNumber", L.LineNumber});
- Obj.insert({"Filename", L.Filename});
+ return Obj;
}
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+ FileURL += "#" + std::to_string(L.LineNumber);
Obj.insert({"FileURL", FileURL});
return Obj;
@@ -144,6 +175,8 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
Object Obj = Object();
Obj.insert({"Link", Path});
Obj.insert({"Name", I.Name});
+ Obj.insert({"QualName", I.QualName});
+ Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
return Obj;
}
@@ -209,12 +242,13 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
Obj.insert({"FunctionComments", ArrDesc});
}
- if (I.DefLoc) {
- if (!CDCtx.RepositoryUrl)
- Obj.insert({"Location", extractValue(*I.DefLoc)});
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ Obj.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
else
- Obj.insert({"Location", extractValue(*I.DefLoc,
- StringRef{*CDCtx.RepositoryUrl})});
+ Obj.insert({"Location", extractValue(L)});
}
return Obj;
}
@@ -229,7 +263,6 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumName", EnumType});
Obj.insert({"HasComment", HasComment});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
-
Value Arr = Array();
for (const EnumValueInfo& M: I.Members) {
Value EnumValue = Object();
@@ -256,75 +289,175 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumComments", ArrDesc});
}
- if (I.DefLoc) {
- if (!CDCtx.RepositoryUrl)
- Obj.insert({"Location", extractValue(*I.DefLoc)});
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ Obj.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
else
- Obj.insert({"Location", extractValue(*I.DefLoc,
- StringRef{*CDCtx.RepositoryUrl})});
+ Obj.insert({"Location", extractValue(L)});
}
return Obj;
}
-Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
- Object NamespaceValue = Object();
- std::string InfoTitle;
- if (I.Name.str() == "")
- InfoTitle = "Global Namespace";
- else
- InfoTitle = ("namespace " + I.Name).str();
-
- StringRef BasePath = I.getRelativeFilePath("");
- NamespaceValue.insert({"NamespaceTitle", InfoTitle});
- NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
-
- if (!I.Description.empty()) {
- Value ArrDesc = Array();
- for (const CommentInfo& Child : I.Description)
- ArrDesc.getAsArray()->emplace_back(extractValue(Child));
- NamespaceValue.insert({"NamespaceComments", ArrDesc });
- }
-
+void extractScopeChildren(const ScopeChildren &S, Object &Obj,
+ StringRef ParentInfoDir,
+ const ClangDocContext &CDCtx) {
Value ArrNamespace = Array();
- for (const Reference& Child : I.Children.Namespaces)
- ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ for (const Reference& Child : S.Namespaces)
+ ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
if (!ArrNamespace.getAsArray()->empty())
- NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+ Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
Value ArrRecord = Array();
- for (const Reference& Child : I.Children.Records)
- ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ for (const Reference& Child : S.Records)
+ ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
if (!ArrRecord.getAsArray()->empty())
- NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
+ Obj.insert({"Record", Object{{"Links", ArrRecord}}});
Value ArrFunction = Array();
- for (const FunctionInfo& Child : I.Children.Functions)
- ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
- CDCtx));
+ Value PublicFunction = Array();
+ Value ProtectedFunction = Array();
+ Value PrivateFunction = Array();
+
+ for (const FunctionInfo& Child : S.Functions) {
+ Value F = extractValue(Child, ParentInfoDir, CDCtx);
+ AccessSpecifier Access = Child.Access;
+ if (Access == AccessSpecifier::AS_public)
+ PublicFunction.getAsArray()->emplace_back(F);
+ else if (Access == AccessSpecifier::AS_protected)
+ ProtectedFunction.getAsArray()->emplace_back(F);
+ else
+ ArrFunction.getAsArray()->emplace_back(F);
+ }
if (!ArrFunction.getAsArray()->empty())
- NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
+ Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+
+ if (!PublicFunction.getAsArray()->empty())
+ Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+
+ if (!ProtectedFunction.getAsArray()->empty())
+ Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+
Value ArrEnum = Array();
- for (const EnumInfo& Child : I.Children.Enums)
+ for (const EnumInfo& Child : S.Enums)
ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
if (!ArrEnum.getAsArray()->empty())
- NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
+ Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
Value ArrTypedefs = Array();
- for (const TypedefInfo& Child : I.Children.Typedefs)
+ for (const TypedefInfo& Child : S.Typedefs)
ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
if (!ArrTypedefs.getAsArray()->empty())
- NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+ Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+ Object NamespaceValue = Object();
+ std::string InfoTitle;
+ if (I.Name.str() == "")
+ InfoTitle = "Global Namespace";
+ else
+ InfoTitle = ("namespace " + I.Name).str();
+
+ StringRef BasePath = I.getRelativeFilePath("");
+ NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+ NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ NamespaceValue.insert({"NamespaceComments", ArrDesc });
+ }
+ extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
return NamespaceValue;
}
+Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
+ Object RecordValue = Object();
+
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ RecordValue.insert({"RecordComments", ArrDesc });
+ }
+ RecordValue.insert({"Name", I.Name});
+ RecordValue.insert({"FullName", I.FullName});
+ RecordValue.insert({"RecordType", getTagType(I.TagType)});
+
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ RecordValue.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
+ else
+ RecordValue.insert({"Location", extractValue(L)});
+ }
+
+ StringRef BasePath = I.getRelativeFilePath("");
+ extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
+ Value PublicMembers = Array();
+ Value ProtectedMembers = Array();
+ Value PrivateMembers = Array();
+ for (const MemberTypeInfo &Member : I.Members ) {
+ Value MemberValue = Object();
+ MemberValue.getAsObject()->insert({"Name", Member.Name});
+ MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
+ if (!Member.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : Member.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
+ }
+
+ if (Member.Access == AccessSpecifier::AS_public)
+ PublicMembers.getAsArray()->emplace_back(MemberValue);
+ else if (Member.Access == AccessSpecifier::AS_protected)
+ ProtectedMembers.getAsArray()->emplace_back(MemberValue);
+ else if (Member.Access == AccessSpecifier::AS_private)
+ PrivateMembers.getAsArray()->emplace_back(MemberValue);
+ }
+ if (!PublicMembers.getAsArray()->empty())
+ RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
+ if (!ProtectedMembers.getAsArray()->empty())
+ RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
+ if (!PrivateMembers.getAsArray()->empty())
+ RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+
+ return RecordValue;
+}
+void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
+ V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+ Value StylesheetArr = Array();
+ auto InfoPath = I->getRelativeFilePath("");
+ SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+ for (const auto &FilePath : CDCtx.UserStylesheets) {
+ SmallString<128> StylesheetPath = RelativePath;
+ llvm::sys::path::append(StylesheetPath,
+ llvm::sys::path::filename(FilePath));
+ llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
+ StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+ }
+ V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+
+ Value ScriptArr = Array();
+ for (auto Script : CDCtx.JsScripts) {
+ SmallString<128> JsPath = RelativePath;
+ llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
+ ScriptArr.getAsArray()->emplace_back(JsPath);
+ }
+ V.getAsObject()->insert({"Scripts", ScriptArr});
+}
+
llvm::Error
MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -332,13 +465,18 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- llvm::raw_ostream &OS = llvm::outs();
- llvm::json::OStream J(OS, /*IndentSize=*/2);
- J.value(V);
+ setupTemplateValue(CDCtx, V, I);
+ OS << NamespaceTemplate->render(V);
break;
}
- case InfoType::IT_record:
+ case InfoType::IT_record: {
+ Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
+ setupTemplateValue(CDCtx, V, I);
+ // Serialize the JSON value to the output stream in a readable format.
+ llvm::outs() << llvm::formatv("{0:2}", V);
+ OS << RecordTemplate->render(V);
break;
+ }
case InfoType::IT_enum:
llvm::outs() << "IT_enum\n";
break;
@@ -356,6 +494,17 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+ llvm::Error Err = llvm::Error::success();
+ for (const auto &FilePath : CDCtx.UserStylesheets) {
+ Err = copyFile(FilePath, CDCtx.OutDirectory);
+ if (Err)
+ return Err;
+ }
+ for (const auto &FilePath : CDCtx.JsScripts) {
+ Err = copyFile(FilePath, CDCtx.OutDirectory);
+ if (Err)
+ return Err;
+ }
return llvm::Error::success();
}
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
new file mode 100644
index 00000000000000..2f07baa7ca0a1a
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -0,0 +1,476 @@
+ at import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap";
+
+*,*::before *::after {
+ box-sizing:border-box
+}
+* {
+ margin:0;
+ padding:0
+}
+ol,
+ul {
+ list-style:none
+}
+img,
+picture,
+svg,
+video {
+ display:block;
+ max-width:100%
+}
+* {
+ --brand-light:#ce6300;
+ --text1-light:#000000;
+ --text2-light:#333333;
+ --surface1-light:#ffffff;
+ --surface2-light:#f5f5f5;
+ --brand-dark:#de9853;
+ --text1-dark:#ffffff;
+ --text2-dark:#cccccc;
+ --surface1-dark:#161212;
+ --surface2-dark:#272424
+}
+:root {
+ color-scheme:light;
+ --brand:var(--brand-light);
+ --text1:var(--text1-light);
+ --text2:var(--text2-light);
+ --text1-inverse:var(--text1-dark);
+ --text2-inverse:var(--text2-dark);
+ --surface1:var(--surface1-light);
+ --surface2:var(--surface2-light)
+}
+ at media(prefers-color-scheme:dark) {
+ :root {
+ color-scheme:dark;
+ --brand:var(--brand-dark);
+ --text1:var(--text1-dark);
+ --text2:var(--text2-dark);
+ --text1-inverse:var(--text1-light);
+ --text2-inverse:var(--text2-light);
+ --surface1:var(--surface1-dark);
+ --surface2:var(--surface2-dark)
+ }
+}
+[color-scheme=light] {
+ color-scheme:light;
+ --brand:var(--brand-light);
+ --text1:var(--text1-light);
+ --text2:var(--text2-light);
+ --text1-inverse:var(--text1-dark);
+ --text2-inverse:var(--text2-dark);
+ --surface1:var(--surface1-light);
+ --surface2:var(--surface2-light)
+}
+[color-scheme=dark] {
+ color-scheme:dark;
+ --brand:var(--brand-dark);
+ --text1:var(--text1-dark);
+ --text2:var(--text2-dark);
+ --text1-inverse:var(--text1-light);
+ --text2-inverse:var(--text2-light);
+ --surface1:var(--surface1-dark);
+ --surface2:var(--surface2-dark)
+}
+html {
+ background-color:var(--surface1)
+}
+
+html, body {
+ min-height: 100vh;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+}
+
+.container {
+ display: flex;
+ margin-top: 60px;
+ height: calc(100% - 60px);
+ box-sizing: border-box;
+}
+
+body, html {
+ font-family:Inter,sans-serif;
+ margin: 0;
+ padding: 0;
+ height: 100%;
+}
+
+/* Navbar Styles */
+.navbar {
+ background-color: var(--surface2);
+ border-bottom: 1px solid var(--text2);
+ position: fixed;
+ width: 100%;
+ top: 0;
+ left: 0;
+ height: 60px; /* Adjust as needed */
+ color: white;
+ display: flex;
+ align-items: center;
+ padding: 0 20px;
+ box-sizing: border-box;
+ z-index: 1000;
+}
+
+
+.navbar__container {
+ display:flex;
+ justify-content:space-between;
+ align-items:center;
+ padding:1rem;
+ color:var(--text1);
+ max-width:2048px;
+ margin:auto
+}
+.navbar__logo {
+ display:flex;
+ align-items:center;
+ height:40px
+}
+.navbar__logo a {
+ display:flex;
+ align-items:center;
+ text-decoration:none;
+ height:100%
+}
+.navbar__logo img {
+ height:100%;
+ width:auto
+}
+.navbar__toggle {
+ background:0 0;
+ color:var(--text2);
+ border:none;
+ cursor:pointer;
+ font-size:1.5rem;
+ width:2.5rem;
+ height:2.5rem;
+ margin-left:auto
+}
+.navbar__toggle:hover {
+ color:var(--text1)
+}
+ at media(min-width:769px) {
+ .navbar__toggle {
+ display:none
+ }
+}
+.navbar__menu {
+ display:flex;
+ justify-content:space-between;
+ align-items:center;
+ list-style:none;
+ margin:0;
+ padding:0;
+ gap:.25rem;
+ margin-left:auto
+}
+
+ at media(max-width:768px) {
+ .navbar__menu {
+ flex-direction:column;
+ justify-content:flex-start;
+ width:100%;
+ background-color:var(--surface2);
+ position:fixed;
+ top:0;
+ left:0;
+ right:0;
+ bottom:0;
+ padding:1.5rem;
+ transform:translateX(100%);
+ transition:transform .5s ease-in-out
+ }
+}
+ at media(max-width:768px) {
+ .navbar__menu.active {
+ transform:translateX(0)
+ }
+}
+.navbar__close {
+ background:0 0;
+ border:none;
+ cursor:pointer;
+ font-size:1.5rem;
+ color:var(--text2);
+ margin-left:auto
+}
+.navbar__close:hover {
+ color:var(--text1)
+}
+ at media(min-width:769px) {
+ .navbar__close {
+ display:none
+ }
+}
+.navbar__links {
+ display:flex;
+ gap:1rem;
+ align-items:center;
+ margin:0;
+ padding:0
+}
+ at media(max-width:768px) {
+ .navbar__links {
+ flex-direction:column
+ }
+}
+.navbar__item {
+ list-style-type:none
+}
+.navbar__link {
+ color:var(--text2);
+ text-decoration:none;
+ padding:.5rem
+}
+.navbar__link:hover {
+ color:var(--text1)
+}
+.navbar__theme-toggle-button {
+ background:0 0;
+ color:var(--text2);
+ border:none;
+ cursor:pointer;
+ font-size:1.5rem;
+ width:2.5rem;
+ height:2.5rem
+}
+.navbar__theme-toggle-button:hover {
+ color:var(--text1)
+}
+
+.hero__container {
+ margin-top:1rem;
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ gap:2rem
+}
+.hero__title {
+ font-size:2.5rem;
+ margin-bottom:.5rem
+}
+.hero__title-large {
+ font-size:3rem
+}
+ at media(max-width:768px) {
+ .hero__title-large {
+ font-size:2.5rem
+ }
+}
+ at media(max-width:480px) {
+ .hero__title-large {
+ font-size:2rem
+ }
+}
+ at media(max-width:768px) {
+ .hero__title {
+ font-size:2rem
+ }
+}
+ at media(max-width:480px) {
+ .hero__title {
+ font-size:1.75rem
+ }
+}
+.hero__subtitle {
+ font-size:1.25rem;
+ font-weight:500
+}
+ at media(max-width:768px) {
+ .hero__subtitle {
+ font-size:1rem
+ }
+}
+ at media(max-width:480px) {
+ .hero__subtitle {
+ font-size:.875rem
+ }
+}
+
+.section-container {
+ max-width: 2048px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-top:0;
+ margin-bottom: 1rem;
+ padding:1rem 2rem
+}
+
+
+ at media(max-width:768px) {
+ .section-container {
+ padding:1rem
+ }
+}
+.section-container h2 {
+ font-size:1.5rem;
+ margin-bottom:1rem;
+ color:var(--brand);
+ border-bottom: 1px solid var(--text2);
+}
+ at media(max-width:768px) {
+ .section-container h2 {
+ font-size:1.25rem
+ }
+}
+.section-container p {
+ font-size:1rem;
+ line-height:1.5
+}
+ at media(max-width:768px) {
+ .section-container p {
+ font-size:.875rem
+ }
+}
+.home__row {
+ display:grid;
+ grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
+ gap:2rem
+}
+
+
+.links-wrapper {
+ display:grid;
+ gap:1rem;
+ grid-template-columns:1fr 1fr 1fr 1fr
+}
+ at media(max-width:768px) {
+ .links-wrapper {
+ grid-template-columns:1fr 1fr
+ }
+}
+ at media(max-width:480px) {
+ .links-wrapper {
+ grid-template-columns:1fr
+ }
+}
+.links-wrapper .link {
+ display:flex;
+ flex-direction:column;
+ align-items:center;
+ padding:1rem;
+ border:1px solid var(--text1);
+ border-radius:.25rem
+}
+.links-wrapper .link__icon {
+ font-size:2rem
+}
+.links-wrapper .link__title {
+ font-size:1rem
+}
+
+.table-wrapper {
+ display:flex;
+ flex-direction:column;
+ padding:1rem;
+ border-collapse: collapse; /* Ensures there are no gaps between cells */
+}
+
+.table-wrapper th, .table-wrapper td {
+ padding: 0.5rem 1rem; /* Adds padding inside the cells */
+ border:1px solid var(--text1);
+ text-align: left;
+}
+
+
+.block-command-command {
+ font-weight: bold;
+}
+
+.code-clang-doc {
+ font-size: 1.1rem;
+}
+
+.delimiter-container {
+ padding: 0.5rem 1rem;
+ margin-bottom:1rem;
+}
+
+.resizer {
+ width: 5px;
+ cursor: col-resize;
+ background-color: var(--text2);
+}
+
+.resizer:hover {
+ background-color: var(--text2-inverse);
+}
+
+.sidebar {
+ width: 250px;
+ top: 0;
+ left: 0;
+ height: 100%;
+ position: fixed;
+ background-color: var(--surface1);
+ display: flex;
+ border-left: 1px solid var(--text2);
+ flex-direction: column;
+ overflow-y: auto;
+ scrollbar-width: thin;
+}
+
+.sidebar h2 {
+ margin-top: 0;
+ margin-bottom: 20px;
+ padding: 10px;
+}
+
+.sidebar ul {
+ width: 100%;
+ padding: 0;
+ list-style-type: none;
+}
+
+.sidebar ul li {
+ padding-right: 1rem;
+ padding-left: 2rem;
+ padding-top: 0.25rem;
+ padding-bottom: 0.25rem;
+}
+
+.sidebar-section {
+ font-size:1.5rem;
+ font-weight: bold;
+ margin-bottom: 1rem;
+ padding: 3rem;
+}
+.sidebar-section a {
+ color: var(--brand)
+}
+
+
+/* Content */
+.content {
+ background-color: var(--text1-inverse);
+ padding: 20px;
+ left: 250px;
+ position: relative;
+ width: calc(100% - 250px);
+ height: 100vh;
+}
+.sidebar-item {
+ color: var(--text1);
+}
+
+.sidebar-item-container:hover {
+ width: 100%;
+ background-color: grey;
+}
+
+.sidebar-item-container:hover a {
+ width: 100%;
+ color: var(--text1-inverse);
+}
+
+.class-container {
+ padding: 0.5rem 1rem;
+}
+
+a, a:visited, a:hover, a:active {
+ text-decoration: none;
+ color: inherit;
+}
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
new file mode 100644
index 00000000000000..7ce51c6e162110
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -0,0 +1,227 @@
+{{!
+ 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
+
+ This file defines the template for classes/struct
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <meta charset="utf-8"/>
+ <title>{{Name}}</title>
+ {{#Stylesheets}}
+ <link rel="stylesheet" type="text/css" href="{{.}}"/>
+ {{/Stylesheets}}
+ {{#Scripts}}
+ <script src="{{.}}"></script>
+ {{/Scripts}}
+ {{! Highlight.js dependency for syntax highlighting }}
+ <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">
+ {{#ProjectName}}
+ <div class="navbar__logo">
+ {{ProjectName}}
+ </div>
+ {{/ProjectName}}
+ <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>{{RecordType}} {{Name}}</h2>
+ <ul>
+ {{#PublicMembers}}
+ <li class="sidebar-section">
+ <a class="sidebar-item" href="#PublicMethods">Public Members</a>
+ </li>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ {{/PublicMembers}}
+ {{#ProtectedMembers}}
+ <li class="sidebar-section">
+ <a class="sidebar-item" href="#PublicMethods">Protected Members</a>
+ </li>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ {{/ProtectedMembers}}
+ {{#PublicFunction}}
+ <li class="sidebar-section">
+ <a class="sidebar-item" href="#PublicMethods">Public Method</a>
+ </li>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ {{/PublicFunction}}
+ {{#ProtectedFunction}}
+ <li class="sidebar-section">
+ <a class="sidebar-item" href="#ProtectedFunction">Protected Method</a>
+ </li>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ {{/ProtectedFunction}}
+ {{#Enums}}
+ <li class="sidebar-section">
+ <a class="sidebar-item" href="#Enums">Enums</a>
+ </li>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ {{/Enums}}
+ {{#Typedef}}
+ <li class="sidebar-section">Typedef</li>
+ {{/Typedef}}
+ {{#Record}}
+ <li class="sidebar-section">
+ <a class="sidebar-item" href="#Classes">Inner Classes</a>
+ </li>
+ <ul>
+ {{#Links}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
+ {{/Record}}
+ </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">{{RecordType}} {{Name}}</h1>
+ {{#RecordComments}}
+ <div class="hero__subtitle">
+ {{>Comments}}
+ </div>
+ {{/RecordComments}}
+ </div>
+ </section>
+ {{#PublicMembers}}
+ <section id="PublicMembers" class="section-container">
+ <h2>Public Members</h2>
+ <div>
+ {{#Obj}}
+ <div id="{{Name}}" class="delimiter-container">
+ <pre>
+<code class="language-cpp code-clang-doc" >{{Type}} {{Name}}</code>
+ </pre>
+ {{#MemberComments}}
+ <div>
+ {{>Comments}}
+ </div>
+ {{/MemberComments}}
+ </div>
+ {{/Obj}}
+ </div>
+ </section>
+ {{/PublicMembers}}
+ {{#ProtectedMembers}}
+ <section id="ProtectedMembers" class="section-container">
+ <h2>Protected Members</h2>
+ <div>
+ {{#Obj}}
+ <div id="{{Name}}" class="delimiter-container">
+ <pre>
+<code class="language-cpp code-clang-doc" >{{Type}} {{Name}}</code>
+ </pre>
+ {{#MemberComments}}
+ <div>
+ {{>Comments}}
+ </div>
+ {{/MemberComments}}
+ </div>
+ {{/Obj}}
+ </div>
+ </section>
+ {{/ProtectedMembers}}
+ {{#PublicFunction}}
+ <section id="PublicMethods" class="section-container">
+ <h2>Public Methods</h2>
+ <div>
+ {{#Obj}}
+{{>FunctionPartial}}
+ {{/Obj}}
+ </div>
+ </section>
+ {{/PublicFunction}}
+ {{#ProtectedFunction}}
+ <section id="ProtectedFunction" class="section-container">
+ <h2>Protected Methods</h2>
+ <div>
+ {{#Obj}}
+{{>FunctionPartial}}
+ {{/Obj}}
+ </div>
+ </section>
+ {{/ProtectedFunction}}
+ {{#Enums}}
+ <section id="Enums" class="section-container">
+ <h2>Enumerations</h2>
+ <div>
+ {{#Obj}}
+{{>EnumPartial}}
+ {{/Obj}}
+ </div>
+ </section>
+ {{/Enums}}
+ {{#Record}}
+ <section id="Classes" class="section-container">
+ <h2>Inner Classes</h2>
+ <ul class="class-container">
+ {{#Links}}
+ <li id="{{ID}}" style="max-height: 40px;">
+<a href="{{Link}}"><pre><code class="language-cpp code-clang-doc" >class {{Name}}</code></pre></a>
+ </li>
+ {{/Links}}
+ </ul>
+ </section>
+ {{/Record}}
+ {{#Typedef}}
+ <section class="section-container">
+ <h2 id="Enums">Enums</h2>
+ </section>
+ {{/Typedef}}
+ </div>
+ </div>
+</main>
+</body>
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/comments-template.mustache b/clang-tools-extra/clang-doc/assets/comments-template.mustache
new file mode 100644
index 00000000000000..f6b62b4407b9c4
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/comments-template.mustache
@@ -0,0 +1,34 @@
+{{!
+ 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
+
+ This file defines templates for generating comments
+}}
+{{#FullComment}}
+ {{#Children}}
+ {{>Comments}}
+ {{/Children}}
+{{/FullComment}}
+{{#ParagraphComment}}
+ {{#Children}}
+ {{>Comments}}
+ {{/Children}}
+{{/ParagraphComment}}
+{{#BlockCommandComment}}
+ <div class="block-command-comment__command">
+ <div class="block-command-command">
+ {{Command}}
+ </div>
+ <div>
+ {{#Children}}
+ {{>Comments}}
+ {{/Children}}
+ </div>
+ </div>
+{{/BlockCommandComment}}
+{{#TextComment}}
+ <div>
+ <p>{{TextComment}}</p>
+ </div>
+{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
deleted file mode 100644
index 1eac4de91836ab..00000000000000
--- a/clang-tools-extra/clang-doc/assets/comments.mustache
+++ /dev/null
@@ -1,27 +0,0 @@
-{{!
- 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
-
- This file defines templates for generating
-}}
-
-{{#FullComments}}
- {{#Children}}
- {{>Comment}}
- {{/Children}}
-{{/FullComments}}
-{{#ParagraphComment}}
- {{#Children}}
- {{>Comment}}
- {{/Children}}
-{{/ParagraphComment}}
-{{#BlockCommandComment}}
- <div>{{Command}}</div>
- {{#Children}}
- {{>Comment}}
- {{/Children}}
-{{/BlockCommandComment}}
-{{#TextComment}}
- <p>{{TextComment}}</p>
-{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache
new file mode 100644
index 00000000000000..d63bf258f8f0fb
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -0,0 +1,47 @@
+{{!
+ 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
+
+ This file defines the template for enums
+}}
+<div id="{{ID}}" class="delimiter-container">
+ <div>
+ <pre>
+ <code class="language-cpp code-clang-doc">
+{{EnumName}}
+ </code>
+ </pre>
+ </div>
+ {{! Enum Values }}
+ <table class="table-wrapper">
+ <tbody>
+ <tr>
+ <th>Name</th>
+ <th>Value</th>
+ {{#HasComment}}
+ <th>Comment</th>
+ {{/HasComment}}
+ </tr>
+ {{#EnumValues}}
+ <tr>
+ <td>{{Name}}</td>
+ <td>{{Value}}</td>
+ {{#EnumValueComments}}
+ <td>{{>Comments}}</td>
+ {{/EnumValueComments}}
+ </tr>
+ {{/EnumValues}}
+ </tbody>
+ </table>
+ {{#EnumComments}}
+ <div>
+ {{>Comments}}
+ </div>
+ {{/EnumComments}}
+ {{#Location}}
+ <div>
+ Defined at line {{LineNumber}} of file {{Filename}}
+ </div>
+ {{/Location}}
+</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache b/clang-tools-extra/clang-doc/assets/function-template.mustache
new file mode 100644
index 00000000000000..0564647467aa60
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/function-template.mustache
@@ -0,0 +1,23 @@
+{{!
+ 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
+
+ This file defines the template for functions/methods
+}}
+<div class="delimiter-container">
+ <div id="{{ID}}">
+ {{! Function Prototype }}
+ <pre>
+ <code class="language-cpp code-clang-doc">
+{{ReturnType.Name}} {{Name}} ({{#Params}}{{^End}}{{Type}} {{Name}}, {{/End}}{{#End}}{{Type}} {{Name}}{{/End}}{{/Params}})
+ </code>
+ </pre>
+ {{! Function Comments }}
+ {{#FunctionComments}}
+ <div>
+ {{>Comments}}
+ </div>
+ {{/FunctionComments}}
+ </div>
+</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/mustache-index.js b/clang-tools-extra/clang-doc/assets/mustache-index.js
new file mode 100644
index 00000000000000..db320cd8ae403c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/mustache-index.js
@@ -0,0 +1,33 @@
+document.addEventListener("DOMContentLoaded", function() {
+ const resizer = document.getElementById('resizer');
+ const sidebar = document.querySelector('.sidebar');
+
+ let isResizing = false;
+ resizer.addEventListener('mousedown', (e) => {
+ isResizing = true;
+ });
+
+ document.addEventListener('mousemove', (e) => {
+ if (!isResizing) return;
+ const newWidth = e.clientX;
+ if (newWidth > 100 && newWidth < window.innerWidth - 100) {
+ sidebar.style.width = `${newWidth}px`;
+ }
+ });
+
+ document.addEventListener('mouseup', () => {
+ isResizing = false;
+ });
+
+ document.querySelectorAll('pre code').forEach((el) => {
+ hljs.highlightElement(el);
+ el.classList.remove("hljs");
+ });
+
+ document.querySelectorAll('.sidebar-item-container').forEach(item => {
+ item.addEventListener('click', function() {
+ const anchor = item.getElementsByTagName("a");
+ window.location.hash = anchor[0].getAttribute('href');
+ });
+ });
+})
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
new file mode 100644
index 00000000000000..4061fd026886e4
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -0,0 +1,87 @@
+{{!
+ 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
+
+ This file defines the template for generating namespaces
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+ <head>
+ <meta charset="utf-8"/>
+ <title>{{NamespaceTitle}}</title>
+ {{#Stylesheets}}
+ <link rel="stylesheet" type="text/css" href="{{.}}"/>
+ {{/Stylesheets}}
+ {{#Scripts}}
+ <script src="{{.}}"></script>
+ {{/Scripts}}
+ {{! Highlight.js dependency for syntax highlighting }}
+ <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.
+ 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.
+ 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.
+ 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.
+ 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.
+ 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>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
deleted file mode 100644
index 3b77e0189f4e22..00000000000000
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ /dev/null
@@ -1,55 +0,0 @@
-{{!
- 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
-
- This file defines the template for generating namespaces
-}}
-<!DOCTYPE html>
-<html lang="en-US">
- <head>
- <meta charset="utf-8"/>
- <title>{{NamespaceTitle}}</title>
- </head>
- <h1>{{NamespaceTitle}}</h1>
- {{#NamespaceComments}}
- <div>
- {{>Comments}}
- </div>
- {{/NamespaceComments}}
- {{#Namespace}}
- <h2 id="Namespace">Namespace</h2>
- <ul>
- {{#Links}}
- <li>
- <a href="{{Link}}">{{Name}}</a>
- </li>
- {{/Links}}
- </ul>
- {{/Namespace}}
- {{#Record}}
- <h2 id="Class">Class</h2>
- <ul>
- {{#Links}}
- <li>
- <a href="{{Link}}">{{Name}}</a>
- </li>
- {{/Links}}
- </ul>
- {{/Record}}
- {{#Function}}
- <h2 id="Function">Function</h2>
- <div>
- {{#Obj}}
-
- {{/Obj}}
- </div>
- {{/Function}}
- {{#Enums}}
- <h2 id="Enums">Enums</h2>
- <div>
- {{#Obj}}
- {{/Obj}}
- </div>
- {{/Enums}}
-</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 8eb067dbe6de87..eccbc99a7ecc4b 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,7 +21,13 @@ target_link_libraries(clang-doc
set(assets
index.js
- template.mustache
+ mustache-index.js
+ class-template.mustache
+ comments-template.mustache
+ enum-template.mustache
+ function-template.mustache
+ namespace-template.mustache
+ clang-doc-mustache.css
clang-doc-default-stylesheet.css
)
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index eee8dbf093cc53..256e29c3c9f894 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -163,6 +163,15 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
return llvm::Error::success();
}
+llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path,
+ llvm::StringRef Asset) {
+ llvm::SmallString<128> Default;
+ llvm::sys::path::native(Path, Default);
+ llvm::sys::path::append(Default, Asset);
+ return Default;
+}
+
+
llvm::Error getDefaultAssetFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -173,13 +182,10 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
llvm::SmallString<128> AssetsPath;
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> DefaultStylesheet;
- llvm::sys::path::native(AssetsPath, DefaultStylesheet);
- llvm::sys::path::append(DefaultStylesheet,
- "clang-doc-default-stylesheet.css");
- llvm::SmallString<128> IndexJS;
- llvm::sys::path::native(AssetsPath, IndexJS);
- llvm::sys::path::append(IndexJS, "index.js");
+ llvm::SmallString<128> DefaultStylesheet =
+ appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
+ llvm::SmallString<128> IndexJS =
+ appendPathNative(AssetsPath, "index.js");
if (!llvm::sys::fs::is_regular_file(IndexJS))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -210,6 +216,7 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
return getDefaultAssetFiles(Argv0, CDCtx);
}
+
llvm::Error getMustacheHtmlFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
if (!UserAssetPath.empty() &&
@@ -228,10 +235,34 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> MustacheTemplate;
- llvm::sys::path::native(AssetsPath, MustacheTemplate);
- llvm::sys::path::append(MustacheTemplate, "template.mustache");
- CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+ llvm::SmallString<128> DefaultStylesheet
+ = appendPathNative(AssetsPath, "clang-doc-mustache.css");
+ llvm::SmallString<128> NamespaceTemplate
+ = appendPathNative(AssetsPath, "namespace-template.mustache");
+ llvm::SmallString<128> ClassTemplate
+ = appendPathNative(AssetsPath, "class-template.mustache");
+ llvm::SmallString<128> EnumTemplate
+ = appendPathNative(AssetsPath, "enum-template.mustache");
+ llvm::SmallString<128> FunctionTemplate
+ = appendPathNative(AssetsPath, "function-template.mustache");
+ llvm::SmallString<128> CommentTemplate
+ = appendPathNative(AssetsPath, "comments-template.mustache");
+ llvm::SmallString<128> IndexJS
+ = appendPathNative(AssetsPath, "mustache-index.js");
+
+ CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+ CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+ std::string(DefaultStylesheet));
+ CDCtx.MustacheTemplates.insert({"namespace-template",
+ NamespaceTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"class-template",
+ ClassTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"enum-template",
+ EnumTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"function-template",
+ FunctionTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"comments-template",
+ CommentTemplate.c_str()});
return llvm::Error::success();
}
>From 47c2d15a0015ea1137b494af9ce712ad23b6d987 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 02:26:22 -0400
Subject: [PATCH 53/62] [clang-doc] init mustache implementation
---
clang-tools-extra/clang-doc/CMakeLists.txt | 5 +-
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 59 +++-
.../clang-doc/HTMLMustacheGenerator.cpp | 289 ++++--------------
.../clang-doc/assets/template.mustache | 23 ++
.../clang-doc/tool/CMakeLists.txt | 8 +-
.../clang-doc/tool/ClangDocMain.cpp | 62 +---
6 files changed, 154 insertions(+), 292 deletions(-)
create mode 100644 clang-tools-extra/clang-doc/assets/template.mustache
diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 0793c343305184..82aded940222db 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,16 +8,15 @@ add_clang_library(clangDoc
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
- FileHelpersClangDoc.cpp
Generators.cpp
HTMLGenerator.cpp
- HTMLMustacheGenerator.cpp
Mapper.cpp
MDGenerator.cpp
Representation.cpp
Serialize.cpp
YAMLGenerator.cpp
-
+ HTMLMustacheGenerator.cpp
+
DEPENDS
omp_gen
ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index aa76d17d9411d6..ed65ac965114b5 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,7 +8,6 @@
#include "Generators.h"
#include "Representation.h"
-#include "FileHelpersClangDoc.h"
#include "clang/Basic/Version.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -252,6 +251,46 @@ static void appendVector(std::vector<Derived> &&New,
std::move(New.begin(), New.end(), std::back_inserter(Original));
}
+// Compute the relative path from an Origin directory to a Destination directory
+static SmallString<128> computeRelativePath(StringRef Destination,
+ StringRef Origin) {
+ // If Origin is empty, the relative path to the Destination is its complete
+ // path.
+ if (Origin.empty())
+ return Destination;
+
+ // The relative path is an empty path if both directories are the same.
+ if (Destination == Origin)
+ return {};
+
+ // These iterators iterate through each of their parent directories
+ llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
+ llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
+ llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
+ llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
+ // Advance both iterators until the paths differ. Example:
+ // Destination = A/B/C/D
+ // Origin = A/B/E/F
+ // FileI will point to C and DirI to E. The directories behind them is the
+ // directory they share (A/B).
+ while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
+ ++FileI;
+ ++DirI;
+ }
+ SmallString<128> Result; // This will hold the resulting path.
+ // Result has to go up one directory for each of the remaining directories in
+ // Origin
+ while (DirI != DirE) {
+ llvm::sys::path::append(Result, "..");
+ ++DirI;
+ }
+ // Result has to append each of the remaining directories in Destination
+ while (FileI != FileE) {
+ llvm::sys::path::append(Result, *FileI);
+ ++FileI;
+ }
+ return Result;
+}
// HTML generation
@@ -1094,6 +1133,24 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
return llvm::Error::success();
}
+static llvm::Error
+copyFile(StringRef FilePath, StringRef OutDirectory) {
+ llvm::SmallString<128> PathWrite;
+ llvm::sys::path::native(OutDirectory, PathWrite);
+ llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
+ llvm::SmallString<128> PathRead;
+ llvm::sys::path::native(FilePath, PathRead);
+ std::error_code OK;
+ std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
+ if (FileErr != OK) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "error creating file " +
+ llvm::sys::path::filename(FilePath) +
+ ": " + FileErr.message() + "\n");
+ }
+ return llvm::Error::success();
+}
+
llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
auto Err = serializeIndex(CDCtx);
if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 6be4c795a68654..63def07c5fa804 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "Generators.h"
#include "Representation.h"
-#include "FileHelpersClangDoc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mustache.h"
@@ -49,12 +48,12 @@ class MustacheTemplateFile : public Template {
Error registerPartialFile(StringRef Name, StringRef FileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
MemoryBuffer::getFile(FileName);
- if (auto EC = BufferOrError.getError())
+ if (auto EC = BufferOrError.getError()) {
return llvm::createFileError("cannot open file", EC);
+ }
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
- return llvm::Error::success();
}
MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -65,54 +64,23 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
-llvm::Error setupTemplate(
- std::unique_ptr<MustacheTemplateFile> &Template,
- StringRef TemplatePath,
- std::vector<std::pair<StringRef, StringRef>> Partials) {
- auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
- if (auto EC = T.getError())
- return llvm::createFileError("cannot open file", EC);
- Template = std::move(T.get());
- for (const auto &P : Partials) {
- auto Err = Template->registerPartialFile(P.first, P.second);
- if (Err)
- return Err;
- }
- return llvm::Error::success();
-}
-
llvm::Error
setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
- auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
- auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
- auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
- auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
- auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
- std::vector<std::pair<StringRef, StringRef>> Partials = {
- {"Comments", CommentFilePath},
- {"FunctionPartial", FunctionFilePath},
- {"EnumPartial", EnumFilePath}
- };
-
- auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
- if (Err)
- return Err;
-
- Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
-
- if (Err)
- return Err;
-
- return llvm::Error::success();
+ auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
+ auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
+ if (auto EC = Template.getError()) {
+ return llvm::createFileError("cannot open file", EC);
+ }
+ NamespaceTemplate = std::move(Template.get());
}
-
llvm::Error
MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
const clang::doc::ClangDocContext &CDCtx) {
- if (auto Err = setupTemplateFiles(CDCtx))
+ if (auto Err = setupTemplateFiles(CDCtx)) {
return Err;
+ }
// Track which directories we already tried to create.
llvm::StringSet<> CreatedDirs;
// Collect all output by file name and create the necessary directories.
@@ -125,9 +93,10 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
if (!CreatedDirs.contains(Path)) {
if (std::error_code Err = llvm::sys::fs::create_directories(Path);
- Err != std::error_code())
+ Err != std::error_code()) {
return llvm::createStringError(Err, "Failed to create directory '%s'.",
Path.c_str());
+ }
CreatedDirs.insert(Path);
}
@@ -139,33 +108,28 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
std::error_code FileErr;
llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
llvm::sys::fs::OF_None);
- if (FileErr)
+ if (FileErr) {
return llvm::createStringError(FileErr, "Error opening file '%s'",
Group.getKey().str().c_str());
-
+ }
for (const auto &Info : Group.getValue()) {
- if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
+ if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
return Err;
+ }
}
}
- return llvm::Error::success();
}
Value extractValue(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
Object Obj = Object();
- Obj.insert({"LineNumber", L.LineNumber});
- Obj.insert({"Filename", L.Filename});
-
if (!L.IsFileInRootDir || !RepositoryUrl) {
- return Obj;
+ Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"Filename", L.Filename});
}
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
- FileURL += "#" + std::to_string(L.LineNumber);
Obj.insert({"FileURL", FileURL});
-
- return Obj;
}
Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -175,8 +139,6 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
Object Obj = Object();
Obj.insert({"Link", Path});
Obj.insert({"Name", I.Name});
- Obj.insert({"QualName", I.QualName});
- Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
return Obj;
}
@@ -227,12 +189,8 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
Value ParamArr = Array();
- for (const auto Val : llvm::enumerate(I.Params)) {
- Value V = Object();
- V.getAsObject()->insert({"Name", Val.value().Name});
- V.getAsObject()->insert({"Type", Val.value().Type.Name});
- V.getAsObject()->insert({"End", Val.index() + 1 == I.Params.size()});
- ParamArr.getAsArray()->emplace_back(V);
+ for (const auto &P : I.Params) {
+ ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
}
Obj.insert({"Params", ParamArr});
@@ -242,13 +200,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
Obj.insert({"FunctionComments", ArrDesc});
}
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- Obj.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
+ if (I.DefLoc) {
+ if (!CDCtx.RepositoryUrl)
+ Obj.insert({"Location", extractValue(*I.DefLoc)});
else
- Obj.insert({"Location", extractValue(L)});
+ Obj.insert({"Location", extractValue(*I.DefLoc,
+ StringRef{*CDCtx.RepositoryUrl})});
}
return Obj;
}
@@ -263,6 +220,7 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumName", EnumType});
Obj.insert({"HasComment", HasComment});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+
Value Arr = Array();
for (const EnumValueInfo& M: I.Members) {
Value EnumValue = Object();
@@ -289,75 +247,17 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumComments", ArrDesc});
}
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- Obj.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
+ if (I.DefLoc) {
+ if (!CDCtx.RepositoryUrl)
+ Obj.insert({"Location", extractValue(*I.DefLoc)});
else
- Obj.insert({"Location", extractValue(L)});
+ Obj.insert({"Location", extractValue(*I.DefLoc,
+ StringRef{*CDCtx.RepositoryUrl})});
}
return Obj;
}
-void extractScopeChildren(const ScopeChildren &S, Object &Obj,
- StringRef ParentInfoDir,
- const ClangDocContext &CDCtx) {
- Value ArrNamespace = Array();
- for (const Reference& Child : S.Namespaces)
- ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!ArrNamespace.getAsArray()->empty())
- Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
-
- Value ArrRecord = Array();
- for (const Reference& Child : S.Records)
- ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!ArrRecord.getAsArray()->empty())
- Obj.insert({"Record", Object{{"Links", ArrRecord}}});
-
- Value ArrFunction = Array();
- Value PublicFunction = Array();
- Value ProtectedFunction = Array();
- Value PrivateFunction = Array();
-
- for (const FunctionInfo& Child : S.Functions) {
- Value F = extractValue(Child, ParentInfoDir, CDCtx);
- AccessSpecifier Access = Child.Access;
- if (Access == AccessSpecifier::AS_public)
- PublicFunction.getAsArray()->emplace_back(F);
- else if (Access == AccessSpecifier::AS_protected)
- ProtectedFunction.getAsArray()->emplace_back(F);
- else
- ArrFunction.getAsArray()->emplace_back(F);
- }
- if (!ArrFunction.getAsArray()->empty())
- Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
-
- if (!PublicFunction.getAsArray()->empty())
- Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
-
- if (!ProtectedFunction.getAsArray()->empty())
- Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
-
-
- Value ArrEnum = Array();
- for (const EnumInfo& Child : S.Enums)
- ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
-
- if (!ArrEnum.getAsArray()->empty())
- Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
-
- Value ArrTypedefs = Array();
- for (const TypedefInfo& Child : S.Typedefs)
- ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
-
- if (!ArrTypedefs.getAsArray()->empty())
- Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
-}
-
Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
Object NamespaceValue = Object();
std::string InfoTitle;
@@ -376,88 +276,36 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
NamespaceValue.insert({"NamespaceComments", ArrDesc });
}
- extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
- return NamespaceValue;
-}
-Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
- Object RecordValue = Object();
+ Value ArrNamespace = Array();
+ for (const Reference& Child : I.Children.Namespaces)
+ ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ NamespaceValue.insert({"Namespace", ArrNamespace});
- if (!I.Description.empty()) {
- Value ArrDesc = Array();
- for (const CommentInfo& Child : I.Description)
- ArrDesc.getAsArray()->emplace_back(extractValue(Child));
- RecordValue.insert({"RecordComments", ArrDesc });
- }
- RecordValue.insert({"Name", I.Name});
- RecordValue.insert({"FullName", I.FullName});
- RecordValue.insert({"RecordType", getTagType(I.TagType)});
+ Value ArrRecord = Array();
+ for (const Reference& Child : I.Children.Records)
+ ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ NamespaceValue.insert({"Record", ArrRecord});
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- RecordValue.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
- else
- RecordValue.insert({"Location", extractValue(L)});
- }
+ Value ArrFunction = Array();
+ for (const FunctionInfo& Child : I.Children.Functions)
+ ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
+ CDCtx));
+ NamespaceValue.insert({"Function", ArrRecord});
- StringRef BasePath = I.getRelativeFilePath("");
- extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
- Value PublicMembers = Array();
- Value ProtectedMembers = Array();
- Value PrivateMembers = Array();
- for (const MemberTypeInfo &Member : I.Members ) {
- Value MemberValue = Object();
- MemberValue.getAsObject()->insert({"Name", Member.Name});
- MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
- if (!Member.Description.empty()) {
- Value ArrDesc = Array();
- for (const CommentInfo& Child : Member.Description)
- ArrDesc.getAsArray()->emplace_back(extractValue(Child));
- MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
- }
-
- if (Member.Access == AccessSpecifier::AS_public)
- PublicMembers.getAsArray()->emplace_back(MemberValue);
- else if (Member.Access == AccessSpecifier::AS_protected)
- ProtectedMembers.getAsArray()->emplace_back(MemberValue);
- else if (Member.Access == AccessSpecifier::AS_private)
- PrivateMembers.getAsArray()->emplace_back(MemberValue);
- }
- if (!PublicMembers.getAsArray()->empty())
- RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
- if (!ProtectedMembers.getAsArray()->empty())
- RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
- if (!PrivateMembers.getAsArray()->empty())
- RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+ Value ArrEnum = Array();
+ for (const EnumInfo& Child : I.Children.Enums)
+ ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
+ NamespaceValue.insert({"Enums", ArrEnum });
- return RecordValue;
-}
-
-void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
- V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
- Value StylesheetArr = Array();
- auto InfoPath = I->getRelativeFilePath("");
- SmallString<128> RelativePath = computeRelativePath("", InfoPath);
- for (const auto &FilePath : CDCtx.UserStylesheets) {
- SmallString<128> StylesheetPath = RelativePath;
- llvm::sys::path::append(StylesheetPath,
- llvm::sys::path::filename(FilePath));
- llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
- StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
- }
- V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+ Value ArrTypedefs = Array();
+ for (const TypedefInfo& Child : I.Children.Typedefs)
+ ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+ NamespaceValue.insert({"Typedefs", ArrTypedefs });
- Value ScriptArr = Array();
- for (auto Script : CDCtx.JsScripts) {
- SmallString<128> JsPath = RelativePath;
- llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
- ScriptArr.getAsArray()->emplace_back(JsPath);
- }
- V.getAsObject()->insert({"Scripts", ScriptArr});
}
-
+
+
llvm::Error
MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -465,26 +313,16 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- setupTemplateValue(CDCtx, V, I);
OS << NamespaceTemplate->render(V);
break;
}
- case InfoType::IT_record: {
- Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
- setupTemplateValue(CDCtx, V, I);
- // Serialize the JSON value to the output stream in a readable format.
- llvm::outs() << llvm::formatv("{0:2}", V);
- OS << RecordTemplate->render(V);
+ case InfoType::IT_record:
break;
- }
case InfoType::IT_enum:
- llvm::outs() << "IT_enum\n";
break;
case InfoType::IT_function:
- llvm::outs() << "IT_Function\n";
break;
case InfoType::IT_typedef:
- llvm::outs() << "IT_typedef\n";
break;
case InfoType::IT_default:
return createStringError(llvm::inconvertibleErrorCode(),
@@ -494,21 +332,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
- llvm::Error Err = llvm::Error::success();
- for (const auto &FilePath : CDCtx.UserStylesheets) {
- Err = copyFile(FilePath, CDCtx.OutDirectory);
- if (Err)
- return Err;
- }
- for (const auto &FilePath : CDCtx.JsScripts) {
- Err = copyFile(FilePath, CDCtx.OutDirectory);
- if (Err)
- return Err;
- }
- return llvm::Error::success();
+
}
-const char *MustacheHTMLGenerator::Format = "mhtml";
+const char *MustacheHTMLGenerator::Format = "mustache";
static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -516,7 +343,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
// This anchor is used to force the linker to link in the generated object
// file and thus register the generator.
-volatile int MHTMLGeneratorAnchorSource = 0;
+volatile int HTMLGeneratorAnchorSource = 0;
} // namespace doc
} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
new file mode 100644
index 00000000000000..af4c60182ae52c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -0,0 +1,23 @@
+{{!
+ 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
+
+ This file defines the template
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+ <head>
+ <meta charset="utf-8"/>
+ <title>{{NamespaceTitle}}</title>
+ </head>
+ {{#NamespaceComments}}
+ <p>Namespace Comment Present!</p>
+ {{/NamespaceComments}}
+ {{#Namespace}}
+ <p>Namespace Present!</p>
+ {{/Namespace}}
+ {{#Record}}
+ <p>Record Present!</p>
+ {{/Record}}
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index eccbc99a7ecc4b..8eb067dbe6de87 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,13 +21,7 @@ target_link_libraries(clang-doc
set(assets
index.js
- mustache-index.js
- class-template.mustache
- comments-template.mustache
- enum-template.mustache
- function-template.mustache
- namespace-template.mustache
- clang-doc-mustache.css
+ template.mustache
clang-doc-default-stylesheet.css
)
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 256e29c3c9f894..fc5a9529111b16 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,7 +103,6 @@ enum OutputFormatTy {
md,
yaml,
html,
- mhtml
};
static llvm::cl::opt<OutputFormatTy>
@@ -113,9 +112,7 @@ static llvm::cl::opt<OutputFormatTy>
clEnumValN(OutputFormatTy::md, "md",
"Documentation in MD format."),
clEnumValN(OutputFormatTy::html, "html",
- "Documentation in HTML format."),
- clEnumValN(OutputFormatTy::mhtml, "mhtml",
- "Documentation in mHTML format")),
+ "Documentation in HTML format.")),
llvm::cl::init(OutputFormatTy::yaml),
llvm::cl::cat(ClangDocCategory));
@@ -127,8 +124,6 @@ std::string getFormatString() {
return "md";
case OutputFormatTy::html:
return "html";
- case OutputFormatTy::mhtml:
- return "mhtml";
}
llvm_unreachable("Unknown OutputFormatTy");
}
@@ -163,15 +158,6 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
return llvm::Error::success();
}
-llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path,
- llvm::StringRef Asset) {
- llvm::SmallString<128> Default;
- llvm::sys::path::native(Path, Default);
- llvm::sys::path::append(Default, Asset);
- return Default;
-}
-
-
llvm::Error getDefaultAssetFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -182,10 +168,13 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
llvm::SmallString<128> AssetsPath;
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> DefaultStylesheet =
- appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
- llvm::SmallString<128> IndexJS =
- appendPathNative(AssetsPath, "index.js");
+ llvm::SmallString<128> DefaultStylesheet;
+ llvm::sys::path::native(AssetsPath, DefaultStylesheet);
+ llvm::sys::path::append(DefaultStylesheet,
+ "clang-doc-default-stylesheet.css");
+ llvm::SmallString<128> IndexJS;
+ llvm::sys::path::native(AssetsPath, IndexJS);
+ llvm::sys::path::append(IndexJS, "index.js");
if (!llvm::sys::fs::is_regular_file(IndexJS))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -216,7 +205,6 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
return getDefaultAssetFiles(Argv0, CDCtx);
}
-
llvm::Error getMustacheHtmlFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
if (!UserAssetPath.empty() &&
@@ -235,36 +223,10 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> DefaultStylesheet
- = appendPathNative(AssetsPath, "clang-doc-mustache.css");
- llvm::SmallString<128> NamespaceTemplate
- = appendPathNative(AssetsPath, "namespace-template.mustache");
- llvm::SmallString<128> ClassTemplate
- = appendPathNative(AssetsPath, "class-template.mustache");
- llvm::SmallString<128> EnumTemplate
- = appendPathNative(AssetsPath, "enum-template.mustache");
- llvm::SmallString<128> FunctionTemplate
- = appendPathNative(AssetsPath, "function-template.mustache");
- llvm::SmallString<128> CommentTemplate
- = appendPathNative(AssetsPath, "comments-template.mustache");
- llvm::SmallString<128> IndexJS
- = appendPathNative(AssetsPath, "mustache-index.js");
-
- CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
- CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
- std::string(DefaultStylesheet));
- CDCtx.MustacheTemplates.insert({"namespace-template",
- NamespaceTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"class-template",
- ClassTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"enum-template",
- EnumTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"function-template",
- FunctionTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"comments-template",
- CommentTemplate.c_str()});
-
- return llvm::Error::success();
+ llvm::SmallString<128> MustacheTemplate;
+ llvm::sys::path::native(AssetsPath, MustacheTemplate);
+ llvm::sys::path::append(MustacheTemplate, "template.mustache");
+ CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
}
/// Make the output of clang-doc deterministic by sorting the children of
>From 279ceee3cbf6ba98f4879999193d67f1c7c4c1e8 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 17:50:03 -0400
Subject: [PATCH 54/62] [clang-doc] add a mustache backend init implementation
---
.../clang-doc/HTMLMustacheGenerator.cpp | 32 ++++++++++++----
.../clang-doc/assets/template.mustache | 37 +++++++++++++++++--
.../clang-doc/tool/ClangDocMain.cpp | 9 ++++-
.../Inputs/basic-project/include/Shape.h | 2 -
4 files changed, 65 insertions(+), 15 deletions(-)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 63def07c5fa804..bfa563ebdc7b37 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -54,6 +54,7 @@ class MustacheTemplateFile : public Template {
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
+ return llvm::Error::success();
}
MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -72,6 +73,7 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
return llvm::createFileError("cannot open file", EC);
}
NamespaceTemplate = std::move(Template.get());
+ return llvm::Error::success();
}
llvm::Error
@@ -118,6 +120,7 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
}
}
}
+ return llvm::Error::success();
}
Value extractValue(const Location &L,
@@ -130,6 +133,8 @@ Value extractValue(const Location &L,
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
Obj.insert({"FileURL", FileURL});
+
+ return Obj;
}
Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -280,29 +285,39 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
Value ArrNamespace = Array();
for (const Reference& Child : I.Children.Namespaces)
ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
- NamespaceValue.insert({"Namespace", ArrNamespace});
+
+ if (!ArrNamespace.getAsArray()->empty())
+ NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
Value ArrRecord = Array();
for (const Reference& Child : I.Children.Records)
ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
- NamespaceValue.insert({"Record", ArrRecord});
+
+ if (!ArrRecord.getAsArray()->empty())
+ NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
Value ArrFunction = Array();
for (const FunctionInfo& Child : I.Children.Functions)
ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
CDCtx));
- NamespaceValue.insert({"Function", ArrRecord});
+ if (!ArrFunction.getAsArray()->empty())
+ NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
Value ArrEnum = Array();
for (const EnumInfo& Child : I.Children.Enums)
ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
- NamespaceValue.insert({"Enums", ArrEnum });
+
+ if (!ArrEnum.getAsArray()->empty())
+ NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
Value ArrTypedefs = Array();
for (const TypedefInfo& Child : I.Children.Typedefs)
ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
- NamespaceValue.insert({"Typedefs", ArrTypedefs });
+ if (!ArrTypedefs.getAsArray()->empty())
+ NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+
+ return NamespaceValue;
}
@@ -313,6 +328,7 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+ llvm::outs() << V << "\n";
OS << NamespaceTemplate->render(V);
break;
}
@@ -332,10 +348,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-
+ return llvm::Error::success();
}
-const char *MustacheHTMLGenerator::Format = "mustache";
+const char *MustacheHTMLGenerator::Format = "mhtml";
static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -343,7 +359,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
// This anchor is used to force the linker to link in the generated object
// file and thus register the generator.
-volatile int HTMLGeneratorAnchorSource = 0;
+volatile int MHTMLGeneratorAnchorSource = 0;
} // namespace doc
} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index af4c60182ae52c..1d3407f8b52920 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
See https://llvm.org/LICENSE.txt for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- This file defines the template
+ This file defines the template for generating Namespaces
}}
<!DOCTYPE html>
<html lang="en-US">
@@ -11,13 +11,42 @@
<meta charset="utf-8"/>
<title>{{NamespaceTitle}}</title>
</head>
+ <h1>{{NamespaceTitle}}</h1>
{{#NamespaceComments}}
- <p>Namespace Comment Present!</p>
+ <p>Namespace Comment</p>
{{/NamespaceComments}}
{{#Namespace}}
- <p>Namespace Present!</p>
+ <h2 id="Namespace">Namespace</h2>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
{{/Namespace}}
{{#Record}}
- <p>Record Present!</p>
+ <h2 id="Class">Class</h2>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
{{/Record}}
+ {{#Function}}
+ <h2 id="Function">Function</h2>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Function}}
+ {{#Enums}}
+ <h2 id="Enums">Enums</h2>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Enums}}
</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index fc5a9529111b16..eee8dbf093cc53 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,6 +103,7 @@ enum OutputFormatTy {
md,
yaml,
html,
+ mhtml
};
static llvm::cl::opt<OutputFormatTy>
@@ -112,7 +113,9 @@ static llvm::cl::opt<OutputFormatTy>
clEnumValN(OutputFormatTy::md, "md",
"Documentation in MD format."),
clEnumValN(OutputFormatTy::html, "html",
- "Documentation in HTML format.")),
+ "Documentation in HTML format."),
+ clEnumValN(OutputFormatTy::mhtml, "mhtml",
+ "Documentation in mHTML format")),
llvm::cl::init(OutputFormatTy::yaml),
llvm::cl::cat(ClangDocCategory));
@@ -124,6 +127,8 @@ std::string getFormatString() {
return "md";
case OutputFormatTy::html:
return "html";
+ case OutputFormatTy::mhtml:
+ return "mhtml";
}
llvm_unreachable("Unknown OutputFormatTy");
}
@@ -227,6 +232,8 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
llvm::sys::path::native(AssetsPath, MustacheTemplate);
llvm::sys::path::append(MustacheTemplate, "template.mustache");
CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+
+ return llvm::Error::success();
}
/// Make the output of clang-doc deterministic by sorting the children of
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index e5c5d4c9e4412b..5354032f4d8323 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,5 +26,3 @@ class Shape {
*/
virtual double perimeter() const = 0;
};
-
-
>From 9ab49b250ac72fcba38638a76709c104db27b2f7 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 24 Sep 2024 16:48:29 -0400
Subject: [PATCH 55/62] [clang-doc] update
---
.../clang-doc/HTMLMustacheGenerator.cpp | 16 ++++++++---
.../clang-doc/assets/comments.mustache | 27 +++++++++++++++++++
.../clang-doc/assets/template.mustache | 7 +++--
.../Inputs/basic-project/include/Shape.h | 2 ++
4 files changed, 46 insertions(+), 6 deletions(-)
create mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index bfa563ebdc7b37..96a685dbe2ae87 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -194,8 +194,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
Value ParamArr = Array();
- for (const auto &P : I.Params) {
- ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+ for (const auto Val : llvm::enumerate(I.Params)) {
+ Value V = Object();
+ V.getAsObject()->insert({"Name", Val.value().Name});
+ V.getAsObject()->insert({"Type", Val.value().Type.Name});
+ V.getAsObject()->insert({"End", Val.index() + 1 == I.Params.size()});
+ ParamArr.getAsArray()->emplace_back(V);
}
Obj.insert({"Params", ParamArr});
@@ -328,17 +332,21 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- llvm::outs() << V << "\n";
- OS << NamespaceTemplate->render(V);
+ llvm::raw_ostream &OS = llvm::outs();
+ llvm::json::OStream J(OS, /*IndentSize=*/2);
+ J.value(V);
break;
}
case InfoType::IT_record:
break;
case InfoType::IT_enum:
+ llvm::outs() << "IT_enum\n";
break;
case InfoType::IT_function:
+ llvm::outs() << "IT_Function\n";
break;
case InfoType::IT_typedef:
+ llvm::outs() << "IT_typedef\n";
break;
case InfoType::IT_default:
return createStringError(llvm::inconvertibleErrorCode(),
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
new file mode 100644
index 00000000000000..1eac4de91836ab
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/comments.mustache
@@ -0,0 +1,27 @@
+{{!
+ 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
+
+ This file defines templates for generating
+}}
+
+{{#FullComments}}
+ {{#Children}}
+ {{>Comment}}
+ {{/Children}}
+{{/FullComments}}
+{{#ParagraphComment}}
+ {{#Children}}
+ {{>Comment}}
+ {{/Children}}
+{{/ParagraphComment}}
+{{#BlockCommandComment}}
+ <div>{{Command}}</div>
+ {{#Children}}
+ {{>Comment}}
+ {{/Children}}
+{{/BlockCommandComment}}
+{{#TextComment}}
+ <p>{{TextComment}}</p>
+{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index 1d3407f8b52920..3b77e0189f4e22 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
See https://llvm.org/LICENSE.txt for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- This file defines the template for generating Namespaces
+ This file defines the template for generating namespaces
}}
<!DOCTYPE html>
<html lang="en-US">
@@ -13,7 +13,9 @@
</head>
<h1>{{NamespaceTitle}}</h1>
{{#NamespaceComments}}
- <p>Namespace Comment</p>
+ <div>
+ {{>Comments}}
+ </div>
{{/NamespaceComments}}
{{#Namespace}}
<h2 id="Namespace">Namespace</h2>
@@ -39,6 +41,7 @@
<h2 id="Function">Function</h2>
<div>
{{#Obj}}
+
{{/Obj}}
</div>
{{/Function}}
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index 5354032f4d8323..e5c5d4c9e4412b 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,3 +26,5 @@ class Shape {
*/
virtual double perimeter() const = 0;
};
+
+
>From 00830fe6ae738ada3389aec794a47d41f9950cfe Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 7 Oct 2024 15:07:41 -0400
Subject: [PATCH 56/62] [clang-doc] add more templates
---
clang-tools-extra/clang-doc/CMakeLists.txt | 5 +-
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 59 +---
.../clang-doc/HTMLMustacheGenerator.cpp | 281 ++++++++++++++----
.../clang-doc/assets/comments.mustache | 27 --
.../clang-doc/assets/template.mustache | 55 ----
.../clang-doc/tool/CMakeLists.txt | 8 +-
.../clang-doc/tool/ClangDocMain.cpp | 53 +++-
7 files changed, 268 insertions(+), 220 deletions(-)
delete mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache
delete mode 100644 clang-tools-extra/clang-doc/assets/template.mustache
diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 82aded940222db..0793c343305184 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,15 +8,16 @@ add_clang_library(clangDoc
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
+ FileHelpersClangDoc.cpp
Generators.cpp
HTMLGenerator.cpp
+ HTMLMustacheGenerator.cpp
Mapper.cpp
MDGenerator.cpp
Representation.cpp
Serialize.cpp
YAMLGenerator.cpp
- HTMLMustacheGenerator.cpp
-
+
DEPENDS
omp_gen
ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ed65ac965114b5..aa76d17d9411d6 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,6 +8,7 @@
#include "Generators.h"
#include "Representation.h"
+#include "FileHelpersClangDoc.h"
#include "clang/Basic/Version.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -251,46 +252,6 @@ static void appendVector(std::vector<Derived> &&New,
std::move(New.begin(), New.end(), std::back_inserter(Original));
}
-// Compute the relative path from an Origin directory to a Destination directory
-static SmallString<128> computeRelativePath(StringRef Destination,
- StringRef Origin) {
- // If Origin is empty, the relative path to the Destination is its complete
- // path.
- if (Origin.empty())
- return Destination;
-
- // The relative path is an empty path if both directories are the same.
- if (Destination == Origin)
- return {};
-
- // These iterators iterate through each of their parent directories
- llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
- llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
- llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
- llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
- // Advance both iterators until the paths differ. Example:
- // Destination = A/B/C/D
- // Origin = A/B/E/F
- // FileI will point to C and DirI to E. The directories behind them is the
- // directory they share (A/B).
- while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
- ++FileI;
- ++DirI;
- }
- SmallString<128> Result; // This will hold the resulting path.
- // Result has to go up one directory for each of the remaining directories in
- // Origin
- while (DirI != DirE) {
- llvm::sys::path::append(Result, "..");
- ++DirI;
- }
- // Result has to append each of the remaining directories in Destination
- while (FileI != FileE) {
- llvm::sys::path::append(Result, *FileI);
- ++FileI;
- }
- return Result;
-}
// HTML generation
@@ -1133,24 +1094,6 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
return llvm::Error::success();
}
-static llvm::Error
-copyFile(StringRef FilePath, StringRef OutDirectory) {
- llvm::SmallString<128> PathWrite;
- llvm::sys::path::native(OutDirectory, PathWrite);
- llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
- llvm::SmallString<128> PathRead;
- llvm::sys::path::native(FilePath, PathRead);
- std::error_code OK;
- std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
- if (FileErr != OK) {
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "error creating file " +
- llvm::sys::path::filename(FilePath) +
- ": " + FileErr.message() + "\n");
- }
- return llvm::Error::success();
-}
-
llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
auto Err = serializeIndex(CDCtx);
if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 96a685dbe2ae87..6be4c795a68654 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Generators.h"
#include "Representation.h"
+#include "FileHelpersClangDoc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mustache.h"
@@ -48,9 +49,8 @@ class MustacheTemplateFile : public Template {
Error registerPartialFile(StringRef Name, StringRef FileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
MemoryBuffer::getFile(FileName);
- if (auto EC = BufferOrError.getError()) {
+ if (auto EC = BufferOrError.getError())
return llvm::createFileError("cannot open file", EC);
- }
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
@@ -65,24 +65,54 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
+llvm::Error setupTemplate(
+ std::unique_ptr<MustacheTemplateFile> &Template,
+ StringRef TemplatePath,
+ std::vector<std::pair<StringRef, StringRef>> Partials) {
+ auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
+ if (auto EC = T.getError())
+ return llvm::createFileError("cannot open file", EC);
+ Template = std::move(T.get());
+ for (const auto &P : Partials) {
+ auto Err = Template->registerPartialFile(P.first, P.second);
+ if (Err)
+ return Err;
+ }
+ return llvm::Error::success();
+}
+
llvm::Error
setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
- auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
- auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
- if (auto EC = Template.getError()) {
- return llvm::createFileError("cannot open file", EC);
- }
- NamespaceTemplate = std::move(Template.get());
+ auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
+ auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
+ auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
+ auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
+ auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+ std::vector<std::pair<StringRef, StringRef>> Partials = {
+ {"Comments", CommentFilePath},
+ {"FunctionPartial", FunctionFilePath},
+ {"EnumPartial", EnumFilePath}
+ };
+
+ auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
+ if (Err)
+ return Err;
+
+ Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
+
+ if (Err)
+ return Err;
+
return llvm::Error::success();
}
+
llvm::Error
MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
const clang::doc::ClangDocContext &CDCtx) {
- if (auto Err = setupTemplateFiles(CDCtx)) {
+ if (auto Err = setupTemplateFiles(CDCtx))
return Err;
- }
// Track which directories we already tried to create.
llvm::StringSet<> CreatedDirs;
// Collect all output by file name and create the necessary directories.
@@ -95,10 +125,9 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
if (!CreatedDirs.contains(Path)) {
if (std::error_code Err = llvm::sys::fs::create_directories(Path);
- Err != std::error_code()) {
+ Err != std::error_code())
return llvm::createStringError(Err, "Failed to create directory '%s'.",
Path.c_str());
- }
CreatedDirs.insert(Path);
}
@@ -110,14 +139,13 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
std::error_code FileErr;
llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
llvm::sys::fs::OF_None);
- if (FileErr) {
+ if (FileErr)
return llvm::createStringError(FileErr, "Error opening file '%s'",
Group.getKey().str().c_str());
- }
+
for (const auto &Info : Group.getValue()) {
- if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+ if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
return Err;
- }
}
}
return llvm::Error::success();
@@ -126,12 +154,15 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
Value extractValue(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
Object Obj = Object();
+ Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"Filename", L.Filename});
+
if (!L.IsFileInRootDir || !RepositoryUrl) {
- Obj.insert({"LineNumber", L.LineNumber});
- Obj.insert({"Filename", L.Filename});
+ return Obj;
}
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+ FileURL += "#" + std::to_string(L.LineNumber);
Obj.insert({"FileURL", FileURL});
return Obj;
@@ -144,6 +175,8 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
Object Obj = Object();
Obj.insert({"Link", Path});
Obj.insert({"Name", I.Name});
+ Obj.insert({"QualName", I.QualName});
+ Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
return Obj;
}
@@ -209,12 +242,13 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
Obj.insert({"FunctionComments", ArrDesc});
}
- if (I.DefLoc) {
- if (!CDCtx.RepositoryUrl)
- Obj.insert({"Location", extractValue(*I.DefLoc)});
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ Obj.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
else
- Obj.insert({"Location", extractValue(*I.DefLoc,
- StringRef{*CDCtx.RepositoryUrl})});
+ Obj.insert({"Location", extractValue(L)});
}
return Obj;
}
@@ -229,7 +263,6 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumName", EnumType});
Obj.insert({"HasComment", HasComment});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
-
Value Arr = Array();
for (const EnumValueInfo& M: I.Members) {
Value EnumValue = Object();
@@ -256,75 +289,175 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumComments", ArrDesc});
}
- if (I.DefLoc) {
- if (!CDCtx.RepositoryUrl)
- Obj.insert({"Location", extractValue(*I.DefLoc)});
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ Obj.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
else
- Obj.insert({"Location", extractValue(*I.DefLoc,
- StringRef{*CDCtx.RepositoryUrl})});
+ Obj.insert({"Location", extractValue(L)});
}
return Obj;
}
-Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
- Object NamespaceValue = Object();
- std::string InfoTitle;
- if (I.Name.str() == "")
- InfoTitle = "Global Namespace";
- else
- InfoTitle = ("namespace " + I.Name).str();
-
- StringRef BasePath = I.getRelativeFilePath("");
- NamespaceValue.insert({"NamespaceTitle", InfoTitle});
- NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
-
- if (!I.Description.empty()) {
- Value ArrDesc = Array();
- for (const CommentInfo& Child : I.Description)
- ArrDesc.getAsArray()->emplace_back(extractValue(Child));
- NamespaceValue.insert({"NamespaceComments", ArrDesc });
- }
-
+void extractScopeChildren(const ScopeChildren &S, Object &Obj,
+ StringRef ParentInfoDir,
+ const ClangDocContext &CDCtx) {
Value ArrNamespace = Array();
- for (const Reference& Child : I.Children.Namespaces)
- ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ for (const Reference& Child : S.Namespaces)
+ ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
if (!ArrNamespace.getAsArray()->empty())
- NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+ Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
Value ArrRecord = Array();
- for (const Reference& Child : I.Children.Records)
- ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ for (const Reference& Child : S.Records)
+ ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
if (!ArrRecord.getAsArray()->empty())
- NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
+ Obj.insert({"Record", Object{{"Links", ArrRecord}}});
Value ArrFunction = Array();
- for (const FunctionInfo& Child : I.Children.Functions)
- ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
- CDCtx));
+ Value PublicFunction = Array();
+ Value ProtectedFunction = Array();
+ Value PrivateFunction = Array();
+
+ for (const FunctionInfo& Child : S.Functions) {
+ Value F = extractValue(Child, ParentInfoDir, CDCtx);
+ AccessSpecifier Access = Child.Access;
+ if (Access == AccessSpecifier::AS_public)
+ PublicFunction.getAsArray()->emplace_back(F);
+ else if (Access == AccessSpecifier::AS_protected)
+ ProtectedFunction.getAsArray()->emplace_back(F);
+ else
+ ArrFunction.getAsArray()->emplace_back(F);
+ }
if (!ArrFunction.getAsArray()->empty())
- NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
+ Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+
+ if (!PublicFunction.getAsArray()->empty())
+ Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+
+ if (!ProtectedFunction.getAsArray()->empty())
+ Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+
Value ArrEnum = Array();
- for (const EnumInfo& Child : I.Children.Enums)
+ for (const EnumInfo& Child : S.Enums)
ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
if (!ArrEnum.getAsArray()->empty())
- NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
+ Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
Value ArrTypedefs = Array();
- for (const TypedefInfo& Child : I.Children.Typedefs)
+ for (const TypedefInfo& Child : S.Typedefs)
ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
if (!ArrTypedefs.getAsArray()->empty())
- NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+ Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+ Object NamespaceValue = Object();
+ std::string InfoTitle;
+ if (I.Name.str() == "")
+ InfoTitle = "Global Namespace";
+ else
+ InfoTitle = ("namespace " + I.Name).str();
+
+ StringRef BasePath = I.getRelativeFilePath("");
+ NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+ NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ NamespaceValue.insert({"NamespaceComments", ArrDesc });
+ }
+ extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
return NamespaceValue;
}
+Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
+ Object RecordValue = Object();
+
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ RecordValue.insert({"RecordComments", ArrDesc });
+ }
+ RecordValue.insert({"Name", I.Name});
+ RecordValue.insert({"FullName", I.FullName});
+ RecordValue.insert({"RecordType", getTagType(I.TagType)});
+
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ RecordValue.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
+ else
+ RecordValue.insert({"Location", extractValue(L)});
+ }
+
+ StringRef BasePath = I.getRelativeFilePath("");
+ extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
+ Value PublicMembers = Array();
+ Value ProtectedMembers = Array();
+ Value PrivateMembers = Array();
+ for (const MemberTypeInfo &Member : I.Members ) {
+ Value MemberValue = Object();
+ MemberValue.getAsObject()->insert({"Name", Member.Name});
+ MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
+ if (!Member.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : Member.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
+ }
+
+ if (Member.Access == AccessSpecifier::AS_public)
+ PublicMembers.getAsArray()->emplace_back(MemberValue);
+ else if (Member.Access == AccessSpecifier::AS_protected)
+ ProtectedMembers.getAsArray()->emplace_back(MemberValue);
+ else if (Member.Access == AccessSpecifier::AS_private)
+ PrivateMembers.getAsArray()->emplace_back(MemberValue);
+ }
+ if (!PublicMembers.getAsArray()->empty())
+ RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
+ if (!ProtectedMembers.getAsArray()->empty())
+ RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
+ if (!PrivateMembers.getAsArray()->empty())
+ RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+
+ return RecordValue;
+}
+void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
+ V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+ Value StylesheetArr = Array();
+ auto InfoPath = I->getRelativeFilePath("");
+ SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+ for (const auto &FilePath : CDCtx.UserStylesheets) {
+ SmallString<128> StylesheetPath = RelativePath;
+ llvm::sys::path::append(StylesheetPath,
+ llvm::sys::path::filename(FilePath));
+ llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
+ StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+ }
+ V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+
+ Value ScriptArr = Array();
+ for (auto Script : CDCtx.JsScripts) {
+ SmallString<128> JsPath = RelativePath;
+ llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
+ ScriptArr.getAsArray()->emplace_back(JsPath);
+ }
+ V.getAsObject()->insert({"Scripts", ScriptArr});
+}
+
llvm::Error
MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -332,13 +465,18 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- llvm::raw_ostream &OS = llvm::outs();
- llvm::json::OStream J(OS, /*IndentSize=*/2);
- J.value(V);
+ setupTemplateValue(CDCtx, V, I);
+ OS << NamespaceTemplate->render(V);
break;
}
- case InfoType::IT_record:
+ case InfoType::IT_record: {
+ Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
+ setupTemplateValue(CDCtx, V, I);
+ // Serialize the JSON value to the output stream in a readable format.
+ llvm::outs() << llvm::formatv("{0:2}", V);
+ OS << RecordTemplate->render(V);
break;
+ }
case InfoType::IT_enum:
llvm::outs() << "IT_enum\n";
break;
@@ -356,6 +494,17 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+ llvm::Error Err = llvm::Error::success();
+ for (const auto &FilePath : CDCtx.UserStylesheets) {
+ Err = copyFile(FilePath, CDCtx.OutDirectory);
+ if (Err)
+ return Err;
+ }
+ for (const auto &FilePath : CDCtx.JsScripts) {
+ Err = copyFile(FilePath, CDCtx.OutDirectory);
+ if (Err)
+ return Err;
+ }
return llvm::Error::success();
}
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
deleted file mode 100644
index 1eac4de91836ab..00000000000000
--- a/clang-tools-extra/clang-doc/assets/comments.mustache
+++ /dev/null
@@ -1,27 +0,0 @@
-{{!
- 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
-
- This file defines templates for generating
-}}
-
-{{#FullComments}}
- {{#Children}}
- {{>Comment}}
- {{/Children}}
-{{/FullComments}}
-{{#ParagraphComment}}
- {{#Children}}
- {{>Comment}}
- {{/Children}}
-{{/ParagraphComment}}
-{{#BlockCommandComment}}
- <div>{{Command}}</div>
- {{#Children}}
- {{>Comment}}
- {{/Children}}
-{{/BlockCommandComment}}
-{{#TextComment}}
- <p>{{TextComment}}</p>
-{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
deleted file mode 100644
index 3b77e0189f4e22..00000000000000
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ /dev/null
@@ -1,55 +0,0 @@
-{{!
- 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
-
- This file defines the template for generating namespaces
-}}
-<!DOCTYPE html>
-<html lang="en-US">
- <head>
- <meta charset="utf-8"/>
- <title>{{NamespaceTitle}}</title>
- </head>
- <h1>{{NamespaceTitle}}</h1>
- {{#NamespaceComments}}
- <div>
- {{>Comments}}
- </div>
- {{/NamespaceComments}}
- {{#Namespace}}
- <h2 id="Namespace">Namespace</h2>
- <ul>
- {{#Links}}
- <li>
- <a href="{{Link}}">{{Name}}</a>
- </li>
- {{/Links}}
- </ul>
- {{/Namespace}}
- {{#Record}}
- <h2 id="Class">Class</h2>
- <ul>
- {{#Links}}
- <li>
- <a href="{{Link}}">{{Name}}</a>
- </li>
- {{/Links}}
- </ul>
- {{/Record}}
- {{#Function}}
- <h2 id="Function">Function</h2>
- <div>
- {{#Obj}}
-
- {{/Obj}}
- </div>
- {{/Function}}
- {{#Enums}}
- <h2 id="Enums">Enums</h2>
- <div>
- {{#Obj}}
- {{/Obj}}
- </div>
- {{/Enums}}
-</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 8eb067dbe6de87..eccbc99a7ecc4b 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,7 +21,13 @@ target_link_libraries(clang-doc
set(assets
index.js
- template.mustache
+ mustache-index.js
+ class-template.mustache
+ comments-template.mustache
+ enum-template.mustache
+ function-template.mustache
+ namespace-template.mustache
+ clang-doc-mustache.css
clang-doc-default-stylesheet.css
)
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index eee8dbf093cc53..256e29c3c9f894 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -163,6 +163,15 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
return llvm::Error::success();
}
+llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path,
+ llvm::StringRef Asset) {
+ llvm::SmallString<128> Default;
+ llvm::sys::path::native(Path, Default);
+ llvm::sys::path::append(Default, Asset);
+ return Default;
+}
+
+
llvm::Error getDefaultAssetFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -173,13 +182,10 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
llvm::SmallString<128> AssetsPath;
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> DefaultStylesheet;
- llvm::sys::path::native(AssetsPath, DefaultStylesheet);
- llvm::sys::path::append(DefaultStylesheet,
- "clang-doc-default-stylesheet.css");
- llvm::SmallString<128> IndexJS;
- llvm::sys::path::native(AssetsPath, IndexJS);
- llvm::sys::path::append(IndexJS, "index.js");
+ llvm::SmallString<128> DefaultStylesheet =
+ appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
+ llvm::SmallString<128> IndexJS =
+ appendPathNative(AssetsPath, "index.js");
if (!llvm::sys::fs::is_regular_file(IndexJS))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -210,6 +216,7 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
return getDefaultAssetFiles(Argv0, CDCtx);
}
+
llvm::Error getMustacheHtmlFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
if (!UserAssetPath.empty() &&
@@ -228,10 +235,34 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> MustacheTemplate;
- llvm::sys::path::native(AssetsPath, MustacheTemplate);
- llvm::sys::path::append(MustacheTemplate, "template.mustache");
- CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+ llvm::SmallString<128> DefaultStylesheet
+ = appendPathNative(AssetsPath, "clang-doc-mustache.css");
+ llvm::SmallString<128> NamespaceTemplate
+ = appendPathNative(AssetsPath, "namespace-template.mustache");
+ llvm::SmallString<128> ClassTemplate
+ = appendPathNative(AssetsPath, "class-template.mustache");
+ llvm::SmallString<128> EnumTemplate
+ = appendPathNative(AssetsPath, "enum-template.mustache");
+ llvm::SmallString<128> FunctionTemplate
+ = appendPathNative(AssetsPath, "function-template.mustache");
+ llvm::SmallString<128> CommentTemplate
+ = appendPathNative(AssetsPath, "comments-template.mustache");
+ llvm::SmallString<128> IndexJS
+ = appendPathNative(AssetsPath, "mustache-index.js");
+
+ CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+ CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+ std::string(DefaultStylesheet));
+ CDCtx.MustacheTemplates.insert({"namespace-template",
+ NamespaceTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"class-template",
+ ClassTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"enum-template",
+ EnumTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"function-template",
+ FunctionTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"comments-template",
+ CommentTemplate.c_str()});
return llvm::Error::success();
}
>From 08a9f9960969e040eac234b5c67aff7acf03bc2f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 02:26:22 -0400
Subject: [PATCH 57/62] [clang-doc] init mustache implementation
---
clang-tools-extra/clang-doc/CMakeLists.txt | 5 +-
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 59 +++-
.../clang-doc/HTMLMustacheGenerator.cpp | 289 ++++--------------
.../clang-doc/assets/template.mustache | 23 ++
.../clang-doc/tool/CMakeLists.txt | 8 +-
.../clang-doc/tool/ClangDocMain.cpp | 62 +---
6 files changed, 154 insertions(+), 292 deletions(-)
create mode 100644 clang-tools-extra/clang-doc/assets/template.mustache
diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 0793c343305184..82aded940222db 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,16 +8,15 @@ add_clang_library(clangDoc
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
- FileHelpersClangDoc.cpp
Generators.cpp
HTMLGenerator.cpp
- HTMLMustacheGenerator.cpp
Mapper.cpp
MDGenerator.cpp
Representation.cpp
Serialize.cpp
YAMLGenerator.cpp
-
+ HTMLMustacheGenerator.cpp
+
DEPENDS
omp_gen
ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index aa76d17d9411d6..ed65ac965114b5 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,7 +8,6 @@
#include "Generators.h"
#include "Representation.h"
-#include "FileHelpersClangDoc.h"
#include "clang/Basic/Version.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -252,6 +251,46 @@ static void appendVector(std::vector<Derived> &&New,
std::move(New.begin(), New.end(), std::back_inserter(Original));
}
+// Compute the relative path from an Origin directory to a Destination directory
+static SmallString<128> computeRelativePath(StringRef Destination,
+ StringRef Origin) {
+ // If Origin is empty, the relative path to the Destination is its complete
+ // path.
+ if (Origin.empty())
+ return Destination;
+
+ // The relative path is an empty path if both directories are the same.
+ if (Destination == Origin)
+ return {};
+
+ // These iterators iterate through each of their parent directories
+ llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
+ llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
+ llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
+ llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
+ // Advance both iterators until the paths differ. Example:
+ // Destination = A/B/C/D
+ // Origin = A/B/E/F
+ // FileI will point to C and DirI to E. The directories behind them is the
+ // directory they share (A/B).
+ while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
+ ++FileI;
+ ++DirI;
+ }
+ SmallString<128> Result; // This will hold the resulting path.
+ // Result has to go up one directory for each of the remaining directories in
+ // Origin
+ while (DirI != DirE) {
+ llvm::sys::path::append(Result, "..");
+ ++DirI;
+ }
+ // Result has to append each of the remaining directories in Destination
+ while (FileI != FileE) {
+ llvm::sys::path::append(Result, *FileI);
+ ++FileI;
+ }
+ return Result;
+}
// HTML generation
@@ -1094,6 +1133,24 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
return llvm::Error::success();
}
+static llvm::Error
+copyFile(StringRef FilePath, StringRef OutDirectory) {
+ llvm::SmallString<128> PathWrite;
+ llvm::sys::path::native(OutDirectory, PathWrite);
+ llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
+ llvm::SmallString<128> PathRead;
+ llvm::sys::path::native(FilePath, PathRead);
+ std::error_code OK;
+ std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
+ if (FileErr != OK) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "error creating file " +
+ llvm::sys::path::filename(FilePath) +
+ ": " + FileErr.message() + "\n");
+ }
+ return llvm::Error::success();
+}
+
llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
auto Err = serializeIndex(CDCtx);
if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 6be4c795a68654..63def07c5fa804 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "Generators.h"
#include "Representation.h"
-#include "FileHelpersClangDoc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mustache.h"
@@ -49,12 +48,12 @@ class MustacheTemplateFile : public Template {
Error registerPartialFile(StringRef Name, StringRef FileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
MemoryBuffer::getFile(FileName);
- if (auto EC = BufferOrError.getError())
+ if (auto EC = BufferOrError.getError()) {
return llvm::createFileError("cannot open file", EC);
+ }
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
- return llvm::Error::success();
}
MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -65,54 +64,23 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
-llvm::Error setupTemplate(
- std::unique_ptr<MustacheTemplateFile> &Template,
- StringRef TemplatePath,
- std::vector<std::pair<StringRef, StringRef>> Partials) {
- auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
- if (auto EC = T.getError())
- return llvm::createFileError("cannot open file", EC);
- Template = std::move(T.get());
- for (const auto &P : Partials) {
- auto Err = Template->registerPartialFile(P.first, P.second);
- if (Err)
- return Err;
- }
- return llvm::Error::success();
-}
-
llvm::Error
setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
- auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
- auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
- auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
- auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
- auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
- std::vector<std::pair<StringRef, StringRef>> Partials = {
- {"Comments", CommentFilePath},
- {"FunctionPartial", FunctionFilePath},
- {"EnumPartial", EnumFilePath}
- };
-
- auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
- if (Err)
- return Err;
-
- Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
-
- if (Err)
- return Err;
-
- return llvm::Error::success();
+ auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
+ auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
+ if (auto EC = Template.getError()) {
+ return llvm::createFileError("cannot open file", EC);
+ }
+ NamespaceTemplate = std::move(Template.get());
}
-
llvm::Error
MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
const clang::doc::ClangDocContext &CDCtx) {
- if (auto Err = setupTemplateFiles(CDCtx))
+ if (auto Err = setupTemplateFiles(CDCtx)) {
return Err;
+ }
// Track which directories we already tried to create.
llvm::StringSet<> CreatedDirs;
// Collect all output by file name and create the necessary directories.
@@ -125,9 +93,10 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
if (!CreatedDirs.contains(Path)) {
if (std::error_code Err = llvm::sys::fs::create_directories(Path);
- Err != std::error_code())
+ Err != std::error_code()) {
return llvm::createStringError(Err, "Failed to create directory '%s'.",
Path.c_str());
+ }
CreatedDirs.insert(Path);
}
@@ -139,33 +108,28 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
std::error_code FileErr;
llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
llvm::sys::fs::OF_None);
- if (FileErr)
+ if (FileErr) {
return llvm::createStringError(FileErr, "Error opening file '%s'",
Group.getKey().str().c_str());
-
+ }
for (const auto &Info : Group.getValue()) {
- if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
+ if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
return Err;
+ }
}
}
- return llvm::Error::success();
}
Value extractValue(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
Object Obj = Object();
- Obj.insert({"LineNumber", L.LineNumber});
- Obj.insert({"Filename", L.Filename});
-
if (!L.IsFileInRootDir || !RepositoryUrl) {
- return Obj;
+ Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"Filename", L.Filename});
}
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
- FileURL += "#" + std::to_string(L.LineNumber);
Obj.insert({"FileURL", FileURL});
-
- return Obj;
}
Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -175,8 +139,6 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
Object Obj = Object();
Obj.insert({"Link", Path});
Obj.insert({"Name", I.Name});
- Obj.insert({"QualName", I.QualName});
- Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
return Obj;
}
@@ -227,12 +189,8 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
Value ParamArr = Array();
- for (const auto Val : llvm::enumerate(I.Params)) {
- Value V = Object();
- V.getAsObject()->insert({"Name", Val.value().Name});
- V.getAsObject()->insert({"Type", Val.value().Type.Name});
- V.getAsObject()->insert({"End", Val.index() + 1 == I.Params.size()});
- ParamArr.getAsArray()->emplace_back(V);
+ for (const auto &P : I.Params) {
+ ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
}
Obj.insert({"Params", ParamArr});
@@ -242,13 +200,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
Obj.insert({"FunctionComments", ArrDesc});
}
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- Obj.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
+ if (I.DefLoc) {
+ if (!CDCtx.RepositoryUrl)
+ Obj.insert({"Location", extractValue(*I.DefLoc)});
else
- Obj.insert({"Location", extractValue(L)});
+ Obj.insert({"Location", extractValue(*I.DefLoc,
+ StringRef{*CDCtx.RepositoryUrl})});
}
return Obj;
}
@@ -263,6 +220,7 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumName", EnumType});
Obj.insert({"HasComment", HasComment});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+
Value Arr = Array();
for (const EnumValueInfo& M: I.Members) {
Value EnumValue = Object();
@@ -289,75 +247,17 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumComments", ArrDesc});
}
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- Obj.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
+ if (I.DefLoc) {
+ if (!CDCtx.RepositoryUrl)
+ Obj.insert({"Location", extractValue(*I.DefLoc)});
else
- Obj.insert({"Location", extractValue(L)});
+ Obj.insert({"Location", extractValue(*I.DefLoc,
+ StringRef{*CDCtx.RepositoryUrl})});
}
return Obj;
}
-void extractScopeChildren(const ScopeChildren &S, Object &Obj,
- StringRef ParentInfoDir,
- const ClangDocContext &CDCtx) {
- Value ArrNamespace = Array();
- for (const Reference& Child : S.Namespaces)
- ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!ArrNamespace.getAsArray()->empty())
- Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
-
- Value ArrRecord = Array();
- for (const Reference& Child : S.Records)
- ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!ArrRecord.getAsArray()->empty())
- Obj.insert({"Record", Object{{"Links", ArrRecord}}});
-
- Value ArrFunction = Array();
- Value PublicFunction = Array();
- Value ProtectedFunction = Array();
- Value PrivateFunction = Array();
-
- for (const FunctionInfo& Child : S.Functions) {
- Value F = extractValue(Child, ParentInfoDir, CDCtx);
- AccessSpecifier Access = Child.Access;
- if (Access == AccessSpecifier::AS_public)
- PublicFunction.getAsArray()->emplace_back(F);
- else if (Access == AccessSpecifier::AS_protected)
- ProtectedFunction.getAsArray()->emplace_back(F);
- else
- ArrFunction.getAsArray()->emplace_back(F);
- }
- if (!ArrFunction.getAsArray()->empty())
- Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
-
- if (!PublicFunction.getAsArray()->empty())
- Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
-
- if (!ProtectedFunction.getAsArray()->empty())
- Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
-
-
- Value ArrEnum = Array();
- for (const EnumInfo& Child : S.Enums)
- ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
-
- if (!ArrEnum.getAsArray()->empty())
- Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
-
- Value ArrTypedefs = Array();
- for (const TypedefInfo& Child : S.Typedefs)
- ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
-
- if (!ArrTypedefs.getAsArray()->empty())
- Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
-}
-
Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
Object NamespaceValue = Object();
std::string InfoTitle;
@@ -376,88 +276,36 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
NamespaceValue.insert({"NamespaceComments", ArrDesc });
}
- extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
- return NamespaceValue;
-}
-Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
- Object RecordValue = Object();
+ Value ArrNamespace = Array();
+ for (const Reference& Child : I.Children.Namespaces)
+ ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ NamespaceValue.insert({"Namespace", ArrNamespace});
- if (!I.Description.empty()) {
- Value ArrDesc = Array();
- for (const CommentInfo& Child : I.Description)
- ArrDesc.getAsArray()->emplace_back(extractValue(Child));
- RecordValue.insert({"RecordComments", ArrDesc });
- }
- RecordValue.insert({"Name", I.Name});
- RecordValue.insert({"FullName", I.FullName});
- RecordValue.insert({"RecordType", getTagType(I.TagType)});
+ Value ArrRecord = Array();
+ for (const Reference& Child : I.Children.Records)
+ ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ NamespaceValue.insert({"Record", ArrRecord});
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- RecordValue.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
- else
- RecordValue.insert({"Location", extractValue(L)});
- }
+ Value ArrFunction = Array();
+ for (const FunctionInfo& Child : I.Children.Functions)
+ ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
+ CDCtx));
+ NamespaceValue.insert({"Function", ArrRecord});
- StringRef BasePath = I.getRelativeFilePath("");
- extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
- Value PublicMembers = Array();
- Value ProtectedMembers = Array();
- Value PrivateMembers = Array();
- for (const MemberTypeInfo &Member : I.Members ) {
- Value MemberValue = Object();
- MemberValue.getAsObject()->insert({"Name", Member.Name});
- MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
- if (!Member.Description.empty()) {
- Value ArrDesc = Array();
- for (const CommentInfo& Child : Member.Description)
- ArrDesc.getAsArray()->emplace_back(extractValue(Child));
- MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
- }
-
- if (Member.Access == AccessSpecifier::AS_public)
- PublicMembers.getAsArray()->emplace_back(MemberValue);
- else if (Member.Access == AccessSpecifier::AS_protected)
- ProtectedMembers.getAsArray()->emplace_back(MemberValue);
- else if (Member.Access == AccessSpecifier::AS_private)
- PrivateMembers.getAsArray()->emplace_back(MemberValue);
- }
- if (!PublicMembers.getAsArray()->empty())
- RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
- if (!ProtectedMembers.getAsArray()->empty())
- RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
- if (!PrivateMembers.getAsArray()->empty())
- RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+ Value ArrEnum = Array();
+ for (const EnumInfo& Child : I.Children.Enums)
+ ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
+ NamespaceValue.insert({"Enums", ArrEnum });
- return RecordValue;
-}
-
-void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
- V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
- Value StylesheetArr = Array();
- auto InfoPath = I->getRelativeFilePath("");
- SmallString<128> RelativePath = computeRelativePath("", InfoPath);
- for (const auto &FilePath : CDCtx.UserStylesheets) {
- SmallString<128> StylesheetPath = RelativePath;
- llvm::sys::path::append(StylesheetPath,
- llvm::sys::path::filename(FilePath));
- llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
- StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
- }
- V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+ Value ArrTypedefs = Array();
+ for (const TypedefInfo& Child : I.Children.Typedefs)
+ ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+ NamespaceValue.insert({"Typedefs", ArrTypedefs });
- Value ScriptArr = Array();
- for (auto Script : CDCtx.JsScripts) {
- SmallString<128> JsPath = RelativePath;
- llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
- ScriptArr.getAsArray()->emplace_back(JsPath);
- }
- V.getAsObject()->insert({"Scripts", ScriptArr});
}
-
+
+
llvm::Error
MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -465,26 +313,16 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- setupTemplateValue(CDCtx, V, I);
OS << NamespaceTemplate->render(V);
break;
}
- case InfoType::IT_record: {
- Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
- setupTemplateValue(CDCtx, V, I);
- // Serialize the JSON value to the output stream in a readable format.
- llvm::outs() << llvm::formatv("{0:2}", V);
- OS << RecordTemplate->render(V);
+ case InfoType::IT_record:
break;
- }
case InfoType::IT_enum:
- llvm::outs() << "IT_enum\n";
break;
case InfoType::IT_function:
- llvm::outs() << "IT_Function\n";
break;
case InfoType::IT_typedef:
- llvm::outs() << "IT_typedef\n";
break;
case InfoType::IT_default:
return createStringError(llvm::inconvertibleErrorCode(),
@@ -494,21 +332,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
- llvm::Error Err = llvm::Error::success();
- for (const auto &FilePath : CDCtx.UserStylesheets) {
- Err = copyFile(FilePath, CDCtx.OutDirectory);
- if (Err)
- return Err;
- }
- for (const auto &FilePath : CDCtx.JsScripts) {
- Err = copyFile(FilePath, CDCtx.OutDirectory);
- if (Err)
- return Err;
- }
- return llvm::Error::success();
+
}
-const char *MustacheHTMLGenerator::Format = "mhtml";
+const char *MustacheHTMLGenerator::Format = "mustache";
static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -516,7 +343,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
// This anchor is used to force the linker to link in the generated object
// file and thus register the generator.
-volatile int MHTMLGeneratorAnchorSource = 0;
+volatile int HTMLGeneratorAnchorSource = 0;
} // namespace doc
} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
new file mode 100644
index 00000000000000..af4c60182ae52c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -0,0 +1,23 @@
+{{!
+ 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
+
+ This file defines the template
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+ <head>
+ <meta charset="utf-8"/>
+ <title>{{NamespaceTitle}}</title>
+ </head>
+ {{#NamespaceComments}}
+ <p>Namespace Comment Present!</p>
+ {{/NamespaceComments}}
+ {{#Namespace}}
+ <p>Namespace Present!</p>
+ {{/Namespace}}
+ {{#Record}}
+ <p>Record Present!</p>
+ {{/Record}}
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index eccbc99a7ecc4b..8eb067dbe6de87 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,13 +21,7 @@ target_link_libraries(clang-doc
set(assets
index.js
- mustache-index.js
- class-template.mustache
- comments-template.mustache
- enum-template.mustache
- function-template.mustache
- namespace-template.mustache
- clang-doc-mustache.css
+ template.mustache
clang-doc-default-stylesheet.css
)
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 256e29c3c9f894..fc5a9529111b16 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,7 +103,6 @@ enum OutputFormatTy {
md,
yaml,
html,
- mhtml
};
static llvm::cl::opt<OutputFormatTy>
@@ -113,9 +112,7 @@ static llvm::cl::opt<OutputFormatTy>
clEnumValN(OutputFormatTy::md, "md",
"Documentation in MD format."),
clEnumValN(OutputFormatTy::html, "html",
- "Documentation in HTML format."),
- clEnumValN(OutputFormatTy::mhtml, "mhtml",
- "Documentation in mHTML format")),
+ "Documentation in HTML format.")),
llvm::cl::init(OutputFormatTy::yaml),
llvm::cl::cat(ClangDocCategory));
@@ -127,8 +124,6 @@ std::string getFormatString() {
return "md";
case OutputFormatTy::html:
return "html";
- case OutputFormatTy::mhtml:
- return "mhtml";
}
llvm_unreachable("Unknown OutputFormatTy");
}
@@ -163,15 +158,6 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
return llvm::Error::success();
}
-llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path,
- llvm::StringRef Asset) {
- llvm::SmallString<128> Default;
- llvm::sys::path::native(Path, Default);
- llvm::sys::path::append(Default, Asset);
- return Default;
-}
-
-
llvm::Error getDefaultAssetFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -182,10 +168,13 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
llvm::SmallString<128> AssetsPath;
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> DefaultStylesheet =
- appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
- llvm::SmallString<128> IndexJS =
- appendPathNative(AssetsPath, "index.js");
+ llvm::SmallString<128> DefaultStylesheet;
+ llvm::sys::path::native(AssetsPath, DefaultStylesheet);
+ llvm::sys::path::append(DefaultStylesheet,
+ "clang-doc-default-stylesheet.css");
+ llvm::SmallString<128> IndexJS;
+ llvm::sys::path::native(AssetsPath, IndexJS);
+ llvm::sys::path::append(IndexJS, "index.js");
if (!llvm::sys::fs::is_regular_file(IndexJS))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -216,7 +205,6 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
return getDefaultAssetFiles(Argv0, CDCtx);
}
-
llvm::Error getMustacheHtmlFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
if (!UserAssetPath.empty() &&
@@ -235,36 +223,10 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> DefaultStylesheet
- = appendPathNative(AssetsPath, "clang-doc-mustache.css");
- llvm::SmallString<128> NamespaceTemplate
- = appendPathNative(AssetsPath, "namespace-template.mustache");
- llvm::SmallString<128> ClassTemplate
- = appendPathNative(AssetsPath, "class-template.mustache");
- llvm::SmallString<128> EnumTemplate
- = appendPathNative(AssetsPath, "enum-template.mustache");
- llvm::SmallString<128> FunctionTemplate
- = appendPathNative(AssetsPath, "function-template.mustache");
- llvm::SmallString<128> CommentTemplate
- = appendPathNative(AssetsPath, "comments-template.mustache");
- llvm::SmallString<128> IndexJS
- = appendPathNative(AssetsPath, "mustache-index.js");
-
- CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
- CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
- std::string(DefaultStylesheet));
- CDCtx.MustacheTemplates.insert({"namespace-template",
- NamespaceTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"class-template",
- ClassTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"enum-template",
- EnumTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"function-template",
- FunctionTemplate.c_str()});
- CDCtx.MustacheTemplates.insert({"comments-template",
- CommentTemplate.c_str()});
-
- return llvm::Error::success();
+ llvm::SmallString<128> MustacheTemplate;
+ llvm::sys::path::native(AssetsPath, MustacheTemplate);
+ llvm::sys::path::append(MustacheTemplate, "template.mustache");
+ CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
}
/// Make the output of clang-doc deterministic by sorting the children of
>From 7e1399b9e73ed4fcedb661f58321f7ab48347c4a Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 17:50:03 -0400
Subject: [PATCH 58/62] [clang-doc] add a mustache backend init implementation
---
.../clang-doc/HTMLMustacheGenerator.cpp | 32 ++++++++++++----
.../clang-doc/assets/template.mustache | 37 +++++++++++++++++--
.../clang-doc/tool/ClangDocMain.cpp | 9 ++++-
.../Inputs/basic-project/include/Shape.h | 2 -
4 files changed, 65 insertions(+), 15 deletions(-)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 63def07c5fa804..bfa563ebdc7b37 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -54,6 +54,7 @@ class MustacheTemplateFile : public Template {
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
+ return llvm::Error::success();
}
MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -72,6 +73,7 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
return llvm::createFileError("cannot open file", EC);
}
NamespaceTemplate = std::move(Template.get());
+ return llvm::Error::success();
}
llvm::Error
@@ -118,6 +120,7 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
}
}
}
+ return llvm::Error::success();
}
Value extractValue(const Location &L,
@@ -130,6 +133,8 @@ Value extractValue(const Location &L,
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
Obj.insert({"FileURL", FileURL});
+
+ return Obj;
}
Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -280,29 +285,39 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
Value ArrNamespace = Array();
for (const Reference& Child : I.Children.Namespaces)
ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
- NamespaceValue.insert({"Namespace", ArrNamespace});
+
+ if (!ArrNamespace.getAsArray()->empty())
+ NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
Value ArrRecord = Array();
for (const Reference& Child : I.Children.Records)
ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
- NamespaceValue.insert({"Record", ArrRecord});
+
+ if (!ArrRecord.getAsArray()->empty())
+ NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
Value ArrFunction = Array();
for (const FunctionInfo& Child : I.Children.Functions)
ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
CDCtx));
- NamespaceValue.insert({"Function", ArrRecord});
+ if (!ArrFunction.getAsArray()->empty())
+ NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
Value ArrEnum = Array();
for (const EnumInfo& Child : I.Children.Enums)
ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
- NamespaceValue.insert({"Enums", ArrEnum });
+
+ if (!ArrEnum.getAsArray()->empty())
+ NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
Value ArrTypedefs = Array();
for (const TypedefInfo& Child : I.Children.Typedefs)
ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
- NamespaceValue.insert({"Typedefs", ArrTypedefs });
+ if (!ArrTypedefs.getAsArray()->empty())
+ NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+
+ return NamespaceValue;
}
@@ -313,6 +328,7 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+ llvm::outs() << V << "\n";
OS << NamespaceTemplate->render(V);
break;
}
@@ -332,10 +348,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-
+ return llvm::Error::success();
}
-const char *MustacheHTMLGenerator::Format = "mustache";
+const char *MustacheHTMLGenerator::Format = "mhtml";
static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -343,7 +359,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
// This anchor is used to force the linker to link in the generated object
// file and thus register the generator.
-volatile int HTMLGeneratorAnchorSource = 0;
+volatile int MHTMLGeneratorAnchorSource = 0;
} // namespace doc
} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index af4c60182ae52c..1d3407f8b52920 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
See https://llvm.org/LICENSE.txt for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- This file defines the template
+ This file defines the template for generating Namespaces
}}
<!DOCTYPE html>
<html lang="en-US">
@@ -11,13 +11,42 @@
<meta charset="utf-8"/>
<title>{{NamespaceTitle}}</title>
</head>
+ <h1>{{NamespaceTitle}}</h1>
{{#NamespaceComments}}
- <p>Namespace Comment Present!</p>
+ <p>Namespace Comment</p>
{{/NamespaceComments}}
{{#Namespace}}
- <p>Namespace Present!</p>
+ <h2 id="Namespace">Namespace</h2>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
{{/Namespace}}
{{#Record}}
- <p>Record Present!</p>
+ <h2 id="Class">Class</h2>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
{{/Record}}
+ {{#Function}}
+ <h2 id="Function">Function</h2>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Function}}
+ {{#Enums}}
+ <h2 id="Enums">Enums</h2>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Enums}}
</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index fc5a9529111b16..eee8dbf093cc53 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,6 +103,7 @@ enum OutputFormatTy {
md,
yaml,
html,
+ mhtml
};
static llvm::cl::opt<OutputFormatTy>
@@ -112,7 +113,9 @@ static llvm::cl::opt<OutputFormatTy>
clEnumValN(OutputFormatTy::md, "md",
"Documentation in MD format."),
clEnumValN(OutputFormatTy::html, "html",
- "Documentation in HTML format.")),
+ "Documentation in HTML format."),
+ clEnumValN(OutputFormatTy::mhtml, "mhtml",
+ "Documentation in mHTML format")),
llvm::cl::init(OutputFormatTy::yaml),
llvm::cl::cat(ClangDocCategory));
@@ -124,6 +127,8 @@ std::string getFormatString() {
return "md";
case OutputFormatTy::html:
return "html";
+ case OutputFormatTy::mhtml:
+ return "mhtml";
}
llvm_unreachable("Unknown OutputFormatTy");
}
@@ -227,6 +232,8 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
llvm::sys::path::native(AssetsPath, MustacheTemplate);
llvm::sys::path::append(MustacheTemplate, "template.mustache");
CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+
+ return llvm::Error::success();
}
/// Make the output of clang-doc deterministic by sorting the children of
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index e5c5d4c9e4412b..5354032f4d8323 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,5 +26,3 @@ class Shape {
*/
virtual double perimeter() const = 0;
};
-
-
>From 4e5109af97bf06357bf7a9de762106bf93b2852b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 11 Oct 2024 17:58:48 -0400
Subject: [PATCH 59/62] [clang-doc] add class template
---
clang-tools-extra/clang-doc/CMakeLists.txt | 1 +
.../clang-doc/HTMLMustacheGenerator.cpp | 292 ++++++++++++++----
.../clang-doc/assets/clang-doc-mustache.css | 63 ++--
.../assets/namespace-template.mustache | 42 +--
.../clang-doc/tool/CMakeLists.txt | 8 +-
.../clang-doc/tool/ClangDocMain.cpp | 53 +++-
6 files changed, 305 insertions(+), 154 deletions(-)
diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 82aded940222db..288a85fea9d3ba 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -16,6 +16,7 @@ add_clang_library(clangDoc
Serialize.cpp
YAMLGenerator.cpp
HTMLMustacheGenerator.cpp
+ FileHelpersClangDoc.cpp
DEPENDS
omp_gen
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index bfa563ebdc7b37..1f96202f1f6de6 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Generators.h"
#include "Representation.h"
+#include "FileHelpersClangDoc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mustache.h"
@@ -48,9 +49,8 @@ class MustacheTemplateFile : public Template {
Error registerPartialFile(StringRef Name, StringRef FileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
MemoryBuffer::getFile(FileName);
- if (auto EC = BufferOrError.getError()) {
+ if (auto EC = BufferOrError.getError())
return llvm::createFileError("cannot open file", EC);
- }
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
llvm::StringRef FileContent = Buffer->getBuffer();
registerPartial(Name, FileContent);
@@ -65,24 +65,54 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
-llvm::Error
-setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
- auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
- auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
- if (auto EC = Template.getError()) {
+llvm::Error setupTemplate(
+ std::unique_ptr<MustacheTemplateFile> &Template,
+ StringRef TemplatePath,
+ std::vector<std::pair<StringRef, StringRef>> Partials) {
+ auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
+ if (auto EC = T.getError())
return llvm::createFileError("cannot open file", EC);
+ Template = std::move(T.get());
+ for (const auto &P : Partials) {
+ auto Err = Template->registerPartialFile(P.first, P.second);
+ if (Err)
+ return Err;
}
- NamespaceTemplate = std::move(Template.get());
return llvm::Error::success();
}
+llvm::Error
+setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
+ auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
+ auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
+ auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
+ auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
+ auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+ std::vector<std::pair<StringRef, StringRef>> Partials = {
+ {"Comments", CommentFilePath},
+ {"FunctionPartial", FunctionFilePath},
+ {"EnumPartial", EnumFilePath}
+ };
+
+ auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
+ if (Err)
+ return Err;
+
+ Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
+
+ if (Err)
+ return Err;
+
+ return llvm::Error::success();
+}
+
+
llvm::Error
MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
const clang::doc::ClangDocContext &CDCtx) {
- if (auto Err = setupTemplateFiles(CDCtx)) {
+ if (auto Err = setupTemplateFiles(CDCtx))
return Err;
- }
// Track which directories we already tried to create.
llvm::StringSet<> CreatedDirs;
// Collect all output by file name and create the necessary directories.
@@ -95,10 +125,9 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
if (!CreatedDirs.contains(Path)) {
if (std::error_code Err = llvm::sys::fs::create_directories(Path);
- Err != std::error_code()) {
+ Err != std::error_code())
return llvm::createStringError(Err, "Failed to create directory '%s'.",
Path.c_str());
- }
CreatedDirs.insert(Path);
}
@@ -110,14 +139,13 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
std::error_code FileErr;
llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
llvm::sys::fs::OF_None);
- if (FileErr) {
+ if (FileErr)
return llvm::createStringError(FileErr, "Error opening file '%s'",
Group.getKey().str().c_str());
- }
+
for (const auto &Info : Group.getValue()) {
- if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+ if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
return Err;
- }
}
}
return llvm::Error::success();
@@ -126,12 +154,15 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
Value extractValue(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
Object Obj = Object();
+ Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"Filename", L.Filename});
+
if (!L.IsFileInRootDir || !RepositoryUrl) {
- Obj.insert({"LineNumber", L.LineNumber});
- Obj.insert({"Filename", L.Filename});
+ return Obj;
}
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+ FileURL += "#" + std::to_string(L.LineNumber);
Obj.insert({"FileURL", FileURL});
return Obj;
@@ -144,6 +175,8 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
Object Obj = Object();
Obj.insert({"Link", Path});
Obj.insert({"Name", I.Name});
+ Obj.insert({"QualName", I.QualName});
+ Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
return Obj;
}
@@ -194,8 +227,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
Value ParamArr = Array();
- for (const auto &P : I.Params) {
- ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+ for (const auto Val : llvm::enumerate(I.Params)) {
+ Value V = Object();
+ V.getAsObject()->insert({"Name", Val.value().Name});
+ V.getAsObject()->insert({"Type", Val.value().Type.Name});
+ V.getAsObject()->insert({"End", Val.index() + 1 == I.Params.size()});
+ ParamArr.getAsArray()->emplace_back(V);
}
Obj.insert({"Params", ParamArr});
@@ -205,12 +242,13 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
Obj.insert({"FunctionComments", ArrDesc});
}
- if (I.DefLoc) {
- if (!CDCtx.RepositoryUrl)
- Obj.insert({"Location", extractValue(*I.DefLoc)});
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ Obj.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
else
- Obj.insert({"Location", extractValue(*I.DefLoc,
- StringRef{*CDCtx.RepositoryUrl})});
+ Obj.insert({"Location", extractValue(L)});
}
return Obj;
}
@@ -225,7 +263,6 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumName", EnumType});
Obj.insert({"HasComment", HasComment});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
-
Value Arr = Array();
for (const EnumValueInfo& M: I.Members) {
Value EnumValue = Object();
@@ -252,75 +289,175 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
Obj.insert({"EnumComments", ArrDesc});
}
- if (I.DefLoc) {
- if (!CDCtx.RepositoryUrl)
- Obj.insert({"Location", extractValue(*I.DefLoc)});
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ Obj.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
else
- Obj.insert({"Location", extractValue(*I.DefLoc,
- StringRef{*CDCtx.RepositoryUrl})});
+ Obj.insert({"Location", extractValue(L)});
}
return Obj;
}
-Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
- Object NamespaceValue = Object();
- std::string InfoTitle;
- if (I.Name.str() == "")
- InfoTitle = "Global Namespace";
- else
- InfoTitle = ("namespace " + I.Name).str();
-
- StringRef BasePath = I.getRelativeFilePath("");
- NamespaceValue.insert({"NamespaceTitle", InfoTitle});
- NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
-
- if (!I.Description.empty()) {
- Value ArrDesc = Array();
- for (const CommentInfo& Child : I.Description)
- ArrDesc.getAsArray()->emplace_back(extractValue(Child));
- NamespaceValue.insert({"NamespaceComments", ArrDesc });
- }
-
+void extractScopeChildren(const ScopeChildren &S, Object &Obj,
+ StringRef ParentInfoDir,
+ const ClangDocContext &CDCtx) {
Value ArrNamespace = Array();
- for (const Reference& Child : I.Children.Namespaces)
- ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ for (const Reference& Child : S.Namespaces)
+ ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
if (!ArrNamespace.getAsArray()->empty())
- NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+ Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
Value ArrRecord = Array();
- for (const Reference& Child : I.Children.Records)
- ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+ for (const Reference& Child : S.Records)
+ ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
if (!ArrRecord.getAsArray()->empty())
- NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
+ Obj.insert({"Record", Object{{"Links", ArrRecord}}});
Value ArrFunction = Array();
- for (const FunctionInfo& Child : I.Children.Functions)
- ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
- CDCtx));
+ Value PublicFunction = Array();
+ Value ProtectedFunction = Array();
+ Value PrivateFunction = Array();
+
+ for (const FunctionInfo& Child : S.Functions) {
+ Value F = extractValue(Child, ParentInfoDir, CDCtx);
+ AccessSpecifier Access = Child.Access;
+ if (Access == AccessSpecifier::AS_public)
+ PublicFunction.getAsArray()->emplace_back(F);
+ else if (Access == AccessSpecifier::AS_protected)
+ ProtectedFunction.getAsArray()->emplace_back(F);
+ else
+ ArrFunction.getAsArray()->emplace_back(F);
+ }
if (!ArrFunction.getAsArray()->empty())
- NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
+ Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+
+ if (!PublicFunction.getAsArray()->empty())
+ Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+
+ if (!ProtectedFunction.getAsArray()->empty())
+ Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+
Value ArrEnum = Array();
- for (const EnumInfo& Child : I.Children.Enums)
+ for (const EnumInfo& Child : S.Enums)
ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
if (!ArrEnum.getAsArray()->empty())
- NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
+ Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
Value ArrTypedefs = Array();
- for (const TypedefInfo& Child : I.Children.Typedefs)
+ for (const TypedefInfo& Child : S.Typedefs)
ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
if (!ArrTypedefs.getAsArray()->empty())
- NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+ Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+ Object NamespaceValue = Object();
+ std::string InfoTitle;
+ if (I.Name.str() == "")
+ InfoTitle = "Global Namespace";
+ else
+ InfoTitle = ("namespace " + I.Name).str();
+
+ StringRef BasePath = I.getRelativeFilePath("");
+ NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+ NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ NamespaceValue.insert({"NamespaceComments", ArrDesc });
+ }
+ extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
return NamespaceValue;
}
+Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
+ Object RecordValue = Object();
+
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ RecordValue.insert({"RecordComments", ArrDesc });
+ }
+ RecordValue.insert({"Name", I.Name});
+ RecordValue.insert({"FullName", I.FullName});
+ RecordValue.insert({"RecordType", getTagType(I.TagType)});
+
+ if (I.DefLoc.has_value()) {
+ Location L = *I.DefLoc;
+ if (CDCtx.RepositoryUrl.has_value())
+ RecordValue.insert({"Location", extractValue(L,
+ StringRef{*CDCtx.RepositoryUrl})});
+ else
+ RecordValue.insert({"Location", extractValue(L)});
+ }
+
+ StringRef BasePath = I.getRelativeFilePath("");
+ extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
+ Value PublicMembers = Array();
+ Value ProtectedMembers = Array();
+ Value PrivateMembers = Array();
+ for (const MemberTypeInfo &Member : I.Members ) {
+ Value MemberValue = Object();
+ MemberValue.getAsObject()->insert({"Name", Member.Name});
+ MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
+ if (!Member.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : Member.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
+ }
+
+ if (Member.Access == AccessSpecifier::AS_public)
+ PublicMembers.getAsArray()->emplace_back(MemberValue);
+ else if (Member.Access == AccessSpecifier::AS_protected)
+ ProtectedMembers.getAsArray()->emplace_back(MemberValue);
+ else if (Member.Access == AccessSpecifier::AS_private)
+ PrivateMembers.getAsArray()->emplace_back(MemberValue);
+ }
+ if (!PublicMembers.getAsArray()->empty())
+ RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
+ if (!ProtectedMembers.getAsArray()->empty())
+ RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
+ if (!PrivateMembers.getAsArray()->empty())
+ RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+
+ return RecordValue;
+}
+void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
+ V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+ Value StylesheetArr = Array();
+ auto InfoPath = I->getRelativeFilePath("");
+ SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+ for (const auto &FilePath : CDCtx.UserStylesheets) {
+ SmallString<128> StylesheetPath = RelativePath;
+ llvm::sys::path::append(StylesheetPath,
+ llvm::sys::path::filename(FilePath));
+ llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
+ StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+ }
+ V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+
+ Value ScriptArr = Array();
+ for (auto Script : CDCtx.JsScripts) {
+ SmallString<128> JsPath = RelativePath;
+ llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
+ ScriptArr.getAsArray()->emplace_back(JsPath);
+ }
+ V.getAsObject()->insert({"Scripts", ScriptArr});
+}
+
llvm::Error
MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -328,17 +465,27 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- llvm::outs() << V << "\n";
+ setupTemplateValue(CDCtx, V, I);
OS << NamespaceTemplate->render(V);
break;
}
- case InfoType::IT_record:
+ case InfoType::IT_record: {
+ Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
+ setupTemplateValue(CDCtx, V, I);
+ // Serialize the JSON value to the output stream in a readable format.
+ llvm::outs() << "Visit: " << I->Name << "\n";
+ //llvm::outs() << llvm::formatv("{0:2}", V) << "\n";
+ llvm::outs() << RecordTemplate->render(V);
break;
+ }
case InfoType::IT_enum:
+ llvm::outs() << "IT_enum\n";
break;
case InfoType::IT_function:
+ llvm::outs() << "IT_Function\n";
break;
case InfoType::IT_typedef:
+ llvm::outs() << "IT_typedef\n";
break;
case InfoType::IT_default:
return createStringError(llvm::inconvertibleErrorCode(),
@@ -348,6 +495,17 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
}
llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+ llvm::Error Err = llvm::Error::success();
+ for (const auto &FilePath : CDCtx.UserStylesheets) {
+ Err = copyFile(FilePath, CDCtx.OutDirectory);
+ if (Err)
+ return Err;
+ }
+ for (const auto &FilePath : CDCtx.JsScripts) {
+ Err = copyFile(FilePath, CDCtx.OutDirectory);
+ if (Err)
+ return Err;
+ }
return llvm::Error::success();
}
@@ -355,7 +513,7 @@ const char *MustacheHTMLGenerator::Format = "mhtml";
static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
- "Generator for mustache HTML output.");
+ "Generator for mustache HTML output.");
// This anchor is used to force the linker to link in the generated object
// file and thus register the generator.
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
index 2f07baa7ca0a1a..a885a36cb4a3dc 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -1,3 +1,4 @@
+/* css for clang-doc mustache backend */
@import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap";
*,*::before *::after {
@@ -18,6 +19,7 @@ video {
display:block;
max-width:100%
}
+
* {
--brand-light:#ce6300;
--text1-light:#000000;
@@ -30,6 +32,7 @@ video {
--surface1-dark:#161212;
--surface2-dark:#272424
}
+
:root {
color-scheme:light;
--brand:var(--brand-light);
@@ -40,6 +43,7 @@ video {
--surface1:var(--surface1-light);
--surface2:var(--surface2-light)
}
+
@media(prefers-color-scheme:dark) {
:root {
color-scheme:dark;
@@ -52,6 +56,7 @@ video {
--surface2:var(--surface2-dark)
}
}
+
[color-scheme=light] {
color-scheme:light;
--brand:var(--brand-light);
@@ -62,6 +67,7 @@ video {
--surface1:var(--surface1-light);
--surface2:var(--surface2-light)
}
+
[color-scheme=dark] {
color-scheme:dark;
--brand:var(--brand-dark);
@@ -72,6 +78,7 @@ video {
--surface1:var(--surface1-dark);
--surface2:var(--surface2-dark)
}
+
html {
background-color:var(--surface1)
}
@@ -200,6 +207,7 @@ body, html {
.navbar__close:hover {
color:var(--text1)
}
+
@media(min-width:769px) {
.navbar__close {
display:none
@@ -212,22 +220,27 @@ body, html {
margin:0;
padding:0
}
+
@media(max-width:768px) {
.navbar__links {
flex-direction:column
}
}
+
.navbar__item {
list-style-type:none
}
+
.navbar__link {
color:var(--text2);
text-decoration:none;
padding:.5rem
}
+
.navbar__link:hover {
color:var(--text1)
}
+
.navbar__theme-toggle-button {
background:0 0;
color:var(--text2);
@@ -237,6 +250,7 @@ body, html {
width:2.5rem;
height:2.5rem
}
+
.navbar__theme-toggle-button:hover {
color:var(--text1)
}
@@ -248,42 +262,51 @@ body, html {
align-items:center;
gap:2rem
}
+
.hero__title {
font-size:2.5rem;
margin-bottom:.5rem
}
+
.hero__title-large {
font-size:3rem
}
+
@media(max-width:768px) {
.hero__title-large {
font-size:2.5rem
}
}
+
@media(max-width:480px) {
.hero__title-large {
font-size:2rem
}
}
+
@media(max-width:768px) {
.hero__title {
font-size:2rem
}
}
+
@media(max-width:480px) {
.hero__title {
font-size:1.75rem
}
}
+
.hero__subtitle {
font-size:1.25rem;
font-weight:500
}
+
@media(max-width:768px) {
.hero__subtitle {
font-size:1rem
}
}
+
@media(max-width:480px) {
.hero__subtitle {
font-size:.875rem
@@ -299,69 +322,42 @@ body, html {
padding:1rem 2rem
}
-
@media(max-width:768px) {
.section-container {
padding:1rem
}
}
+
.section-container h2 {
font-size:1.5rem;
margin-bottom:1rem;
color:var(--brand);
border-bottom: 1px solid var(--text2);
}
+
@media(max-width:768px) {
.section-container h2 {
font-size:1.25rem
}
}
+
.section-container p {
font-size:1rem;
line-height:1.5
}
+
@media(max-width:768px) {
.section-container p {
font-size:.875rem
}
}
+
.home__row {
display:grid;
grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
gap:2rem
}
-
-.links-wrapper {
- display:grid;
- gap:1rem;
- grid-template-columns:1fr 1fr 1fr 1fr
-}
- at media(max-width:768px) {
- .links-wrapper {
- grid-template-columns:1fr 1fr
- }
-}
- at media(max-width:480px) {
- .links-wrapper {
- grid-template-columns:1fr
- }
-}
-.links-wrapper .link {
- display:flex;
- flex-direction:column;
- align-items:center;
- padding:1rem;
- border:1px solid var(--text1);
- border-radius:.25rem
-}
-.links-wrapper .link__icon {
- font-size:2rem
-}
-.links-wrapper .link__title {
- font-size:1rem
-}
-
.table-wrapper {
display:flex;
flex-direction:column;
@@ -375,7 +371,6 @@ body, html {
text-align: left;
}
-
.block-command-command {
font-weight: bold;
}
@@ -442,7 +437,6 @@ body, html {
color: var(--brand)
}
-
/* Content */
.content {
background-color: var(--text1-inverse);
@@ -452,6 +446,7 @@ body, html {
width: calc(100% - 250px);
height: 100vh;
}
+
.sidebar-item {
color: var(--text1);
}
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
index 4061fd026886e4..21cbaa7ec5cf3f 100644
--- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -35,47 +35,7 @@
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.
- 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.
- 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.
- 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.
- 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.
- 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.
+ anim id est laborum
</div>
<div class="resizer" id="resizer"></div>
<div class="content">
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 8eb067dbe6de87..eccbc99a7ecc4b 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,7 +21,13 @@ target_link_libraries(clang-doc
set(assets
index.js
- template.mustache
+ mustache-index.js
+ class-template.mustache
+ comments-template.mustache
+ enum-template.mustache
+ function-template.mustache
+ namespace-template.mustache
+ clang-doc-mustache.css
clang-doc-default-stylesheet.css
)
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index eee8dbf093cc53..256e29c3c9f894 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -163,6 +163,15 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
return llvm::Error::success();
}
+llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path,
+ llvm::StringRef Asset) {
+ llvm::SmallString<128> Default;
+ llvm::sys::path::native(Path, Default);
+ llvm::sys::path::append(Default, Asset);
+ return Default;
+}
+
+
llvm::Error getDefaultAssetFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -173,13 +182,10 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
llvm::SmallString<128> AssetsPath;
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> DefaultStylesheet;
- llvm::sys::path::native(AssetsPath, DefaultStylesheet);
- llvm::sys::path::append(DefaultStylesheet,
- "clang-doc-default-stylesheet.css");
- llvm::SmallString<128> IndexJS;
- llvm::sys::path::native(AssetsPath, IndexJS);
- llvm::sys::path::append(IndexJS, "index.js");
+ llvm::SmallString<128> DefaultStylesheet =
+ appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
+ llvm::SmallString<128> IndexJS =
+ appendPathNative(AssetsPath, "index.js");
if (!llvm::sys::fs::is_regular_file(IndexJS))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -210,6 +216,7 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
return getDefaultAssetFiles(Argv0, CDCtx);
}
+
llvm::Error getMustacheHtmlFiles(const char *Argv0,
clang::doc::ClangDocContext &CDCtx) {
if (!UserAssetPath.empty() &&
@@ -228,10 +235,34 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
- llvm::SmallString<128> MustacheTemplate;
- llvm::sys::path::native(AssetsPath, MustacheTemplate);
- llvm::sys::path::append(MustacheTemplate, "template.mustache");
- CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+ llvm::SmallString<128> DefaultStylesheet
+ = appendPathNative(AssetsPath, "clang-doc-mustache.css");
+ llvm::SmallString<128> NamespaceTemplate
+ = appendPathNative(AssetsPath, "namespace-template.mustache");
+ llvm::SmallString<128> ClassTemplate
+ = appendPathNative(AssetsPath, "class-template.mustache");
+ llvm::SmallString<128> EnumTemplate
+ = appendPathNative(AssetsPath, "enum-template.mustache");
+ llvm::SmallString<128> FunctionTemplate
+ = appendPathNative(AssetsPath, "function-template.mustache");
+ llvm::SmallString<128> CommentTemplate
+ = appendPathNative(AssetsPath, "comments-template.mustache");
+ llvm::SmallString<128> IndexJS
+ = appendPathNative(AssetsPath, "mustache-index.js");
+
+ CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+ CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+ std::string(DefaultStylesheet));
+ CDCtx.MustacheTemplates.insert({"namespace-template",
+ NamespaceTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"class-template",
+ ClassTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"enum-template",
+ EnumTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"function-template",
+ FunctionTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"comments-template",
+ CommentTemplate.c_str()});
return llvm::Error::success();
}
>From 42d29951ddd29259be61492aaa9d5afa82d08af3 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 31 Oct 2024 05:07:02 -0400
Subject: [PATCH 60/62] clang-doc improve mustache output
---
clang-tools-extra/clang-doc/BitcodeReader.cpp | 30 ++-
clang-tools-extra/clang-doc/BitcodeWriter.cpp | 27 ++-
clang-tools-extra/clang-doc/BitcodeWriter.h | 4 +
.../clang-doc/HTMLMustacheGenerator.cpp | 185 +++++++++++++--
.../clang-doc/Representation.cpp | 2 +
clang-tools-extra/clang-doc/Representation.h | 16 +-
clang-tools-extra/clang-doc/Serialize.cpp | 216 ++++++++++++++++--
.../clang-doc/assets/clang-doc-mustache.css | 91 +++++---
.../clang-doc/assets/class-template.mustache | 67 ++----
.../clang-doc/assets/enum-template.mustache | 4 +-
.../assets/function-template.mustache | 2 +-
.../clang-doc/assets/mustache-index.js | 195 +++++++++++++++-
.../assets/namespace-template.mustache | 75 ++++--
.../clang-doc/tool/ClangDocMain.cpp | 10 +
14 files changed, 770 insertions(+), 154 deletions(-)
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 1f2fb0a8b2b855..4d639937a59152 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -169,6 +169,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
return decodeRecord(R, I->USR, Blob);
case RECORD_NAME:
return decodeRecord(R, I->Name, Blob);
+ case RECORD_FULLNAME:
+ return decodeRecord(R, I->FullName, Blob);
case RECORD_PATH:
return decodeRecord(R, I->Path, Blob);
case RECORD_DEFLOCATION:
@@ -268,6 +270,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
return decodeRecord(R, I->Name, Blob);
case FUNCTION_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
+ case FUNCTION_PROTOTYPE:
+ return decodeRecord(R, I->ProtoType, Blob);
case FUNCTION_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case FUNCTION_ACCESS:
@@ -282,7 +286,15 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
TypeInfo *I) {
- return llvm::Error::success();
+ switch (ID) {
+ case TYPE_IS_BUILTIN:
+ return decodeRecord(R, I->IsBuiltIn, Blob);
+ case TYPE_IS_TEMPLATE:
+ return decodeRecord(R, I->IsTemplate, Blob);
+ default:
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid field for TypeInfo");
+ }
}
llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
@@ -468,6 +480,22 @@ template <> llvm::Error addTypeInfo(TypedefInfo *I, TypeInfo &&T) {
return llvm::Error::success();
}
+template <> llvm::Error addTypeInfo(FieldTypeInfo *I, TypeInfo &&T) {
+ I->Type = std::move(T.Type);
+ I->IsTemplate = T.IsTemplate;
+ I->IsBuiltIn = T.IsBuiltIn;
+ return llvm::Error::success();
+}
+
+template <> llvm::Error addTypeInfo(MemberTypeInfo *I, FieldTypeInfo &&T) {
+ I->Type = std::move(T.Type);
+ I->IsTemplate = T.IsTemplate;
+ I->IsBuiltIn = T.IsBuiltIn;
+ I->Name = std::move(T.Name);
+ I->DefaultValue = std::move(T.DefaultValue);
+ return llvm::Error::success();
+}
+
template <typename T> llvm::Error addReference(T I, Reference &&R, FieldId F) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 06f30f76e33d8c..4b8f67302ce9d9 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -154,6 +154,8 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{COMMENT_ARG, {"Arg", &StringAbbrev}},
{FIELD_TYPE_NAME, {"Name", &StringAbbrev}},
{FIELD_DEFAULT_VALUE, {"DefaultValue", &StringAbbrev}},
+ {TYPE_IS_BUILTIN, {"IsBuiltIn", &BoolAbbrev}},
+ {TYPE_IS_TEMPLATE, {"IsTemplate", &BoolAbbrev}},
{MEMBER_TYPE_NAME, {"Name", &StringAbbrev}},
{MEMBER_TYPE_ACCESS, {"Access", &IntAbbrev}},
{NAMESPACE_USR, {"USR", &SymbolIDAbbrev}},
@@ -169,6 +171,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{ENUM_VALUE_EXPR, {"Expr", &StringAbbrev}},
{RECORD_USR, {"USR", &SymbolIDAbbrev}},
{RECORD_NAME, {"Name", &StringAbbrev}},
+ {RECORD_FULLNAME, {"FullName", &StringAbbrev}},
{RECORD_PATH, {"Path", &StringAbbrev}},
{RECORD_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
{RECORD_LOCATION, {"Location", &LocationAbbrev}},
@@ -187,6 +190,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{FUNCTION_LOCATION, {"Location", &LocationAbbrev}},
{FUNCTION_ACCESS, {"Access", &IntAbbrev}},
{FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}},
+ {FUNCTION_PROTOTYPE, {"ProtoType", &StringAbbrev}},
{REFERENCE_USR, {"USR", &SymbolIDAbbrev}},
{REFERENCE_NAME, {"Name", &StringAbbrev}},
{REFERENCE_QUAL_NAME, {"QualName", &StringAbbrev}},
@@ -218,7 +222,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
COMMENT_PARAMNAME, COMMENT_CLOSENAME, COMMENT_SELFCLOSING,
COMMENT_EXPLICIT, COMMENT_ATTRKEY, COMMENT_ATTRVAL, COMMENT_ARG}},
// Type Block
- {BI_TYPE_BLOCK_ID, {}},
+ {BI_TYPE_BLOCK_ID, {TYPE_IS_BUILTIN, TYPE_IS_TEMPLATE}},
// FieldType Block
{BI_FIELD_TYPE_BLOCK_ID, {FIELD_TYPE_NAME, FIELD_DEFAULT_VALUE}},
// MemberType Block
@@ -237,7 +241,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
{NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
// Record Block
{BI_RECORD_BLOCK_ID,
- {RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION,
+ {RECORD_USR, RECORD_NAME, RECORD_FULLNAME, RECORD_PATH, RECORD_DEFLOCATION,
RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}},
// BaseRecord Block
{BI_BASE_RECORD_BLOCK_ID,
@@ -246,11 +250,11 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
BASE_RECORD_IS_PARENT}},
// Function Block
{BI_FUNCTION_BLOCK_ID,
- {FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION,
- FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
+ {FUNCTION_USR, FUNCTION_PROTOTYPE, FUNCTION_NAME, FUNCTION_DEFLOCATION,
+ FUNCTION_LOCATION, FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
// Reference Block
{BI_REFERENCE_BLOCK_ID,
- {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE,
+ {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE,
REFERENCE_PATH, REFERENCE_FIELD}},
// Template Blocks.
{BI_TEMPLATE_BLOCK_ID, {}},
@@ -425,6 +429,11 @@ void ClangDocBitcodeWriter::emitBlockInfo(BlockId BID,
void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
if (R.USR == EmptySID && R.Name.empty())
return;
+
+ if (R.Name == "const Shape &") {
+ llvm::outs() << "const Shape & USR: " <<
+ llvm::toHex(llvm::toStringRef(R.USR)) << "\n";
+ }
StreamSubBlockGuard Block(Stream, BI_REFERENCE_BLOCK_ID);
emitRecord(R.USR, REFERENCE_USR);
emitRecord(R.Name, REFERENCE_NAME);
@@ -437,6 +446,8 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
void ClangDocBitcodeWriter::emitBlock(const TypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_TYPE_BLOCK_ID);
emitBlock(T.Type, FieldId::F_type);
+ emitRecord(T.IsBuiltIn, TYPE_IS_BUILTIN);
+ emitRecord(T.IsTemplate, TYPE_IS_TEMPLATE);
}
void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
@@ -455,14 +466,14 @@ void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
void ClangDocBitcodeWriter::emitBlock(const FieldTypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_FIELD_TYPE_BLOCK_ID);
- emitBlock(T.Type, FieldId::F_type);
+ emitBlock(static_cast<TypeInfo>(T));
emitRecord(T.Name, FIELD_TYPE_NAME);
emitRecord(T.DefaultValue, FIELD_DEFAULT_VALUE);
}
void ClangDocBitcodeWriter::emitBlock(const MemberTypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_MEMBER_TYPE_BLOCK_ID);
- emitBlock(T.Type, FieldId::F_type);
+ emitBlock(static_cast<FieldTypeInfo>(T));
emitRecord(T.Name, MEMBER_TYPE_NAME);
emitRecord(T.Access, MEMBER_TYPE_ACCESS);
for (const auto &CI : T.Description)
@@ -544,6 +555,7 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
StreamSubBlockGuard Block(Stream, BI_RECORD_BLOCK_ID);
emitRecord(I.USR, RECORD_USR);
emitRecord(I.Name, RECORD_NAME);
+ emitRecord(I.FullName, RECORD_FULLNAME);
emitRecord(I.Path, RECORD_PATH);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
@@ -593,6 +605,7 @@ void ClangDocBitcodeWriter::emitBlock(const BaseRecordInfo &I) {
void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) {
StreamSubBlockGuard Block(Stream, BI_FUNCTION_BLOCK_ID);
emitRecord(I.USR, FUNCTION_USR);
+ emitRecord(I.ProtoType, FUNCTION_PROTOTYPE);
emitRecord(I.Name, FUNCTION_NAME);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 9a572e40e352f1..117527cf89c969 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -77,6 +77,7 @@ enum RecordId {
VERSION = 1,
FUNCTION_USR,
FUNCTION_NAME,
+ FUNCTION_PROTOTYPE,
FUNCTION_DEFLOCATION,
FUNCTION_LOCATION,
FUNCTION_ACCESS,
@@ -94,6 +95,8 @@ enum RecordId {
COMMENT_ARG,
FIELD_TYPE_NAME,
FIELD_DEFAULT_VALUE,
+ TYPE_IS_BUILTIN,
+ TYPE_IS_TEMPLATE,
MEMBER_TYPE_NAME,
MEMBER_TYPE_ACCESS,
NAMESPACE_USR,
@@ -109,6 +112,7 @@ enum RecordId {
ENUM_VALUE_EXPR,
RECORD_USR,
RECORD_NAME,
+ RECORD_FULLNAME,
RECORD_PATH,
RECORD_DEFLOCATION,
RECORD_LOCATION,
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 1f96202f1f6de6..d7079497c25096 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -10,6 +10,7 @@
#include "FileHelpersClangDoc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mustache.h"
+#include "llvm/ADT/SmallSet.h"
using namespace llvm;
using namespace llvm::json;
@@ -18,6 +19,44 @@ using namespace llvm::mustache;
namespace clang {
namespace doc {
+static StringMap<llvm::SmallString<16>> Index;
+
+void generateIndex(Info *I) {
+ switch (I->IT) {
+ case InfoType::IT_namespace: {
+ const NamespaceInfo& N = *static_cast<clang::doc::NamespaceInfo *>(I);
+ SmallString<16> Name = N.getFileBaseName();
+ std::string HexId = llvm::toHex(llvm::toStringRef(N.USR));
+ Index[HexId] = HexId;
+ for (const EnumInfo& E : N.Children.Enums)
+ Index[llvm::toHex(llvm::toStringRef(E.USR))] = HexId;
+ for (const TypedefInfo& T : N.Children.Typedefs)
+ Index[llvm::toHex(llvm::toStringRef(T.USR))] = HexId;
+ break;
+ }
+ case InfoType::IT_record: {
+ const RecordInfo& R = *static_cast<clang::doc::RecordInfo *>(I);
+ SmallString<16> Name = R.getFileBaseName();
+ std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
+ Index[HexId] = HexId;
+ for (const EnumInfo& E : R.Children.Enums) {
+ Index[llvm::toHex(llvm::toStringRef(E.USR))] = HexId;
+ }
+ for (const TypedefInfo& T : R.Children.Typedefs) {
+ Index[llvm::toHex(llvm::toStringRef(T.USR))] = HexId;
+ }
+ break;
+ }
+ case InfoType::IT_enum:
+ break;
+ case InfoType::IT_function:
+ break;
+ case InfoType::IT_typedef:
+ break;
+ case InfoType::IT_default:
+ return;
+ }
+}
class MustacheHTMLGenerator : public Generator {
public:
@@ -119,19 +158,11 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
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(""));
- if (!CreatedDirs.contains(Path)) {
- 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");
+ std::string InfoFile = llvm::toHex(llvm::toStringRef(Info->USR)) + ".html";
+ llvm::sys::path::append(Path, InfoFile);
+ generateIndex(Info);
FileToInfos[Path].push_back(Info);
}
@@ -157,9 +188,9 @@ Value extractValue(const Location &L,
Obj.insert({"LineNumber", L.LineNumber});
Obj.insert({"Filename", L.Filename});
- if (!L.IsFileInRootDir || !RepositoryUrl) {
+ if (!L.IsFileInRootDir || !RepositoryUrl)
return Obj;
- }
+
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
FileURL += "#" + std::to_string(L.LineNumber);
@@ -218,14 +249,124 @@ Value extractValue(const CommentInfo &I) {
return Obj;
}
+
+// Function to replace a substring within a SmallString with another SmallString
+void replaceSubstring(llvm::SmallString<256> &Input,
+ const llvm::SmallString<16> &From,
+ const llvm::SmallString<16> &To) {
+ llvm::StringRef InputStr = Input;
+ llvm::SmallString<16> FromStr;
+ raw_svector_ostream Stream(FromStr);
+ printHTMLEscaped(From, Stream);
+ llvm::StringRef ToStr = To;
+
+ // Find the first occurrence of 'from' in 'input'
+ size_t Pos = InputStr.find(FromStr);
+ while (Pos != llvm::StringRef::npos) {
+ // Create a new SmallString to hold the modified string
+ llvm::SmallString<256> NewString;
+
+ // Append the part before the found substring
+ NewString.append(Input.begin(), Input.begin() + Pos);
+
+ // Append the replacement substring
+ NewString.append(ToStr.begin(), ToStr.end());
+
+ // Append the part after the found substring
+ NewString.append(Input.begin() + Pos + FromStr.size(), Input.end());
+
+ // Update 'input' with the modified string
+ Input = NewString;
+
+ // Update the input string and find the next occurrence
+ InputStr = Input;
+ Pos = InputStr.find(FromStr, Pos + ToStr.size());
+ }
+}
+
+SmallString<64> extractLink(const Reference& R) {
+ std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
+ SmallString<16> Name = Index[HexId];
+ SmallString<64> Link({Name, ".html"});
+ return Link;
+}
+
+Value extractRecordPrototype(const SmallString<16> Prototype,
+ llvm::SmallVector<Reference, 4> Parents,
+ llvm::SmallVector<Reference, 4> VirtualParents)
+{
+ SmallString<256> Result;
+ raw_svector_ostream Stream(Result);
+ printHTMLEscaped(Prototype, Stream);
+
+ for (Reference& R : Parents)
+ {
+ SmallString<16> ParentLink({
+ "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+ " href=\"",extractLink(R),"\">",
+ R.Name,
+ "</a>"
+ });
+ replaceSubstring(Result, R.Name, ParentLink);
+ }
+
+ for (Reference& V : VirtualParents)
+ {
+ SmallString<16> ParentLink({
+ "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+ " href=\"",extractLink(V),"\">",
+ V.Name,
+ "</a>"
+ });
+ replaceSubstring(Result, V.Name, ParentLink);
+ }
+
+ return Result;
+}
+
+// extract the prototype and adds anchor tags to it
+Value extractFunctionPrototype(const SmallString<256>& Prototype,
+ const llvm::SmallVector<FieldTypeInfo, 4>& Params,
+ const TypeInfo& ReturnType) {
+ SmallString<256> Result;
+ raw_svector_ostream Stream(Result);
+ printHTMLEscaped(Prototype, Stream);
+
+ if (!ReturnType.IsBuiltIn && !ReturnType.IsTemplate) {
+ replaceSubstring(Result, ReturnType.Type.Name, {
+ "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+ " href=\"",extractLink(ReturnType.Type),"\">",
+ ReturnType.Type.Name,
+ "</a>"
+ });
+ }
+ SmallSet<StringRef, 16> ParamNames;
+ for (const FieldTypeInfo& F : Params)
+ {
+ if (ParamNames.count(F.Type.Name) > 0 || F.IsBuiltIn || F.IsTemplate)
+ continue;
+
+ ParamNames.insert(F.Type.Name);
+ SmallString<16> ParamLink({
+ "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+ " href=\"",extractLink(F.Type),"\">",
+ F.Type.Name,
+ "</a>"
+ });
+ replaceSubstring(Result, F.Type.Name, ParamLink);
+ }
+ return Result;
+}
+
Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
const ClangDocContext &CDCtx) {
Object Obj = Object();
Obj.insert({"Name", I.Name});
+ Obj.insert({"FunctionPrototype",
+ extractFunctionPrototype(I.ProtoType, I.Params, I.ReturnType)});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
Obj.insert({"Access", getAccessSpelling(I.Access).str()});
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
-
Value ParamArr = Array();
for (const auto Val : llvm::enumerate(I.Params)) {
Value V = Object();
@@ -390,14 +531,17 @@ Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
RecordValue.insert({"RecordComments", ArrDesc });
}
RecordValue.insert({"Name", I.Name});
- RecordValue.insert({"FullName", I.FullName});
+ RecordValue.insert({"FullName", extractRecordPrototype(I.FullName,
+ I.Parents,
+ I.VirtualParents)});
+
RecordValue.insert({"RecordType", getTagType(I.TagType)});
if (I.DefLoc.has_value()) {
Location L = *I.DefLoc;
if (CDCtx.RepositoryUrl.has_value())
RecordValue.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
+ StringRef{*CDCtx.RepositoryUrl})});
else
RecordValue.insert({"Location", extractValue(L)});
}
@@ -440,7 +584,7 @@ void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
Value StylesheetArr = Array();
auto InfoPath = I->getRelativeFilePath("");
SmallString<128> RelativePath = computeRelativePath("", InfoPath);
- for (const auto &FilePath : CDCtx.UserStylesheets) {
+ for (const auto &FilePath : CDCtx.UserStylesheets) {
SmallString<128> StylesheetPath = RelativePath;
llvm::sys::path::append(StylesheetPath,
llvm::sys::path::filename(FilePath));
@@ -457,7 +601,6 @@ void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
}
V.getAsObject()->insert({"Scripts", ScriptArr});
}
-
llvm::Error
MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -466,16 +609,14 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
case InfoType::IT_namespace: {
Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
setupTemplateValue(CDCtx, V, I);
- OS << NamespaceTemplate->render(V);
+ NamespaceTemplate->render(V, OS);
break;
}
case InfoType::IT_record: {
Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
setupTemplateValue(CDCtx, V, I);
// Serialize the JSON value to the output stream in a readable format.
- llvm::outs() << "Visit: " << I->Name << "\n";
- //llvm::outs() << llvm::formatv("{0:2}", V) << "\n";
- llvm::outs() << RecordTemplate->render(V);
+ RecordTemplate->render(V, OS);
break;
}
case InfoType::IT_enum:
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 4da93b24c131fd..cb42709f467a36 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -239,6 +239,8 @@ RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)
void RecordInfo::merge(RecordInfo &&Other) {
assert(mergeable(Other));
+ if (FullName.empty())
+ FullName = std::move(Other.FullName);
if (!llvm::to_underlying(TagType))
TagType = Other.TagType;
IsTypeDef = IsTypeDef || Other.IsTypeDef;
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 3175b8209e3be3..edd9143f444fed 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -113,7 +113,8 @@ struct Reference {
llvm::SmallString<16> getFileBaseName() const;
SymbolID USR = SymbolID(); // Unique identifier for referenced decl
-
+
+
// Name of type (possibly unresolved). Not including namespaces or template
// parameters (so for a std::vector<int> this would be "vector"). See also
// QualName.
@@ -152,7 +153,9 @@ struct ScopeChildren {
// A base struct for TypeInfos
struct TypeInfo {
+
TypeInfo() = default;
+
TypeInfo(const Reference &R) : Type(R) {}
// Convenience constructor for when there is no symbol ID or info type
@@ -161,8 +164,11 @@ struct TypeInfo {
: Type(SymbolID(), Name, InfoType::IT_default, Name, Path) {}
bool operator==(const TypeInfo &Other) const { return Type == Other.Type; }
-
+
Reference Type; // Referenced type in this info.
+
+ bool IsTemplate = false;
+ bool IsBuiltIn = false;
};
// Represents one template parameter.
@@ -209,6 +215,7 @@ struct FieldTypeInfo : public TypeInfo {
return std::tie(Type, Name, DefaultValue) ==
std::tie(Other.Type, Other.Name, Other.DefaultValue);
}
+
SmallString<16> Name; // Name associated with this info.
@@ -359,6 +366,9 @@ struct FunctionInfo : public SymbolInfo {
// Full qualified name of this function, including namespaces and template
// specializations.
SmallString<16> FullName;
+
+ // Function Prototype
+ SmallString<256> ProtoType;
// When present, this function is a template or specialization.
std::optional<TemplateInfo> Template;
@@ -379,7 +389,7 @@ struct RecordInfo : public SymbolInfo {
// Full qualified name of this record, including namespaces and template
// specializations.
SmallString<16> FullName;
-
+
// When present, this record is a template or specialization.
std::optional<TemplateInfo> Template;
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index b9db78cf7d688f..560647e4e03c1d 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -32,6 +32,144 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
+void printTemplateParameters(const TemplateParameterList *TemplateParams, llvm::raw_ostream &stream) {
+ stream << "template <";
+
+ for (unsigned i = 0; i < TemplateParams->size(); ++i) {
+ if (i > 0) {
+ stream << ", ";
+ }
+
+ const NamedDecl *Param = TemplateParams->getParam(i);
+ if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Param)) {
+ if (TTP->wasDeclaredWithTypename()) {
+ stream << "typename";
+ } else {
+ stream << "class";
+ }
+
+ if (TTP->isParameterPack()) {
+ stream << "...";
+ }
+ stream << " " << TTP->getNameAsString();
+ } else if (const auto *NTTP = llvm::dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ NTTP->getType().print(stream, NTTP->getASTContext().getPrintingPolicy());
+ if (NTTP->isParameterPack()) {
+ stream << "...";
+ }
+ stream << " " << NTTP->getNameAsString();
+ } else if (const auto *TTPD = llvm::dyn_cast<TemplateTemplateParmDecl>(Param)) {
+ stream << "template <";
+ printTemplateParameters(TTPD->getTemplateParameters(), stream);
+ stream << "> class " << TTPD->getNameAsString();
+ }
+ }
+
+ stream << "> ";
+}
+
+// Extract the full function prototype from a FunctionDecl including
+// Full Decl
+llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
+ llvm::SmallString<256> Result;
+ llvm::raw_svector_ostream Stream(Result);
+ const ASTContext& Ctx = FuncDecl->getASTContext();
+ // If it's a templated function, handle the template parameters
+ if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) {
+ printTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+ }
+
+ // Print return type
+ FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy());
+
+ // Print function name
+ Stream << " " << FuncDecl->getNameAsString() << "(";
+
+ // Print parameter list with types, names, and default values
+ for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) {
+ if (I > 0) {
+ Stream << ", ";
+ }
+ const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(I);
+ QualType ParamType = ParamDecl->getType();
+ ParamType.print(Stream, Ctx.getPrintingPolicy());
+
+ // Print parameter name if it has one
+ if (!ParamDecl->getName().empty()) {
+ Stream << " " << ParamDecl->getNameAsString();
+ }
+
+ // Print default argument if it exists
+ if (ParamDecl->hasDefaultArg()) {
+ const Expr *DefaultArg = ParamDecl->getDefaultArg();
+ if (DefaultArg) {
+ Stream << " = ";
+ DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy());
+ }
+ }
+ }
+
+ // If it is a variadic function, add '...'
+ if (FuncDecl->isVariadic()) {
+ if (FuncDecl->getNumParams() > 0) {
+ Stream << ", ";
+ }
+ Stream << "...";
+ }
+
+ Stream << ")";
+
+ // If it's a const method, add 'const' qualifier
+ if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
+ if (Method->isConst()) {
+ Stream << " const";
+ }
+ }
+ return Result; // Convert SmallString to std::string for return
+}
+
+// extract full cl for record declaration
+llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
+ llvm::SmallString<16> ReturnVal;
+ LangOptions LangOpts;
+ PrintingPolicy Policy(LangOpts);
+ Policy.SuppressTagKeyword = false;
+ Policy.FullyQualifiedName = true;
+ Policy.IncludeNewlines = false;
+ llvm::raw_svector_ostream OS(ReturnVal);
+ if (const auto *TD = CXXRD->getDescribedClassTemplate()) {
+ OS << "template <";
+ bool FirstParam = true;
+ for (const auto *Param : *TD->getTemplateParameters()) {
+ if (!FirstParam) OS << ", ";
+ Param->print(OS, Policy);
+ FirstParam = false;
+ }
+ OS << ">\n";
+ }
+ if (CXXRD->isStruct()) {
+ OS << "struct ";
+ } else if (CXXRD->isClass()) {
+ OS << "class ";
+ } else if (CXXRD->isUnion()) {
+ OS << "union ";
+ }
+ OS << CXXRD->getNameAsString();
+ if (CXXRD->getNumBases() > 0) {
+ OS << " : ";
+ bool FirstBase = true;
+ for (const auto &Base : CXXRD->bases()) {
+ if (!FirstBase) OS << ", ";
+ if (Base.isVirtual()) OS << "virtual ";
+ OS << getAccessSpelling(Base.getAccessSpecifier()) << " ";
+ OS << Base.getType().getAsString(Policy);
+ FirstBase = false;
+ }
+ }
+ return ReturnVal;
+}
+
+
// A function to extract the appropriate relative path for a given info's
// documentation. The path returned is a composite of the parent namespaces.
//
@@ -224,10 +362,38 @@ static SymbolID getUSRForDecl(const Decl *D) {
return hashUSR(USR);
}
-static TagDecl *getTagDeclForType(const QualType &T) {
- if (const TagDecl *D = T->getAsTagDecl())
- return D->getDefinition();
- return nullptr;
+static QualType getBaseQualType(const QualType &T) {
+ QualType QT = T;
+ // Get the base type of the QualType
+ // eg. int* -> int, int& -> int, const int -> int
+ bool Modified = true; // Track whether we've modified `qt` in this loop iteration
+ while (Modified) {
+ Modified = false;
+ // If it's a reference type, strip the reference
+ if (QT->isReferenceType()) {
+ QT = QT->getPointeeType();
+ Modified = true;
+ }
+ // If it's a pointer type, strip the pointer
+ else if (QT->isPointerType()) {
+ QT = QT->getPointeeType();
+ Modified = true;
+ }
+ else if (const auto *ElaboratedType = QT->getAs<clang::ElaboratedType>()) {
+ QT = ElaboratedType->desugar();
+ Modified = true;
+ }
+ // Remove const/volatile qualifiers if present
+ else if (QT.hasQualifiers()) {
+ QT = QT.getUnqualifiedType();
+ Modified = true;
+ }
+ else if (const auto *TypedefType = QT->getAs<clang::TypedefType>()) {
+ return QT;
+ }
+ }
+
+ return QT;
}
static RecordDecl *getRecordDeclForType(const QualType &T) {
@@ -237,27 +403,35 @@ static RecordDecl *getRecordDeclForType(const QualType &T) {
}
TypeInfo getTypeInfoForType(const QualType &T) {
- const TagDecl *TD = getTagDeclForType(T);
- if (!TD)
- return TypeInfo(Reference(SymbolID(), T.getAsString()));
-
+ const QualType QT = getBaseQualType(T);
+ const TagDecl *TD = QT->getAsTagDecl();
+ if (!TD) {
+ TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString()));
+ TI.IsBuiltIn = QT->isBuiltinType();
+ TI.IsTemplate = QT->isTemplateTypeParmType();
+ return TI;
+ }
InfoType IT;
- if (dyn_cast<EnumDecl>(TD)) {
+ if (isa<EnumDecl>(TD))
IT = InfoType::IT_enum;
- } else if (dyn_cast<RecordDecl>(TD)) {
+ else if (isa<RecordDecl>(TD))
IT = InfoType::IT_record;
- } else {
+ else
IT = InfoType::IT_default;
- }
- return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
- T.getAsString(), getInfoRelativePath(TD)));
+
+ Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
+ T.getAsString(), getInfoRelativePath(TD));
+ TypeInfo TI = TypeInfo(R);
+ TI.IsBuiltIn = QT->isBuiltinType();
+ TI.IsTemplate = QT->isTemplateTypeParmType();
+ return TI;
}
static bool isPublic(const clang::AccessSpecifier AS,
const clang::Linkage Link) {
if (AS == clang::AccessSpecifier::AS_private)
return false;
- else if ((Link == clang::Linkage::Module) ||
+ if ((Link == clang::Linkage::Module) ||
(Link == clang::Linkage::External))
return true;
return false; // otherwise, linkage is some form of internal linkage
@@ -425,6 +599,7 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
// Don't parse bases if this isn't a definition.
if (!D->isThisDeclarationADefinition())
return;
+
for (const CXXBaseSpecifier &B : D->bases()) {
if (B.isVirtual())
continue;
@@ -487,7 +662,7 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
InfoType::IT_namespace);
}
-void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
+void populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
const clang::Decl *D) {
if (const TemplateParameterList *ParamList =
D->getDescribedTemplateParams()) {
@@ -542,9 +717,10 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
IsInAnonymousNamespace);
I.ReturnType = getTypeInfoForType(D->getReturnType());
+ I.ProtoType = getFunctionPrototype(D);
parseParameters(I, D);
- PopulateTemplateParameters(I.Template, D);
+ populateTemplateParameters(I.Template, D);
// Handle function template specializations.
if (const FunctionTemplateSpecializationInfo *FTSI =
@@ -668,10 +844,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
-
+
I->TagType = D->getTagKind();
parseFields(*I, D, PublicOnly);
if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
+ I->FullName = getRecordPrototype(C);
if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
I->Name = TD->getNameAsString();
I->IsTypeDef = true;
@@ -682,7 +859,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}
I->Path = getInfoRelativePath(I->Namespace);
- PopulateTemplateParameters(I->Template, D);
+ populateTemplateParameters(I->Template, D);
// Full and partial specializations.
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
@@ -721,7 +898,6 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}
}
}
-
// Records are inserted into the parent by reference, so we need to return
// both the parent and the record itself.
auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
index a885a36cb4a3dc..d17fe54bb4009d 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -1,4 +1,3 @@
-/* css for clang-doc mustache backend */
@import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap";
*,*::before *::after {
@@ -19,7 +18,6 @@ video {
display:block;
max-width:100%
}
-
* {
--brand-light:#ce6300;
--text1-light:#000000;
@@ -32,7 +30,6 @@ video {
--surface1-dark:#161212;
--surface2-dark:#272424
}
-
:root {
color-scheme:light;
--brand:var(--brand-light);
@@ -43,7 +40,6 @@ video {
--surface1:var(--surface1-light);
--surface2:var(--surface2-light)
}
-
@media(prefers-color-scheme:dark) {
:root {
color-scheme:dark;
@@ -56,7 +52,6 @@ video {
--surface2:var(--surface2-dark)
}
}
-
[color-scheme=light] {
color-scheme:light;
--brand:var(--brand-light);
@@ -67,7 +62,6 @@ video {
--surface1:var(--surface1-light);
--surface2:var(--surface2-light)
}
-
[color-scheme=dark] {
color-scheme:dark;
--brand:var(--brand-dark);
@@ -78,7 +72,6 @@ video {
--surface1:var(--surface1-dark);
--surface2:var(--surface2-dark)
}
-
html {
background-color:var(--surface1)
}
@@ -92,7 +85,9 @@ html, body {
.container {
display: flex;
- margin-top: 60px;
+ position: relative;
+ flex-direction: row;
+ top: 60px;
height: calc(100% - 60px);
box-sizing: border-box;
}
@@ -121,7 +116,6 @@ body, html {
z-index: 1000;
}
-
.navbar__container {
display:flex;
justify-content:space-between;
@@ -131,21 +125,25 @@ body, html {
max-width:2048px;
margin:auto
}
+
.navbar__logo {
display:flex;
align-items:center;
height:40px
}
+
.navbar__logo a {
display:flex;
align-items:center;
text-decoration:none;
height:100%
}
+
.navbar__logo img {
height:100%;
width:auto
}
+
.navbar__toggle {
background:0 0;
color:var(--text2);
@@ -156,14 +154,17 @@ body, html {
height:2.5rem;
margin-left:auto
}
+
.navbar__toggle:hover {
color:var(--text1)
}
+
@media(min-width:769px) {
.navbar__toggle {
display:none
}
}
+
.navbar__menu {
display:flex;
justify-content:space-between;
@@ -191,11 +192,13 @@ body, html {
transition:transform .5s ease-in-out
}
}
+
@media(max-width:768px) {
.navbar__menu.active {
transform:translateX(0)
}
}
+
.navbar__close {
background:0 0;
border:none;
@@ -204,6 +207,7 @@ body, html {
color:var(--text2);
margin-left:auto
}
+
.navbar__close:hover {
color:var(--text1)
}
@@ -213,6 +217,7 @@ body, html {
display:none
}
}
+
.navbar__links {
display:flex;
gap:1rem;
@@ -263,13 +268,8 @@ body, html {
gap:2rem
}
-.hero__title {
- font-size:2.5rem;
- margin-bottom:.5rem
-}
-
.hero__title-large {
- font-size:3rem
+ font-size:2rem
}
@media(max-width:768px) {
@@ -277,25 +277,21 @@ body, html {
font-size:2.5rem
}
}
-
@media(max-width:480px) {
.hero__title-large {
font-size:2rem
}
}
-
@media(max-width:768px) {
.hero__title {
font-size:2rem
}
}
-
@media(max-width:480px) {
.hero__title {
font-size:1.75rem
}
}
-
.hero__subtitle {
font-size:1.25rem;
font-weight:500
@@ -327,7 +323,6 @@ body, html {
padding:1rem
}
}
-
.section-container h2 {
font-size:1.5rem;
margin-bottom:1rem;
@@ -352,10 +347,38 @@ body, html {
}
}
-.home__row {
+.links-wrapper {
display:grid;
- grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
- gap:2rem
+ gap:1rem;
+ grid-template-columns:1fr 1fr 1fr 1fr
+}
+
+ at media(max-width:768px) {
+ .links-wrapper {
+ grid-template-columns:1fr 1fr
+ }
+}
+ at media(max-width:480px) {
+ .links-wrapper {
+ grid-template-columns:1fr
+ }
+}
+
+.links-wrapper .link {
+ display:flex;
+ flex-direction:column;
+ align-items:center;
+ padding:1rem;
+ border:1px solid var(--text1);
+ border-radius:.25rem
+}
+
+.links-wrapper .link__icon {
+ font-size:2rem
+}
+
+.links-wrapper .link__title {
+ font-size:1rem
}
.table-wrapper {
@@ -371,23 +394,31 @@ body, html {
text-align: left;
}
+
.block-command-command {
font-weight: bold;
}
.code-clang-doc {
- font-size: 1.1rem;
+ font-size: 1.2rem;
+}
+
+.code-highlight:hover {
+ text-decoration: underline;
}
.delimiter-container {
padding: 0.5rem 1rem;
- margin-bottom:1rem;
+ margin-bottom:1rem;
}
.resizer {
width: 5px;
cursor: col-resize;
- background-color: var(--text2);
+ border-left: 3px solid black;
+ top: 60px;
+ left: 250px;
+ position: relative;
}
.resizer:hover {
@@ -400,7 +431,7 @@ body, html {
left: 0;
height: 100%;
position: fixed;
- background-color: var(--surface1);
+ background-color: var(--surface2);
display: flex;
border-left: 1px solid var(--text2);
flex-direction: column;
@@ -433,6 +464,7 @@ body, html {
margin-bottom: 1rem;
padding: 3rem;
}
+
.sidebar-section a {
color: var(--brand)
}
@@ -444,7 +476,8 @@ body, html {
left: 250px;
position: relative;
width: calc(100% - 250px);
- height: 100vh;
+ max-width: 960px;
+ margin-right: auto;
}
.sidebar-item {
@@ -468,4 +501,4 @@ body, html {
a, a:visited, a:hover, a:active {
text-decoration: none;
color: inherit;
-}
+}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
index 7ce51c6e162110..e611000474f14e 100644
--- a/clang-tools-extra/clang-doc/assets/class-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -9,13 +9,10 @@
<html lang="en-US">
<head>
<meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{Name}}</title>
- {{#Stylesheets}}
- <link rel="stylesheet" type="text/css" href="{{.}}"/>
- {{/Stylesheets}}
- {{#Scripts}}
- <script src="{{.}}"></script>
- {{/Scripts}}
+ <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+ <script src="mustache-index.js"></script>
{{! Highlight.js dependency for syntax highlighting }}
<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>
@@ -25,30 +22,21 @@
<nav class="navbar">
<div class="navbar__container">
{{#ProjectName}}
- <div class="navbar__logo">
- {{ProjectName}}
- </div>
- {{/ProjectName}}
- <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 class="navbar__logo">
+ {{ProjectName}}
</div>
+ {{/ProjectName}}
</div>
</nav>
<main>
+ <!-- Sidebar -->
<div class="container">
<div class="sidebar">
<h2>{{RecordType}} {{Name}}</h2>
<ul>
{{#PublicMembers}}
- <li class="sidebar-section">
- <a class="sidebar-item" href="#PublicMethods">Public Members</a>
+ <li class="sidebar-section sidebar-item-container">
+ <a href="#PublicMethods">Public Members</a>
</li>
<ul>
{{#Obj}}
@@ -71,7 +59,7 @@
</ul>
{{/ProtectedMembers}}
{{#PublicFunction}}
- <li class="sidebar-section">
+ <li class="sidebar-section sidebar-item-container">
<a class="sidebar-item" href="#PublicMethods">Public Method</a>
</li>
<ul>
@@ -84,7 +72,7 @@
{{/PublicFunction}}
{{#ProtectedFunction}}
<li class="sidebar-section">
- <a class="sidebar-item" href="#ProtectedFunction">Protected Method</a>
+ <a href="#ProtectedFunction">Protected Method</a>
</li>
<ul>
{{#Obj}}
@@ -96,7 +84,7 @@
{{/ProtectedFunction}}
{{#Enums}}
<li class="sidebar-section">
- <a class="sidebar-item" href="#Enums">Enums</a>
+ <a href="#Enums">Enums</a>
</li>
<ul>
{{#Obj}}
@@ -107,33 +95,26 @@
</ul>
{{/Enums}}
{{#Typedef}}
- <li class="sidebar-section">Typedef</li>
- {{/Typedef}}
- {{#Record}}
<li class="sidebar-section">
- <a class="sidebar-item" href="#Classes">Inner Classes</a>
+ <a href="#ProtectedFunction">Typedef</a>
</li>
- <ul>
- {{#Links}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
- </li>
- {{/Links}}
- </ul>
- {{/Record}}
+ {{/Typedef}}
</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">{{RecordType}} {{Name}}</h1>
- {{#RecordComments}}
- <div class="hero__subtitle">
- {{>Comments}}
- </div>
- {{/RecordComments}}
+ <pre>
+ <code class="language-cpp hero__title-large">
+{{&FullName}}
+ </code>
+ </pre>
+ {{#RecordComments}}
+ <h2>Description</h2>
+ <div class="hero__subtitle">
+ {{>Comments}}
</div>
+ {{/RecordComments}}
</section>
{{#PublicMembers}}
<section id="PublicMembers" class="section-container">
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache
index d63bf258f8f0fb..54608842eaf837 100644
--- a/clang-tools-extra/clang-doc/assets/enum-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -20,7 +20,7 @@
<th>Name</th>
<th>Value</th>
{{#HasComment}}
- <th>Comment</th>
+ <th>Comment</th>
{{/HasComment}}
</tr>
{{#EnumValues}}
@@ -28,7 +28,7 @@
<td>{{Name}}</td>
<td>{{Value}}</td>
{{#EnumValueComments}}
- <td>{{>Comments}}</td>
+ <td>{{>Comments}}</td>
{{/EnumValueComments}}
</tr>
{{/EnumValues}}
diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache b/clang-tools-extra/clang-doc/assets/function-template.mustache
index 0564647467aa60..d023fca17ec626 100644
--- a/clang-tools-extra/clang-doc/assets/function-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/function-template.mustache
@@ -10,7 +10,7 @@
{{! Function Prototype }}
<pre>
<code class="language-cpp code-clang-doc">
-{{ReturnType.Name}} {{Name}} ({{#Params}}{{^End}}{{Type}} {{Name}}, {{/End}}{{#End}}{{Type}} {{Name}}{{/End}}{{/Params}})
+{{&FunctionPrototype}}
</code>
</pre>
{{! Function Comments }}
diff --git a/clang-tools-extra/clang-doc/assets/mustache-index.js b/clang-tools-extra/clang-doc/assets/mustache-index.js
index db320cd8ae403c..438c4e44b195d4 100644
--- a/clang-tools-extra/clang-doc/assets/mustache-index.js
+++ b/clang-tools-extra/clang-doc/assets/mustache-index.js
@@ -1,7 +1,195 @@
+
+// highlight.js plugin to merge the original HTML stream and the highlighted stream
+// see this issue: https://github.com/highlightjs/highlight.js/issues/2889
+var mergeHTMLPlugin = (function () {
+ 'use strict';
+
+ var originalStream;
+
+ /**
+ * @param {string} value
+ * @returns {string}
+ */
+ function escapeHTML(value) {
+ return value
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ /* plugin itself */
+
+ /** @type {HLJSPlugin} */
+ const mergeHTMLPlugin = {
+ // preserve the original HTML token stream
+ "before:highlightElement": ({ el }) => {
+ originalStream = nodeStream(el);
+ },
+ // merge it afterwards with the highlighted token stream
+ "after:highlightElement": ({ el, result, text }) => {
+ if (!originalStream.length) return;
+
+ const resultNode = document.createElement('div');
+ resultNode.innerHTML = result.value;
+ result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
+ el.innerHTML = result.value;
+ }
+ };
+
+ /* Stream merging support functions */
+
+ /**
+ * @typedef Event
+ * @property {'start'|'stop'} event
+ * @property {number} offset
+ * @property {Node} node
+ */
+
+ /**
+ * @param {Node} node
+ */
+ function tag(node) {
+ return node.nodeName.toLowerCase();
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function nodeStream(node) {
+ /** @type Event[] */
+ const result = [];
+ (function _nodeStream(node, offset) {
+ for (let child = node.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType === 3) {
+ offset += child.nodeValue.length;
+ } else if (child.nodeType === 1) {
+ result.push({
+ event: 'start',
+ offset: offset,
+ node: child
+ });
+ offset = _nodeStream(child, offset);
+ // Prevent void elements from having an end tag that would actually
+ // double them in the output. There are more void elements in HTML
+ // but we list only those realistically expected in code display.
+ if (!tag(child).match(/br|hr|img|input/)) {
+ result.push({
+ event: 'stop',
+ offset: offset,
+ node: child
+ });
+ }
+ }
+ }
+ return offset;
+ })(node, 0);
+ return result;
+ }
+
+ /**
+ * @param {any} original - the original stream
+ * @param {any} highlighted - stream of the highlighted source
+ * @param {string} value - the original source itself
+ */
+ function mergeStreams(original, highlighted, value) {
+ let processed = 0;
+ let result = '';
+ const nodeStack = [];
+
+ function selectStream() {
+ if (!original.length || !highlighted.length) {
+ return original.length ? original : highlighted;
+ }
+ if (original[0].offset !== highlighted[0].offset) {
+ return (original[0].offset < highlighted[0].offset) ? original : highlighted;
+ }
+
+ /*
+ To avoid starting the stream just before it should stop the order is
+ ensured that original always starts first and closes last:
+
+ if (event1 == 'start' && event2 == 'start')
+ return original;
+ if (event1 == 'start' && event2 == 'stop')
+ return highlighted;
+ if (event1 == 'stop' && event2 == 'start')
+ return original;
+ if (event1 == 'stop' && event2 == 'stop')
+ return highlighted;
+
+ ... which is collapsed to:
+ */
+ return highlighted[0].event === 'start' ? original : highlighted;
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function open(node) {
+ /** @param {Attr} attr */
+ function attributeString(attr) {
+ return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
+ }
+ // @ts-ignore
+ result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>';
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function close(node) {
+ result += '</' + tag(node) + '>';
+ }
+
+ /**
+ * @param {Event} event
+ */
+ function render(event) {
+ (event.event === 'start' ? open : close)(event.node);
+ }
+
+ while (original.length || highlighted.length) {
+ let stream = selectStream();
+ result += escapeHTML(value.substring(processed, stream[0].offset));
+ processed = stream[0].offset;
+ if (stream === original) {
+ /*
+ On any opening or closing tag of the original markup we first close
+ the entire highlighted node stack, then render the original tag along
+ with all the following original tags at the same offset and then
+ reopen all the tags on the highlighted stack.
+ */
+ nodeStack.reverse().forEach(close);
+ do {
+ render(stream.splice(0, 1)[0]);
+ stream = selectStream();
+ } while (stream === original && stream.length && stream[0].offset === processed);
+ nodeStack.reverse().forEach(open);
+ } else {
+ if (stream[0].event === 'start') {
+ nodeStack.push(stream[0].node);
+ } else {
+ nodeStack.pop();
+ }
+ render(stream.splice(0, 1)[0]);
+ }
+ }
+ return result + escapeHTML(value.substr(processed));
+ }
+
+ return mergeHTMLPlugin;
+
+}());
+
document.addEventListener("DOMContentLoaded", function() {
const resizer = document.getElementById('resizer');
const sidebar = document.querySelector('.sidebar');
-
+ const content = document.querySelector('.content');
+ const minWidth = 100;
+ const maxWidth = 500;
+ hljs.addPlugin(mergeHTMLPlugin);
let isResizing = false;
resizer.addEventListener('mousedown', (e) => {
isResizing = true;
@@ -10,8 +198,11 @@ document.addEventListener("DOMContentLoaded", function() {
document.addEventListener('mousemove', (e) => {
if (!isResizing) return;
const newWidth = e.clientX;
- if (newWidth > 100 && newWidth < window.innerWidth - 100) {
+ if (newWidth > minWidth && newWidth < maxWidth) {
sidebar.style.width = `${newWidth}px`;
+ resizer.style.left = `${newWidth}px`;
+ content.style.left = `${newWidth}px`
+ content.style.width = `calc(100% - ${newWidth}px)`;
}
});
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
index 21cbaa7ec5cf3f..19b0ce28addd9d 100644
--- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -9,39 +9,66 @@
<html lang="en-US">
<head>
<meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{NamespaceTitle}}</title>
- {{#Stylesheets}}
- <link rel="stylesheet" type="text/css" href="{{.}}"/>
- {{/Stylesheets}}
- {{#Scripts}}
- <script src="{{.}}"></script>
- {{/Scripts}}
+ <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+ <script src="mustache-index.js"></script>
{{! Highlight.js dependency for syntax highlighting }}
<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 class="sidebar">
+ {{#ProjectName}}
+ <h2 class="sidebar-project-name">
+ {{ProjectName}}
+ </h2>
+ {{/ProjectName}}
+ <h1>{{NamespaceTitle}}</h1>
+ {{#Namespace}}
+ <h3 id="Namespace">Namespace</h3>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
+ {{/Namespace}}
+ {{#Record}}
+ <h3 id="Class">Class</h3>
+ <ul>
+ {{#Links}}
+ <li>
+ <a href="{{Link}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
+ {{/Record}}
+ {{#Function}}
+ <h3 id="Function">Function</h3>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Function}}
+ {{#Enums}}
+ <h3 id="Enums">Enums</h3>
+ <div>
+ {{#Obj}}
+ {{/Obj}}
+ </div>
+ {{/Enums}}
</nav>
+ <div class="sidebar-resizer"></div>
<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>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc
+ ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
+ nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
+ nec purus nec nunc ultricies ultricies. Nullam nec purus nec nunc
+ ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
+ nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
</main>
</body>
</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 256e29c3c9f894..054856c158dd86 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -427,6 +427,16 @@ Example usage for a project using a compile commands database:
return 1;
sortUsrToInfo(USRToInfo);
+
+ for (const auto &G : USRToInfo) {
+ const clang::doc::Info& I = *G.getValue();
+
+ if (I.IT == clang::doc::InfoType::IT_record) {
+ const clang::doc::RecordInfo& R = *static_cast<const clang::doc::RecordInfo *>(&I);
+ llvm::outs() << "Full Name from record " << R.Name << " Full Name: " << R.FullName << "\n";
+ }
+
+ }
// Ensure the root output directory exists.
if (std::error_code Err = llvm::sys::fs::create_directories(OutDirectory);
>From 377884941796b77ed17d6438cc923d6600d7322c Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 1 Nov 2024 17:47:28 -0400
Subject: [PATCH 61/62] updated working copy
---
clang-tools-extra/clang-doc/BitcodeReader.cpp | 2 +
clang-tools-extra/clang-doc/BitcodeWriter.cpp | 9 +-
clang-tools-extra/clang-doc/BitcodeWriter.h | 1 +
.../clang-doc/HTMLMustacheGenerator.cpp | 138 ++++-----
clang-tools-extra/clang-doc/Mapper.cpp | 14 +-
clang-tools-extra/clang-doc/Representation.h | 12 +-
clang-tools-extra/clang-doc/Serialize.cpp | 112 +++++--
.../clang-doc/assets/clang-doc-mustache.css | 277 +++++-------------
.../clang-doc/assets/class-template.mustache | 178 ++++++-----
.../clang-doc/assets/enum-template.mustache | 4 +-
.../assets/function-template.mustache | 5 +
.../assets/typedef-template.mustache | 29 ++
.../clang-doc/tool/CMakeLists.txt | 3 +-
.../clang-doc/tool/ClangDocMain.cpp | 21 +-
14 files changed, 401 insertions(+), 404 deletions(-)
create mode 100644 clang-tools-extra/clang-doc/assets/typedef-template.mustache
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 4d639937a59152..468fd7e023deaf 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -234,6 +234,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
switch (ID) {
case TYPEDEF_USR:
return decodeRecord(R, I->USR, Blob);
+ case TYPEDEF_DECLARATION:
+ return decodeRecord(R, I->TypeDeclaration, Blob);
case TYPEDEF_NAME:
return decodeRecord(R, I->Name, Blob);
case TYPEDEF_DEFLOCATION:
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 4b8f67302ce9d9..96f00d67470ec3 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -200,6 +200,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{TEMPLATE_PARAM_CONTENTS, {"Contents", &StringAbbrev}},
{TEMPLATE_SPECIALIZATION_OF, {"SpecializationOf", &SymbolIDAbbrev}},
{TYPEDEF_USR, {"USR", &SymbolIDAbbrev}},
+ {TYPEDEF_DECLARATION, {"Declaration", &StringAbbrev}},
{TYPEDEF_NAME, {"Name", &StringAbbrev}},
{TYPEDEF_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
{TYPEDEF_IS_USING, {"IsUsing", &BoolAbbrev}}};
@@ -235,7 +236,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
{ENUM_VALUE_NAME, ENUM_VALUE_VALUE, ENUM_VALUE_EXPR}},
// Typedef Block
{BI_TYPEDEF_BLOCK_ID,
- {TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}},
+ {TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DECLARATION, TYPEDEF_DEFLOCATION,
+ TYPEDEF_IS_USING}},
// Namespace Block
{BI_NAMESPACE_BLOCK_ID,
{NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
@@ -430,10 +432,6 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
if (R.USR == EmptySID && R.Name.empty())
return;
- if (R.Name == "const Shape &") {
- llvm::outs() << "const Shape & USR: " <<
- llvm::toHex(llvm::toStringRef(R.USR)) << "\n";
- }
StreamSubBlockGuard Block(Stream, BI_REFERENCE_BLOCK_ID);
emitRecord(R.USR, REFERENCE_USR);
emitRecord(R.Name, REFERENCE_NAME);
@@ -454,6 +452,7 @@ void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
StreamSubBlockGuard Block(Stream, BI_TYPEDEF_BLOCK_ID);
emitRecord(T.USR, TYPEDEF_USR);
emitRecord(T.Name, TYPEDEF_NAME);
+ emitRecord(T.TypeDeclaration, TYPEDEF_DECLARATION);
for (const auto &N : T.Namespace)
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : T.Description)
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 117527cf89c969..fa3e9e32b5c3bb 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -135,6 +135,7 @@ enum RecordId {
TEMPLATE_SPECIALIZATION_OF,
TYPEDEF_USR,
TYPEDEF_NAME,
+ TYPEDEF_DECLARATION,
TYPEDEF_DEFLOCATION,
TYPEDEF_IS_USING,
RI_LAST,
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index d7079497c25096..313d1c5346961c 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -25,7 +25,6 @@ void generateIndex(Info *I) {
switch (I->IT) {
case InfoType::IT_namespace: {
const NamespaceInfo& N = *static_cast<clang::doc::NamespaceInfo *>(I);
- SmallString<16> Name = N.getFileBaseName();
std::string HexId = llvm::toHex(llvm::toStringRef(N.USR));
Index[HexId] = HexId;
for (const EnumInfo& E : N.Children.Enums)
@@ -36,15 +35,12 @@ void generateIndex(Info *I) {
}
case InfoType::IT_record: {
const RecordInfo& R = *static_cast<clang::doc::RecordInfo *>(I);
- SmallString<16> Name = R.getFileBaseName();
std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
Index[HexId] = HexId;
- for (const EnumInfo& E : R.Children.Enums) {
+ for (const EnumInfo& E : R.Children.Enums)
Index[llvm::toHex(llvm::toStringRef(E.USR))] = HexId;
- }
- for (const TypedefInfo& T : R.Children.Typedefs) {
+ for (const TypedefInfo& T : R.Children.Typedefs)
Index[llvm::toHex(llvm::toStringRef(T.USR))] = HexId;
- }
break;
}
case InfoType::IT_enum:
@@ -127,10 +123,12 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+ auto TypeDefFilePath = CDCtx.MustacheTemplates.lookup("typedef-template");
std::vector<std::pair<StringRef, StringRef>> Partials = {
{"Comments", CommentFilePath},
{"FunctionPartial", FunctionFilePath},
- {"EnumPartial", EnumFilePath}
+ {"EnumPartial", EnumFilePath},
+ {"TypedefPartial", TypeDefFilePath}
};
auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
@@ -152,8 +150,6 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
const clang::doc::ClangDocContext &CDCtx) {
if (auto Err = setupTemplateFiles(CDCtx))
return Err;
- // Track which directories we already tried to create.
- llvm::StringSet<> CreatedDirs;
// Collect all output by file name and create the necessary directories.
llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
for (const auto &Group : Infos) {
@@ -182,41 +178,6 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
return llvm::Error::success();
}
-Value extractValue(const Location &L,
- std::optional<StringRef> RepositoryUrl = std::nullopt) {
- Object Obj = Object();
- Obj.insert({"LineNumber", L.LineNumber});
- Obj.insert({"Filename", L.Filename});
-
- if (!L.IsFileInRootDir || !RepositoryUrl)
- return Obj;
-
- SmallString<128> FileURL(*RepositoryUrl);
- llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
- FileURL += "#" + std::to_string(L.LineNumber);
- Obj.insert({"FileURL", FileURL});
-
- return Obj;
-}
-
-Value extractValue(const Reference &I, StringRef CurrentDirectory) {
- llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
- llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
- llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
- Object Obj = Object();
- Obj.insert({"Link", Path});
- Obj.insert({"Name", I.Name});
- Obj.insert({"QualName", I.QualName});
- Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
- return Obj;
-}
-
-
-Value extractValue(const TypedefInfo &I) {
- // Not Supported
- return nullptr;
-}
-
Value extractValue(const CommentInfo &I) {
Object Obj = Object();
Value Child = Object();
@@ -238,6 +199,7 @@ Value extractValue(const CommentInfo &I) {
if (I.Kind == "BlockCommandComment") {
Child.getAsObject()->insert({"Command", I.Name});
Value ChildArr = Array();
+
for (const auto& C: I.Children)
ChildArr.getAsArray()->emplace_back(extractValue(*C));
Child.getAsObject()->insert({"Children", ChildArr});
@@ -250,6 +212,56 @@ Value extractValue(const CommentInfo &I) {
}
+Value extractValue(const std::optional<Location> &Loc,
+ std::optional<StringRef> RepositoryUrl = std::nullopt) {
+
+ Object Obj = Object();
+ if (Loc.has_value()) {
+ Location L = *Loc;
+ SmallString<128> Filename(llvm::sys::path::filename(L.Filename));
+ Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"Filename", Filename});
+ if (!RepositoryUrl)
+ return Obj;
+ // This link only works specifically for github
+ SmallString<128> FileURL(*RepositoryUrl);
+ llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, "blob/main");
+ llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+ FileURL += "#L" + std::to_string(L.LineNumber);
+ Obj.insert({"FileURL", FileURL});
+ }
+ return Obj;
+}
+
+Value extractValue(const Reference &I, StringRef CurrentDirectory) {
+ llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
+ llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
+ llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
+ Object Obj = Object();
+ Obj.insert({"Link", Path});
+ Obj.insert({"Name", I.Name});
+ Obj.insert({"QualName", I.QualName});
+ Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+ return Obj;
+}
+
+
+Value extractValue(const TypedefInfo &I, const ClangDocContext& CDCtx) {
+ Object Obj = Object();
+ Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+ Obj.insert({"TypeDeclaration", I.TypeDeclaration});
+ Obj.insert({"Name", I.Name});
+ Obj.insert({"IsAlias", I.IsUsing});
+ Obj.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
+ if (!I.Description.empty()) {
+ Value ArrDesc = Array();
+ for (const CommentInfo& Child : I.Description)
+ ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+ Obj.insert({"TypeDefComments", ArrDesc});
+ }
+ return Obj;
+}
+
// Function to replace a substring within a SmallString with another SmallString
void replaceSubstring(llvm::SmallString<256> &Input,
const llvm::SmallString<16> &From,
@@ -383,14 +395,7 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
Obj.insert({"FunctionComments", ArrDesc});
}
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- Obj.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
- else
- Obj.insert({"Location", extractValue(L)});
- }
+ Obj.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
return Obj;
}
@@ -429,16 +434,7 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
ArrDesc.getAsArray()->emplace_back(extractValue(Child));
Obj.insert({"EnumComments", ArrDesc});
}
-
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- Obj.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
- else
- Obj.insert({"Location", extractValue(L)});
- }
-
+ Obj.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
return Obj;
}
@@ -493,10 +489,15 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
Value ArrTypedefs = Array();
for (const TypedefInfo& Child : S.Typedefs)
- ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+ ArrTypedefs.getAsArray()->emplace_back(extractValue(Child, CDCtx));
if (!ArrTypedefs.getAsArray()->empty())
Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+
+
+ llvm::raw_fd_ostream os(1, false);
+ llvm::json::OStream jStream(os, /*Indent=*/2);
+ jStream.value(ArrTypedefs);
}
Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
@@ -536,16 +537,7 @@ Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
I.VirtualParents)});
RecordValue.insert({"RecordType", getTagType(I.TagType)});
-
- if (I.DefLoc.has_value()) {
- Location L = *I.DefLoc;
- if (CDCtx.RepositoryUrl.has_value())
- RecordValue.insert({"Location", extractValue(L,
- StringRef{*CDCtx.RepositoryUrl})});
- else
- RecordValue.insert({"Location", extractValue(L)});
- }
-
+ RecordValue.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
StringRef BasePath = I.getRelativeFilePath("");
extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
Value PublicMembers = Array();
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index 6c90db03424c65..53f9f129c187e8 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -22,9 +22,8 @@ static llvm::StringSet<> USRVisited;
static llvm::sys::Mutex USRVisitedGuard;
template <typename T> bool isTypedefAnonRecord(const T *D) {
- if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
+ if (const auto *C = dyn_cast<CXXRecordDecl>(D))
return C->getTypedefNameForAnonDecl();
- }
return false;
}
@@ -121,6 +120,7 @@ int MapASTVisitor::getLine(const NamedDecl *D,
return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine();
}
+
llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
const ASTContext &Context,
llvm::StringRef RootDir,
@@ -128,9 +128,12 @@ llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
llvm::SmallString<128> File(Context.getSourceManager()
.getPresumedLoc(D->getBeginLoc())
.getFilename());
+ llvm::SmallString<128> Result;
IsFileInRootDir = false;
+ Result = llvm::sys::path::remove_leading_dotslash(File);
+ Result = llvm::SmallString<128>(llvm::sys::path::convert_to_slash(Result));
if (RootDir.empty() || !File.starts_with(RootDir))
- return File;
+ return Result;
IsFileInRootDir = true;
llvm::SmallString<128> Prefix(RootDir);
// replace_path_prefix removes the exact prefix provided. The result of
@@ -139,8 +142,9 @@ llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
// ends with a / and the result has the desired format.
if (!llvm::sys::path::is_separator(Prefix.back()))
Prefix += llvm::sys::path::get_separator();
- llvm::sys::path::replace_path_prefix(File, Prefix, "");
- return File;
+
+ llvm::sys::path::replace_path_prefix(Result, Prefix, "");
+ return Result;
}
} // namespace doc
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index edd9143f444fed..0bd2ec9d201ae0 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -422,12 +422,15 @@ struct TypedefInfo : public SymbolInfo {
void merge(TypedefInfo &&I);
TypeInfo Underlying;
-
- // Inidicates if this is a new C++ "using"-style typedef:
+ // Underlying type declaration
+ SmallString<16> TypeDeclaration;
+ // Indicates if this is a new C++ "using"-style typedef:
// using MyVector = std::vector<int>
// False means it's a C-style typedef:
// typedef std::vector<int> MyVector;
bool IsUsing = false;
+
+ std::vector<CommentInfo> Description;
};
struct BaseRecordInfo : public RecordInfo {
@@ -465,8 +468,9 @@ struct EnumValueInfo {
// Stores the user-supplied initialization expression for this enumeration
// constant. This will be empty for implicit enumeration values.
SmallString<16> ValueExpr;
-
- std::vector<CommentInfo> Description; /// Comment description of this field.
+
+ /// Comment description of this field.
+ std::vector<CommentInfo> Description;
};
// TODO: Expand to allow for documenting templating.
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 560647e4e03c1d..f65974a91ee946 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -32,40 +32,40 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
-void printTemplateParameters(const TemplateParameterList *TemplateParams, llvm::raw_ostream &stream) {
- stream << "template <";
+void getTemplateParameters(const TemplateParameterList *TemplateParams,
+ llvm::raw_ostream &Stream) {
+ Stream << "template <";
for (unsigned i = 0; i < TemplateParams->size(); ++i) {
if (i > 0) {
- stream << ", ";
+ Stream << ", ";
}
const NamedDecl *Param = TemplateParams->getParam(i);
if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Param)) {
if (TTP->wasDeclaredWithTypename()) {
- stream << "typename";
+ Stream << "typename";
} else {
- stream << "class";
+ Stream << "class";
}
-
if (TTP->isParameterPack()) {
- stream << "...";
+ Stream << "...";
}
- stream << " " << TTP->getNameAsString();
+ Stream << " " << TTP->getNameAsString();
} else if (const auto *NTTP = llvm::dyn_cast<NonTypeTemplateParmDecl>(Param)) {
- NTTP->getType().print(stream, NTTP->getASTContext().getPrintingPolicy());
+ NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy());
if (NTTP->isParameterPack()) {
- stream << "...";
+ Stream << "...";
}
- stream << " " << NTTP->getNameAsString();
+ Stream << " " << NTTP->getNameAsString();
} else if (const auto *TTPD = llvm::dyn_cast<TemplateTemplateParmDecl>(Param)) {
- stream << "template <";
- printTemplateParameters(TTPD->getTemplateParameters(), stream);
- stream << "> class " << TTPD->getNameAsString();
+ Stream << "template <";
+ getTemplateParameters(TTPD->getTemplateParameters(), Stream);
+ Stream << "> class " << TTPD->getNameAsString();
}
}
- stream << "> ";
+ Stream << "> ";
}
// Extract the full function prototype from a FunctionDecl including
@@ -76,9 +76,14 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
const ASTContext& Ctx = FuncDecl->getASTContext();
// If it's a templated function, handle the template parameters
if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) {
- printTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+ getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+ }
+ // If it's a const method, add 'const' qualifier
+ if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
+ if (Method->isVirtual()) {
+ Stream << "virtual ";
+ }
}
-
// Print return type
FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy());
@@ -121,22 +126,50 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
// If it's a const method, add 'const' qualifier
if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
- if (Method->isConst()) {
+ if (Method->isConst())
Stream << " const";
- }
+ if (Method->isPureVirtual())
+ Stream << " = 0";
}
return Result; // Convert SmallString to std::string for return
}
-// extract full cl for record declaration
+llvm::SmallString<16> getTypeDefDecl(const TypedefDecl *TypeDef) {
+ llvm::SmallString<16> Result;
+ llvm::raw_svector_ostream Stream(Result);
+ const ASTContext& Ctx = TypeDef->getASTContext();
+ Stream << "typedef ";
+ QualType Q = TypeDef->getUnderlyingType();
+ Q.print(Stream, Ctx.getPrintingPolicy());
+ Stream << " " << TypeDef->getNameAsString();
+ return Result;
+}
+
+llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) {
+ llvm::SmallString<16> Result;
+ llvm::raw_svector_ostream Stream(Result);
+ const ASTContext& Ctx = Alias->getASTContext();
+ if (const auto *TmplDecl = Alias->getDescribedTemplate()) {
+ getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+ }
+ Stream << "using "
+ << Alias->getNameAsString()
+ << " = ";
+ QualType Q = Alias->getUnderlyingType();
+ Q.print(Stream, Ctx.getPrintingPolicy());
+
+ return Result;
+}
+
+// extract full syntax for record declaration
llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
- llvm::SmallString<16> ReturnVal;
+ llvm::SmallString<16> Result;
LangOptions LangOpts;
PrintingPolicy Policy(LangOpts);
Policy.SuppressTagKeyword = false;
Policy.FullyQualifiedName = true;
Policy.IncludeNewlines = false;
- llvm::raw_svector_ostream OS(ReturnVal);
+ llvm::raw_svector_ostream OS(Result);
if (const auto *TD = CXXRD->getDescribedClassTemplate()) {
OS << "template <";
bool FirstParam = true;
@@ -166,7 +199,7 @@ llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
FirstBase = false;
}
}
- return ReturnVal;
+ return Result;
}
@@ -574,7 +607,6 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
ASTContext &Context = E->getASTContext();
if (RawComment *Comment =
E->getASTContext().getRawCommentForDeclNoCache(E)) {
- CommentInfo CInfo;
Comment->setAttached();
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
EnumValueInfo &Member = I.Members.back();
@@ -719,7 +751,6 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
I.ReturnType = getTypeInfoForType(D->getReturnType());
I.ProtoType = getFunctionPrototype(D);
parseParameters(I, D);
-
populateTemplateParameters(I.Template, D);
// Handle function template specializations.
@@ -743,7 +774,8 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
-
+ if (!D)
+ return;
ASTContext& Context = D->getASTContext();
// TODO investigate whether we can use ASTContext::getCommentForDecl instead
// of this logic. See also similar code in Mapper.cpp.
@@ -752,9 +784,9 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
return;
Comment->setAttached();
- if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
+ if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
I.Description.emplace_back();
- parseFullComment(fc, I.Description.back());
+ parseFullComment(Fc, I.Description.back());
}
}
@@ -952,7 +984,7 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly) {
TypedefInfo Info;
-
+ ASTContext& Context = D->getASTContext();
bool IsInAnonymousNamespace = false;
populateInfo(Info, D, FC, IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
@@ -960,6 +992,8 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+ Info.TypeDeclaration = getTypeDefDecl(D);
+
if (Info.Underlying.Type.Name.empty()) {
// Typedef for an unnamed type. This is like "typedef struct { } Foo;"
// The record serializer explicitly checks for this syntax and constructs
@@ -967,7 +1001,13 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
return {};
}
Info.IsUsing = false;
-
+ if (RawComment *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
+ Comment->setAttached();
+ if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
+ Info.Description.emplace_back();
+ parseFullComment(Fc, Info.Description.back());
+ }
+ }
// Info is wrapped in its parent scope so is returned in the second position.
return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
}
@@ -978,7 +1018,7 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly) {
TypedefInfo Info;
-
+ ASTContext& Context = D->getASTContext();
bool IsInAnonymousNamespace = false;
populateInfo(Info, D, FC, IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
@@ -986,8 +1026,16 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+ Info.TypeDeclaration = getTypeAlias(D);
Info.IsUsing = true;
-
+
+ if (RawComment *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
+ Comment->setAttached();
+ if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
+ Info.Description.emplace_back();
+ parseFullComment(Fc, Info.Description.back());
+ }
+ }
// Info is wrapped in its parent scope so is returned in the second position.
return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
}
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
index d17fe54bb4009d..9fdf43bfbea1e9 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -28,7 +28,8 @@ video {
--text1-dark:#ffffff;
--text2-dark:#cccccc;
--surface1-dark:#161212;
- --surface2-dark:#272424
+ --surface2-dark:#272424;
+ --sidebar-width: 300px;
}
:root {
color-scheme:light;
@@ -77,188 +78,22 @@ html {
}
html, body {
+ font-family:Inter,sans-serif;
min-height: 100vh;
margin: 0;
padding: 0;
width: 100%;
+ height: 100%;
}
.container {
display: flex;
position: relative;
flex-direction: row;
- top: 60px;
- height: calc(100% - 60px);
- box-sizing: border-box;
-}
-
-body, html {
- font-family:Inter,sans-serif;
- margin: 0;
- padding: 0;
- height: 100%;
-}
-
-/* Navbar Styles */
-.navbar {
- background-color: var(--surface2);
- border-bottom: 1px solid var(--text2);
- position: fixed;
- width: 100%;
- top: 0;
- left: 0;
- height: 60px; /* Adjust as needed */
- color: white;
- display: flex;
- align-items: center;
- padding: 0 20px;
+ height: calc(100% - var(--sidebar-width));
box-sizing: border-box;
- z-index: 1000;
-}
-
-.navbar__container {
- display:flex;
- justify-content:space-between;
- align-items:center;
- padding:1rem;
- color:var(--text1);
- max-width:2048px;
- margin:auto
-}
-
-.navbar__logo {
- display:flex;
- align-items:center;
- height:40px
-}
-
-.navbar__logo a {
- display:flex;
- align-items:center;
- text-decoration:none;
- height:100%
}
-.navbar__logo img {
- height:100%;
- width:auto
-}
-
-.navbar__toggle {
- background:0 0;
- color:var(--text2);
- border:none;
- cursor:pointer;
- font-size:1.5rem;
- width:2.5rem;
- height:2.5rem;
- margin-left:auto
-}
-
-.navbar__toggle:hover {
- color:var(--text1)
-}
-
- at media(min-width:769px) {
- .navbar__toggle {
- display:none
- }
-}
-
-.navbar__menu {
- display:flex;
- justify-content:space-between;
- align-items:center;
- list-style:none;
- margin:0;
- padding:0;
- gap:.25rem;
- margin-left:auto
-}
-
- at media(max-width:768px) {
- .navbar__menu {
- flex-direction:column;
- justify-content:flex-start;
- width:100%;
- background-color:var(--surface2);
- position:fixed;
- top:0;
- left:0;
- right:0;
- bottom:0;
- padding:1.5rem;
- transform:translateX(100%);
- transition:transform .5s ease-in-out
- }
-}
-
- at media(max-width:768px) {
- .navbar__menu.active {
- transform:translateX(0)
- }
-}
-
-.navbar__close {
- background:0 0;
- border:none;
- cursor:pointer;
- font-size:1.5rem;
- color:var(--text2);
- margin-left:auto
-}
-
-.navbar__close:hover {
- color:var(--text1)
-}
-
- at media(min-width:769px) {
- .navbar__close {
- display:none
- }
-}
-
-.navbar__links {
- display:flex;
- gap:1rem;
- align-items:center;
- margin:0;
- padding:0
-}
-
- at media(max-width:768px) {
- .navbar__links {
- flex-direction:column
- }
-}
-
-.navbar__item {
- list-style-type:none
-}
-
-.navbar__link {
- color:var(--text2);
- text-decoration:none;
- padding:.5rem
-}
-
-.navbar__link:hover {
- color:var(--text1)
-}
-
-.navbar__theme-toggle-button {
- background:0 0;
- color:var(--text2);
- border:none;
- cursor:pointer;
- font-size:1.5rem;
- width:2.5rem;
- height:2.5rem
-}
-
-.navbar__theme-toggle-button:hover {
- color:var(--text1)
-}
.hero__container {
margin-top:1rem;
@@ -394,7 +229,6 @@ body, html {
text-align: left;
}
-
.block-command-command {
font-weight: bold;
}
@@ -403,9 +237,7 @@ body, html {
font-size: 1.2rem;
}
-.code-highlight:hover {
- text-decoration: underline;
-}
+
.delimiter-container {
padding: 0.5rem 1rem;
@@ -416,9 +248,11 @@ body, html {
width: 5px;
cursor: col-resize;
border-left: 3px solid black;
- top: 60px;
- left: 250px;
- position: relative;
+ top: 0;
+ left: var(--sidebar-width);
+ height: 100%;
+ position: fixed;
+ z-index: 1000;
}
.resizer:hover {
@@ -426,14 +260,14 @@ body, html {
}
.sidebar {
- width: 250px;
+ width: var(--sidebar-width);
top: 0;
left: 0;
height: 100%;
+ padding-left: 1rem;
position: fixed;
background-color: var(--surface2);
display: flex;
- border-left: 1px solid var(--text2);
flex-direction: column;
overflow-y: auto;
scrollbar-width: thin;
@@ -451,31 +285,27 @@ body, html {
list-style-type: none;
}
-.sidebar ul li {
- padding-right: 1rem;
- padding-left: 2rem;
- padding-top: 0.25rem;
- padding-bottom: 0.25rem;
-}
-
.sidebar-section {
+ padding-right: 1rem;
font-size:1.5rem;
font-weight: bold;
- margin-bottom: 1rem;
- padding: 3rem;
}
-.sidebar-section a {
- color: var(--brand)
+.sidebar-section h2 {
+ font-size:1.25rem;
+}
+
+.sidebar-section h1 {
+ margin: 1rem;
+ font-size:2rem;
}
-/* Content */
.content {
background-color: var(--text1-inverse);
padding: 20px;
- left: 250px;
+ left: var(--sidebar-width);
position: relative;
- width: calc(100% - 250px);
+ width: calc(100% - var(--sidebar-width));
max-width: 960px;
margin-right: auto;
}
@@ -484,14 +314,51 @@ body, html {
color: var(--text1);
}
+.sidebar-item-container {
+ font-size: 16px;
+ padding-left: 5px;
+ color: #0e79c0;
+}
+
+
.sidebar-item-container:hover {
- width: 100%;
- background-color: grey;
+ border-left: 3px solid var(--brand);
}
-.sidebar-item-container:hover a {
- width: 100%;
- color: var(--text1-inverse);
+.sidebar-section input[type="checkbox"] {
+ display: none;
+}
+
+.sidebar-section label {
+ cursor: pointer;
+ display: block;
+ font-size: 1.1rem;
+ font-weight: bold;
+ padding: 1rem 2rem;
+ position: relative;
+}
+
+.sidebar-section label::before {
+ content: '\25B6';
+ position: absolute;
+ left: 1rem;
+ font-size: 0.8rem;
+ color: grey;
+ transition: transform 0.3s ease;
+}
+
+
+.sidebar-section input[type="checkbox"]:checked + label::before {
+ transform: rotate(90deg);
+}
+
+.sidebar-section ul {
+ display: none;
+ padding-left: 1rem;
+}
+
+.sidebar-section input[type="checkbox"]:checked + label + ul {
+ display: block;
}
.class-container {
@@ -501,4 +368,16 @@ body, html {
a, a:visited, a:hover, a:active {
text-decoration: none;
color: inherit;
-}
\ No newline at end of file
+}
+
+.definition {
+ margin-top: 10px;
+}
+
+.definition a {
+ color: #0e84b5;
+}
+
+.definition a:hover {
+ text-decoration: underline;
+}
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
index e611000474f14e..313d578b30c8e4 100644
--- a/clang-tools-extra/clang-doc/assets/class-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -19,91 +19,121 @@
<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">
- {{#ProjectName}}
- <div class="navbar__logo">
- {{ProjectName}}
- </div>
- {{/ProjectName}}
- </div>
-</nav>
<main>
<!-- Sidebar -->
<div class="container">
<div class="sidebar">
- <h2>{{RecordType}} {{Name}}</h2>
<ul>
- {{#PublicMembers}}
- <li class="sidebar-section sidebar-item-container">
- <a href="#PublicMethods">Public Members</a>
+
+ <li class="sidebar-section">
+ <h1>
+ {{ProjectName}}
+ </h1>
</li>
- <ul>
- {{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
- </li>
- {{/Obj}}
- </ul>
- {{/PublicMembers}}
- {{#ProtectedMembers}}
- <li class="sidebar-section">
- <a class="sidebar-item" href="#PublicMethods">Protected Members</a>
- </li>
+ <li class="sidebar-section">
+ <h2>
+ {{RecordType}} {{Name}}
+ </h2>
+ </li>
+ <!-- Members -->
+ <li class="sidebar-section">
+ <input type="checkbox" id="members-toggle">
+ <label for="members-toggle">Members</label>
<ul>
- {{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
- </li>
- {{/Obj}}
+ {{#PublicMembers}}
+ <li class="sidebar-subsection">
+ <input type="checkbox" id="public-members-toggle">
+ <label for="public-members-toggle">Public</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
+ {{/PublicMembers}}
+ {{#ProtectedMembers}}
+ <li class="sidebar-subsection">
+ <input type="checkbox" id="protected-members-toggle">
+ <label for="protected-members-toggle">Protected</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
+ {{/ProtectedMembers}}
</ul>
- {{/ProtectedMembers}}
- {{#PublicFunction}}
- <li class="sidebar-section sidebar-item-container">
- <a class="sidebar-item" href="#PublicMethods">Public Method</a>
</li>
- <ul>
- {{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
- </li>
- {{/Obj}}
- </ul>
- {{/PublicFunction}}
- {{#ProtectedFunction}}
+ <!-- Methods -->
<li class="sidebar-section">
- <a href="#ProtectedFunction">Protected Method</a>
+ <input type="checkbox" id="methods-toggle">
+ <label for="methods-toggle">Methods</label>
+ <ul>
+ {{#PublicFunction}}
+ <li class="sidebar-subsection">
+ <input type="checkbox" id="public-methods-toggle">
+ <label for="public-methods-toggle">Public</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
+ {{/PublicFunction}}
+ {{#ProtectedFunction}}
+ <li class="sidebar-subsection">
+ <input type="checkbox" id="protected-methods-toggle">
+ <label for="protected-methods-toggle">Protected</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
+ {{/ProtectedFunction}}
+ </ul>
</li>
- <ul>
- {{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
- </li>
- {{/Obj}}
- </ul>
- {{/ProtectedFunction}}
+ <!-- Enums -->
{{#Enums}}
<li class="sidebar-section">
- <a href="#Enums">Enums</a>
+ <input type="checkbox" id="enums-toggle">
+ <label for="enums-toggle">Enums</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
</li>
- <ul>
- {{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
- </li>
- {{/Obj}}
- </ul>
{{/Enums}}
- {{#Typedef}}
+ <!-- Typedefs -->
+ {{#Typedefs}}
<li class="sidebar-section">
- <a href="#ProtectedFunction">Typedef</a>
- </li>
- {{/Typedef}}
+ <input type="checkbox" id="enums-toggle">
+ <label for="enums-toggle">Typedefs</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
+ {{/Typedefs}}
</ul>
</div>
<div class="resizer" id="resizer"></div>
<div class="content">
- <section class="hero section-container">
+ <section id="class" class="hero section-container">
<pre>
<code class="language-cpp hero__title-large">
{{&FullName}}
@@ -115,6 +145,11 @@
{{>Comments}}
</div>
{{/RecordComments}}
+ {{#Location}}
+ <div class="definition">
+ Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
+ </div>
+ {{/Location}}
</section>
{{#PublicMembers}}
<section id="PublicMembers" class="section-container">
@@ -196,11 +231,16 @@
</ul>
</section>
{{/Record}}
- {{#Typedef}}
- <section class="section-container">
- <h2 id="Enums">Enums</h2>
+ {{#Typedefs}}
+ <section id="Typedefs" class="section-container">
+ <h2>Typedefs</h2>
+ <div>
+ {{#Obj}}
+{{>TypedefPartial}}
+ {{/Obj}}
+ </div>
</section>
- {{/Typedef}}
+ {{/Typedefs}}
</div>
</div>
</main>
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache
index 54608842eaf837..a09322ce44c174 100644
--- a/clang-tools-extra/clang-doc/assets/enum-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -40,8 +40,8 @@
</div>
{{/EnumComments}}
{{#Location}}
- <div>
- Defined at line {{LineNumber}} of file {{Filename}}
+ <div class="definition">
+ Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
</div>
{{/Location}}
</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache b/clang-tools-extra/clang-doc/assets/function-template.mustache
index d023fca17ec626..8d0130f5d3591f 100644
--- a/clang-tools-extra/clang-doc/assets/function-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/function-template.mustache
@@ -19,5 +19,10 @@
{{>Comments}}
</div>
{{/FunctionComments}}
+ {{#Location}}
+ <div class="definition">
+ Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
+ </div>
+ {{/Location}}
</div>
</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/typedef-template.mustache b/clang-tools-extra/clang-doc/assets/typedef-template.mustache
new file mode 100644
index 00000000000000..6d72b31563ee82
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/typedef-template.mustache
@@ -0,0 +1,29 @@
+{{!
+ 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
+
+ This file defines the template for type definition
+}}
+<div class="delimiter-container">
+ <div id="{{ID}}">
+ {{! Function Prototype }}
+ <pre>
+ <code class="language-cpp code-clang-doc">
+{{TypeDeclaration}};
+ </code>
+ </pre>
+ {{#TypeDefComments}}
+ <div>
+ {{>Comments}}
+ </div>
+ {{/TypeDefComments}}
+ {{#Location}}
+ <div class="definition">
+ <div>
+ Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
+ </div>
+ </div>
+ {{/Location}}
+ </div>
+</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index eccbc99a7ecc4b..b2d0c3a1feab28 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -26,7 +26,8 @@ set(assets
comments-template.mustache
enum-template.mustache
function-template.mustache
- namespace-template.mustache
+ namespace-template.mustache
+ typedef-template.mustache
clang-doc-mustache.css
clang-doc-default-stylesheet.css
)
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 054856c158dd86..2a8a346e57bd79 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -230,7 +230,6 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
std::string ClangDocPath = getExecutablePath(Argv0, MainAddr);
llvm::SmallString<128> NativeClangDocPath;
llvm::sys::path::native(ClangDocPath, NativeClangDocPath);
-
llvm::SmallString<128> AssetsPath;
AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
@@ -247,12 +246,11 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
= appendPathNative(AssetsPath, "function-template.mustache");
llvm::SmallString<128> CommentTemplate
= appendPathNative(AssetsPath, "comments-template.mustache");
+ llvm::SmallString<128> TypedefTemplate
+ = appendPathNative(AssetsPath, "typedef-template.mustache");
llvm::SmallString<128> IndexJS
= appendPathNative(AssetsPath, "mustache-index.js");
- CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
- CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
- std::string(DefaultStylesheet));
CDCtx.MustacheTemplates.insert({"namespace-template",
NamespaceTemplate.c_str()});
CDCtx.MustacheTemplates.insert({"class-template",
@@ -263,6 +261,11 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
FunctionTemplate.c_str()});
CDCtx.MustacheTemplates.insert({"comments-template",
CommentTemplate.c_str()});
+ CDCtx.MustacheTemplates.insert({"typedef-template",
+ TypedefTemplate.c_str()});
+ CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+ CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+ std::string(DefaultStylesheet));
return llvm::Error::success();
}
@@ -428,16 +431,6 @@ Example usage for a project using a compile commands database:
sortUsrToInfo(USRToInfo);
- for (const auto &G : USRToInfo) {
- const clang::doc::Info& I = *G.getValue();
-
- if (I.IT == clang::doc::InfoType::IT_record) {
- const clang::doc::RecordInfo& R = *static_cast<const clang::doc::RecordInfo *>(&I);
- llvm::outs() << "Full Name from record " << R.Name << " Full Name: " << R.FullName << "\n";
- }
-
- }
-
// Ensure the root output directory exists.
if (std::error_code Err = llvm::sys::fs::create_directories(OutDirectory);
Err != std::error_code()) {
>From 2b490ed6636256590adf700d9721e6f37580e166 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 11 Nov 2024 22:57:44 -0500
Subject: [PATCH 62/62] add source ranges
---
clang-tools-extra/clang-doc/BitcodeReader.cpp | 4 +-
clang-tools-extra/clang-doc/BitcodeWriter.cpp | 14 +-
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 6 +-
.../clang-doc/HTMLMustacheGenerator.cpp | 54 +++--
clang-tools-extra/clang-doc/MDGenerator.cpp | 6 +-
clang-tools-extra/clang-doc/Mapper.cpp | 24 +-
clang-tools-extra/clang-doc/Mapper.h | 3 +-
clang-tools-extra/clang-doc/Representation.h | 23 +-
clang-tools-extra/clang-doc/Serialize.cpp | 83 +++----
clang-tools-extra/clang-doc/Serialize.h | 28 +--
clang-tools-extra/clang-doc/YAMLGenerator.cpp | 2 +-
.../clang-doc/assets/clang-doc-mustache.css | 4 +-
.../clang-doc/assets/class-template.mustache | 84 +++----
.../clang-doc/assets/mustache-index.js | 1 -
.../assets/namespace-template.mustache | 211 +++++++++++++-----
.../unittests/clang-doc/ClangDocTest.cpp | 2 +-
16 files changed, 343 insertions(+), 206 deletions(-)
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 468fd7e023deaf..0aa78a807b0268 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -85,7 +85,7 @@ llvm::Error decodeRecord(const Record &R, std::optional<Location> &Field,
if (R[0] > INT_MAX)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"integer too large to parse");
- Field.emplace((int)R[0], Blob, (bool)R[1]);
+ Field.emplace((int)R[0], (int)R[1], Blob, (bool)R[2]);
return llvm::Error::success();
}
@@ -135,7 +135,7 @@ llvm::Error decodeRecord(const Record &R,
if (R[0] > INT_MAX)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"integer too large to parse");
- Field.emplace_back((int)R[0], Blob, (bool)R[1]);
+ Field.emplace_back((int)R[0], (int)R[1], Blob, (bool)R[2]);
return llvm::Error::success();
}
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 96f00d67470ec3..2b08e29ded75be 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -74,16 +74,19 @@ static void StringAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
static void LocationAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
AbbrevGen(
Abbrev,
- {// 0. Fixed-size integer (line number)
+ {// 0. Start fixed-size integer (line number)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::LineNumberSize),
- // 1. Boolean (IsFileInRootDir)
+ // 1. End fixed-size integer (line number)
+ llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
+ BitCodeConstants::LineNumberSize),
+ // 2. Boolean (IsFileInRootDir)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::BoolSize),
- // 2. Fixed-size integer (length of the following string (filename))
+ // 3. Fixed-size integer (length of the following string (filename))
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::StringLengthSize),
- // 3. The string blob
+ // 4. The string blob
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)});
}
@@ -358,7 +361,8 @@ void ClangDocBitcodeWriter::emitRecord(const Location &Loc, RecordId ID) {
if (!prepRecordData(ID, true))
return;
// FIXME: Assert that the line number is of the appropriate size.
- Record.push_back(Loc.LineNumber);
+ Record.push_back(Loc.StartLineNumber);
+ Record.push_back(Loc.EndLineNumber);
assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize));
Record.push_back(Loc.IsFileInRootDir);
Record.push_back(Loc.Filename.size());
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ed65ac965114b5..07c4bb5765e15e 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -496,18 +496,18 @@ writeFileDefinition(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
if (!L.IsFileInRootDir || !RepositoryUrl)
return std::make_unique<TagNode>(
- HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
+ HTMLTag::TAG_P, "Defined at line " + std::to_string(L.StartLineNumber) +
" of file " + L.Filename);
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
auto LocNumberNode =
- std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
+ std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.StartLineNumber));
// The links to a specific line in the source code use the github /
// googlesource notation so it won't work for all hosting pages.
LocNumberNode->Attributes.emplace_back(
- "href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
+ "href", (FileURL + "#" + std::to_string(L.StartLineNumber)).str());
Node->Children.emplace_back(std::move(LocNumberNode));
Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
auto LocFileNode = std::make_unique<TagNode>(
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 313d1c5346961c..03255da637a988 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -219,7 +219,7 @@ Value extractValue(const std::optional<Location> &Loc,
if (Loc.has_value()) {
Location L = *Loc;
SmallString<128> Filename(llvm::sys::path::filename(L.Filename));
- Obj.insert({"LineNumber", L.LineNumber});
+ Obj.insert({"LineNumber", L.StartLineNumber});
Obj.insert({"Filename", Filename});
if (!RepositoryUrl)
return Obj;
@@ -227,18 +227,17 @@ Value extractValue(const std::optional<Location> &Loc,
SmallString<128> FileURL(*RepositoryUrl);
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, "blob/main");
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
- FileURL += "#L" + std::to_string(L.LineNumber);
+ FileURL += "#L" + std::to_string(L.StartLineNumber) +
+ "-L" + std::to_string(L.EndLineNumber);
Obj.insert({"FileURL", FileURL});
}
return Obj;
}
-Value extractValue(const Reference &I, StringRef CurrentDirectory) {
- llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
- llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
- llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
+Value extractValue(const Reference &I) {
+ std::string Link = llvm::toHex(llvm::toStringRef(I.USR)) + ".html";
Object Obj = Object();
- Obj.insert({"Link", Path});
+ Obj.insert({"Link", Link});
Obj.insert({"Name", I.Name});
Obj.insert({"QualName", I.QualName});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
@@ -299,7 +298,7 @@ void replaceSubstring(llvm::SmallString<256> &Input,
SmallString<64> extractLink(const Reference& R) {
std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
SmallString<16> Name = Index[HexId];
- SmallString<64> Link({Name, ".html"});
+ SmallString<64> Link({Name, ".html#", HexId});
return Link;
}
@@ -378,7 +377,7 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
extractFunctionPrototype(I.ProtoType, I.Params, I.ReturnType)});
Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
Obj.insert({"Access", getAccessSpelling(I.Access).str()});
- Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
+ Obj.insert({"ReturnType", extractValue(I.ReturnType.Type)});
Value ParamArr = Array();
for (const auto Val : llvm::enumerate(I.Params)) {
Value V = Object();
@@ -443,14 +442,14 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
const ClangDocContext &CDCtx) {
Value ArrNamespace = Array();
for (const Reference& Child : S.Namespaces)
- ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+ ArrNamespace.getAsArray()->emplace_back(extractValue(Child));
if (!ArrNamespace.getAsArray()->empty())
Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
Value ArrRecord = Array();
for (const Reference& Child : S.Records)
- ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+ ArrRecord.getAsArray()->emplace_back(extractValue(Child));
if (!ArrRecord.getAsArray()->empty())
Obj.insert({"Record", Object{{"Links", ArrRecord}}});
@@ -470,15 +469,23 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
else
ArrFunction.getAsArray()->emplace_back(F);
}
- if (!ArrFunction.getAsArray()->empty())
+
+ bool HasFunction = !ArrFunction.getAsArray()->empty();
+ bool HasPublicFunction = !PublicFunction.getAsArray()->empty();
+ bool HasProtectedFunction = !ProtectedFunction.getAsArray()->empty();
+
+ if (HasFunction)
Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
- if (!PublicFunction.getAsArray()->empty())
+ if (HasPublicFunction)
Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
- if (!ProtectedFunction.getAsArray()->empty())
+ if (HasProtectedFunction)
Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+ if (HasFunction || HasPublicFunction || HasProtectedFunction)
+ Obj.insert({"FunctionFlag", true});
+
Value ArrEnum = Array();
for (const EnumInfo& Child : S.Enums)
@@ -493,11 +500,6 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
if (!ArrTypedefs.getAsArray()->empty())
Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
-
-
- llvm::raw_fd_ostream os(1, false);
- llvm::json::OStream jStream(os, /*Indent=*/2);
- jStream.value(ArrTypedefs);
}
Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
@@ -519,6 +521,7 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
NamespaceValue.insert({"NamespaceComments", ArrDesc });
}
extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
+
return NamespaceValue;
}
@@ -561,13 +564,20 @@ Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
else if (Member.Access == AccessSpecifier::AS_private)
PrivateMembers.getAsArray()->emplace_back(MemberValue);
}
- if (!PublicMembers.getAsArray()->empty())
+ bool HasPublicMembers = !PublicMembers.getAsArray()->empty();
+ bool HasProtectedMembers = !ProtectedMembers.getAsArray()->empty();
+ bool HasPrivateMembers = !PrivateMembers.getAsArray()->empty();
+
+ if (HasPublicMembers)
RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
- if (!ProtectedMembers.getAsArray()->empty())
+ if (HasProtectedMembers)
RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
- if (!PrivateMembers.getAsArray()->empty())
+ if (HasPrivateMembers)
RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+ if (HasPublicMembers || HasProtectedMembers || HasPrivateMembers)
+ RecordValue.insert({"MemberFlag", true});
+
return RecordValue;
}
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 795eb4b904e3ef..37b1ee2d39e101 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -54,13 +54,13 @@ static void writeFileDefinition(const ClangDocContext &CDCtx, const Location &L,
raw_ostream &OS) {
if (!CDCtx.RepositoryUrl) {
- OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
+ OS << "*Defined at " << L.Filename << "#" << std::to_string(L.StartLineNumber)
<< "*";
} else {
- OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.LineNumber)
+ OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.StartLineNumber)
<< "](" << StringRef{*CDCtx.RepositoryUrl}
<< llvm::sys::path::relative_path(L.Filename) << "#"
- << std::to_string(L.LineNumber) << ")"
+ << std::to_string(L.StartLineNumber) << ")"
<< "*";
}
OS << "\n\n";
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index 53f9f129c187e8..f2c6e6bcb15bdc 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -31,6 +31,21 @@ void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
TraverseDecl(Context.getTranslationUnitDecl());
}
+Location MapASTVisitor::getDeclLocation(const NamedDecl *D) const {
+ bool IsFileInRootDir;
+ llvm::SmallString<128> File =
+ getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
+ ASTContext &Context = D->getASTContext();
+ int Start = Context.getSourceManager()
+ .getPresumedLoc(D->getBeginLoc())
+ .getLine();
+ int End = Context.getSourceManager()
+ .getPresumedLoc(D->getEndLoc())
+ .getLine();
+
+ return Location(Start, End, File, IsFileInRootDir);
+}
+
template <typename T>
bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
// If we're looking a decl not in user files, skip this decl.
@@ -59,8 +74,7 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
llvm::SmallString<128> File =
getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()),
- getLine(D, D->getASTContext()), File,
- IsFileInRootDir, CDCtx.PublicOnly);
+ getDeclLocation(D), CDCtx.PublicOnly);
// A null in place of I indicates that the serializer is skipping this decl
// for some reason (e.g. we're only reporting public decls).
@@ -115,12 +129,6 @@ MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
return nullptr;
}
-int MapASTVisitor::getLine(const NamedDecl *D,
- const ASTContext &Context) const {
- return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine();
-}
-
-
llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
const ASTContext &Context,
llvm::StringRef RootDir,
diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h
index 75c8e947c8f903..e1dd34ea6f0cc6 100644
--- a/clang-tools-extra/clang-doc/Mapper.h
+++ b/clang-tools-extra/clang-doc/Mapper.h
@@ -45,7 +45,8 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
private:
template <typename T> bool mapDecl(const T *D, bool IsDefinition);
- int getLine(const NamedDecl *D, const ASTContext &Context) const;
+ Location getDeclLocation(const NamedDecl *D) const;
+
llvm::SmallString<128> getFile(const NamedDecl *D, const ASTContext &Context,
StringRef RootDir,
bool &IsFileInRootDir) const;
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 0bd2ec9d201ae0..3c7ada38b2ab89 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -245,19 +245,23 @@ struct MemberTypeInfo : public FieldTypeInfo {
};
struct Location {
- Location(int LineNumber = 0, StringRef Filename = StringRef(),
+ Location(int StartLineNumber = 0,
+ int EndLineNumber = 0,
+ StringRef Filename = StringRef(),
bool IsFileInRootDir = false)
- : LineNumber(LineNumber), Filename(Filename),
+ : StartLineNumber(StartLineNumber),
+ EndLineNumber(EndLineNumber),
+ Filename(Filename),
IsFileInRootDir(IsFileInRootDir) {}
bool operator==(const Location &Other) const {
- return std::tie(LineNumber, Filename) ==
- std::tie(Other.LineNumber, Other.Filename);
+ return std::tie(StartLineNumber, EndLineNumber, Filename) ==
+ std::tie(Other.StartLineNumber, Other.EndLineNumber, Other.Filename);
}
bool operator!=(const Location &Other) const {
- return std::tie(LineNumber, Filename) !=
- std::tie(Other.LineNumber, Other.Filename);
+ return std::tie(StartLineNumber, Filename) !=
+ std::tie(Other.StartLineNumber, Other.Filename);
}
// This operator is used to sort a vector of Locations.
@@ -265,11 +269,12 @@ struct Location {
// sort is enough, the order is only needed to call std::unique after sorting
// the vector.
bool operator<(const Location &Other) const {
- return std::tie(LineNumber, Filename) <
- std::tie(Other.LineNumber, Other.Filename);
+ return std::tie(StartLineNumber, Filename) <
+ std::tie(Other.StartLineNumber, Other.Filename);
}
- int LineNumber = 0; // Line number of this Location.
+ int StartLineNumber = 0; // Line number of this Location.
+ int EndLineNumber = 0; // End line number of this Location.
SmallString<32> Filename; // File for this Location.
bool IsFileInRootDir = false; // Indicates if file is inside root directory
};
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index f65974a91ee946..fe31c368dd8c98 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -11,6 +11,7 @@
#include "clang/AST/Comment.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
+#include "clang/AST/Attr.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/SHA1.h"
@@ -74,13 +75,15 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
llvm::SmallString<256> Result;
llvm::raw_svector_ostream Stream(Result);
const ASTContext& Ctx = FuncDecl->getASTContext();
+ const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl);
// If it's a templated function, handle the template parameters
if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) {
getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
}
- // If it's a const method, add 'const' qualifier
- if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
- if (Method->isVirtual()) {
+ // If it's a virtual method
+ if (Method) {
+ if (Method->isVirtual())
+ {
Stream << "virtual ";
}
}
@@ -125,7 +128,11 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
Stream << ")";
// If it's a const method, add 'const' qualifier
- if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
+ if (Method) {
+ if (Method->size_overridden_methods())
+ Stream << " override";
+ if (Method->hasAttr<clang::FinalAttr>())
+ Stream << " final";
if (Method->isConst())
Stream << " const";
if (Method->isPureVirtual())
@@ -732,22 +739,20 @@ static void populateInfo(Info &I, const T *D, const FullComment *C,
template <typename T>
static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
- int LineNumber, StringRef Filename,
- bool IsFileInRootDir,
+ Location Loc,
bool &IsInAnonymousNamespace) {
populateInfo(I, D, C, IsInAnonymousNamespace);
if (D->isThisDeclarationADefinition())
- I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
+ I.DefLoc = Loc;
else
- I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
+ I.Loc.emplace_back(Loc);
}
static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
- const FullComment *FC, int LineNumber,
- StringRef Filename, bool IsFileInRootDir,
+ const FullComment *FC,
+ Location Loc,
bool &IsInAnonymousNamespace) {
- populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
- IsInAnonymousNamespace);
+ populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
I.ReturnType = getTypeInfoForType(D->getReturnType());
I.ProtoType = getFunctionPrototype(D);
parseParameters(I, D);
@@ -828,8 +833,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
// reference, its value is not relevant in here so it's not used
// anywhere besides the function call.
bool IsInAnonymousNamespace;
- populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
- /*FileName=*/{}, IsFileInRootDir,
+ populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},
IsInAnonymousNamespace);
FI.Access =
getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
@@ -847,8 +851,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly) {
auto I = std::make_unique<NamespaceInfo>();
bool IsInAnonymousNamespace = false;
populateInfo(*I, D, FC, IsInAnonymousNamespace);
@@ -868,12 +872,11 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const RecordDecl *D, const FullComment *FC,
+ Location Loc, bool PublicOnly) {
auto I = std::make_unique<RecordInfo>();
bool IsInAnonymousNamespace = false;
- populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
+ populateSymbolInfo(*I, D, FC, Loc, IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
@@ -887,7 +890,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}
// TODO: remove first call to parseBases, that function should be deleted
parseBases(*I, C);
- parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
+ parseBases(*I, C, true, PublicOnly, true);
}
I->Path = getInfoRelativePath(I->Namespace);
@@ -937,12 +940,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const FunctionDecl *D, const FullComment *FC,
+ Location Loc, bool PublicOnly) {
FunctionInfo Func;
bool IsInAnonymousNamespace = false;
- populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
+ populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
Func.Access = clang::AccessSpecifier::AS_none;
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
@@ -952,12 +954,11 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const CXXMethodDecl *D, const FullComment *FC,
+ Location Loc, bool PublicOnly) {
FunctionInfo Func;
bool IsInAnonymousNamespace = false;
- populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
+ populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
@@ -981,16 +982,18 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly) {
+
TypedefInfo Info;
ASTContext& Context = D->getASTContext();
bool IsInAnonymousNamespace = false;
populateInfo(Info, D, FC, IsInAnonymousNamespace);
+
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
-
- Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+
+ Info.DefLoc = Loc;
Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
Info.TypeDeclaration = getTypeDefDecl(D);
@@ -1015,8 +1018,8 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
// A type alias is a C++ "using" declaration for a type. It gets mapped to a
// TypedefInfo with the IsUsing flag set.
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const TypeAliasDecl *D, const FullComment *FC,
+ Location Loc, bool PublicOnly) {
TypedefInfo Info;
ASTContext& Context = D->getASTContext();
bool IsInAnonymousNamespace = false;
@@ -1024,7 +1027,7 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
- Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+ Info.DefLoc = Loc;
Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
Info.TypeDeclaration = getTypeAlias(D);
Info.IsUsing = true;
@@ -1041,12 +1044,12 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly) {
EnumInfo Enum;
bool IsInAnonymousNamespace = false;
- populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
+ populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace);
+
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h
index 4e203ca7891aca..8874299e9af9e5 100644
--- a/clang-tools-extra/clang-doc/Serialize.h
+++ b/clang-tools-extra/clang-doc/Serialize.h
@@ -37,32 +37,32 @@ namespace serialize {
// its parent scope. For NamespaceDecl and RecordDecl both elements are not
// nullptr.
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,
+ bool PublicOnly);
// Function to hash a given USR value for storage.
// As USRs (Unified Symbol Resolution) could be large, especially for functions
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index b612380a3cfa7f..ae4102c33382d0 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -169,7 +169,7 @@ static void CommentInfoMapping(IO &IO, CommentInfo &I) {
template <> struct MappingTraits<Location> {
static void mapping(IO &IO, Location &Loc) {
- IO.mapOptional("LineNumber", Loc.LineNumber, 0);
+ IO.mapOptional("LineNumber", Loc.StartLineNumber, 0);
IO.mapOptional("Filename", Loc.Filename, SmallString<32>());
}
};
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
index 9fdf43bfbea1e9..fd1f2d11d521a3 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -237,8 +237,6 @@ html, body {
font-size: 1.2rem;
}
-
-
.delimiter-container {
padding: 0.5rem 1rem;
margin-bottom:1rem;
@@ -317,12 +315,12 @@ html, body {
.sidebar-item-container {
font-size: 16px;
padding-left: 5px;
- color: #0e79c0;
}
.sidebar-item-container:hover {
border-left: 3px solid var(--brand);
+ color: #0e79c0;
}
.sidebar-section input[type="checkbox"] {
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
index 313d578b30c8e4..3d7d2babe51422 100644
--- a/clang-tools-extra/clang-doc/assets/class-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -12,6 +12,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{Name}}</title>
<link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+ {{! Highlight.js dependency for syntax highlighting }}
<script src="mustache-index.js"></script>
{{! Highlight.js dependency for syntax highlighting }}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
@@ -20,11 +21,10 @@
</head>
<body>
<main>
- <!-- Sidebar -->
<div class="container">
+ <!-- Sidebar -->
<div class="sidebar">
<ul>
-
<li class="sidebar-section">
<h1>
{{ProjectName}}
@@ -36,81 +36,85 @@
</h2>
</li>
<!-- Members -->
+ {{#MemberFlag}}
<li class="sidebar-section">
- <input type="checkbox" id="members-toggle">
+ <input type="checkbox" id="members-toggle" checked>
<label for="members-toggle">Members</label>
<ul>
{{#PublicMembers}}
<li class="sidebar-subsection">
- <input type="checkbox" id="public-members-toggle">
+ <input type="checkbox" id="public-members-toggle" checked>
<label for="public-members-toggle">Public</label>
<ul>
{{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
- </li>
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+ </li>
{{/Obj}}
</ul>
</li>
{{/PublicMembers}}
{{#ProtectedMembers}}
<li class="sidebar-subsection">
- <input type="checkbox" id="protected-members-toggle">
+ <input type="checkbox" id="protected-members-toggle" checked>
<label for="protected-members-toggle">Protected</label>
<ul>
{{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
- </li>
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+ </li>
{{/Obj}}
</ul>
</li>
{{/ProtectedMembers}}
</ul>
</li>
+ {{/MemberFlag}}
<!-- Methods -->
+ {{#FunctionFlag}}
<li class="sidebar-section">
- <input type="checkbox" id="methods-toggle">
+ <input type="checkbox" id="methods-toggle" checked>
<label for="methods-toggle">Methods</label>
<ul>
{{#PublicFunction}}
- <li class="sidebar-subsection">
- <input type="checkbox" id="public-methods-toggle">
- <label for="public-methods-toggle">Public</label>
- <ul>
- {{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
- </li>
- {{/Obj}}
- </ul>
- </li>
+ <li class="sidebar-subsection">
+ <input type="checkbox" id="public-methods-toggle" checked>
+ <label for="public-methods-toggle">Public</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
{{/PublicFunction}}
{{#ProtectedFunction}}
- <li class="sidebar-subsection">
- <input type="checkbox" id="protected-methods-toggle">
- <label for="protected-methods-toggle">Protected</label>
- <ul>
- {{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
- </li>
- {{/Obj}}
- </ul>
- </li>
+ <li class="sidebar-subsection">
+ <input type="checkbox" id="protected-methods-toggle" checked>
+ <label for="protected-methods-toggle">Protected</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
{{/ProtectedFunction}}
</ul>
- </li>
+ </li>
+ {{/FunctionFlag}}
<!-- Enums -->
{{#Enums}}
<li class="sidebar-section">
- <input type="checkbox" id="enums-toggle">
+ <input type="checkbox" id="enums-toggle" checked>
<label for="enums-toggle">Enums</label>
<ul>
{{#Obj}}
- <li class="sidebar-item-container">
- <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
- </li>
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+ </li>
{{/Obj}}
</ul>
</li>
@@ -118,7 +122,7 @@
<!-- Typedefs -->
{{#Typedefs}}
<li class="sidebar-section">
- <input type="checkbox" id="enums-toggle">
+ <input type="checkbox" id="enums-toggle" checked>
<label for="enums-toggle">Typedefs</label>
<ul>
{{#Obj}}
diff --git a/clang-tools-extra/clang-doc/assets/mustache-index.js b/clang-tools-extra/clang-doc/assets/mustache-index.js
index 438c4e44b195d4..4f70a65399041f 100644
--- a/clang-tools-extra/clang-doc/assets/mustache-index.js
+++ b/clang-tools-extra/clang-doc/assets/mustache-index.js
@@ -1,4 +1,3 @@
-
// highlight.js plugin to merge the original HTML stream and the highlighted stream
// see this issue: https://github.com/highlightjs/highlight.js/issues/2889
var mergeHTMLPlugin = (function () {
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
index 19b0ce28addd9d..efd88e76cf96ff 100644
--- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -7,68 +7,173 @@
}}
<!DOCTYPE html>
<html lang="en-US">
- <head>
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>{{NamespaceTitle}}</title>
- <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
- <script src="mustache-index.js"></script>
- {{! Highlight.js dependency for syntax highlighting }}
- <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="sidebar">
- {{#ProjectName}}
- <h2 class="sidebar-project-name">
- {{ProjectName}}
- </h2>
- {{/ProjectName}}
- <h1>{{NamespaceTitle}}</h1>
- {{#Namespace}}
- <h3 id="Namespace">Namespace</h3>
+<head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>{{Name}}</title>
+ <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+ <script src="mustache-index.js"></script>
+ {{! Highlight.js dependency for syntax highlighting }}
+ <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>
+<main>
+ <div class="container">
+ <!-- Sidebar -->
+ <div class="sidebar">
<ul>
- {{#Links}}
- <li>
- <a href="{{Link}}">{{Name}}</a>
+ <li class="sidebar-section">
+ <h1>
+ {{ProjectName}}
+ </h1>
+ </li>
+ <li class="sidebar-section">
+ <h2>
+ {{NamespaceTitle}}
+ </h2>
+ </li>
+ <!-- Namespace -->
+ {{#Namespace}}
+ <li class="sidebar-section">
+ <input type="checkbox" id="namespace-toggle" checked>
+ <label for="namespace-toggle">Namespace</label>
+ <ul>
+ {{#Links}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
+ </li>
+ {{/Namespace}}
+ {{#Record}}
+ <li class="sidebar-section">
+ <input type="checkbox" id="namespace-toggle" checked>
+ <label for="namespace-toggle">Classes</label>
+ <ul>
+ {{#Links}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Links}}
+ </ul>
+ </li>
+ {{/Record}}
+ <!-- Methods -->
+ {{#Function}}
+ <li class="sidebar-section">
+ <input type="checkbox" id="function-toggle" checked>
+ <label for="function-toggle">Functions</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
+ {{/Function}}
+ <!-- Enums -->
+ {{#Enums}}
+ <li class="sidebar-section">
+ <input type="checkbox" id="enums-toggle" checked>
+ <label for="enums-toggle">Enums</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
+ </li>
+ {{/Enums}}
+ <!-- Typedefs -->
+ {{#Typedefs}}
+ <li class="sidebar-section">
+ <input type="checkbox" id="typedef-toggle" checked>
+ <label for="typedef-toggle">Typedefs</label>
+ <ul>
+ {{#Obj}}
+ <li class="sidebar-item-container">
+ <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+ </li>
+ {{/Obj}}
+ </ul>
</li>
- {{/Links}}
+ {{/Typedefs}}
</ul>
+ </div>
+ <div class="resizer" id="resizer"></div>
+ <div class="content">
+ <section id="class" class="hero section-container">
+ <pre>
+ <code class="language-cpp hero__title-large">
+{{&NamespaceTitle}}
+ </code>
+ </pre>
+ {{#NamespaceComments}}
+ <h2>Description</h2>
+ <div class="hero__subtitle">
+ {{>Comments}}
+ </div>
+ {{/NamespaceComments}}
+ </section>
+ {{#Namespace}}
+ <section id="Classes" class="section-container">
+ <h2>Namespace</h2>
+ <ul class="class-container">
+ {{#Links}}
+ <li id="{{ID}}" style="max-height: 40px;">
+<a href="{{Link}}"><pre><code class="language-cpp code-clang-doc" >namespace {{Name}}</code></pre></a>
+ </li>
+ {{/Links}}
+ </ul>
+ </section>
{{/Namespace}}
{{#Record}}
- <h3 id="Class">Class</h3>
- <ul>
- {{#Links}}
- <li>
- <a href="{{Link}}">{{Name}}</a>
- </li>
- {{/Links}}
- </ul>
+ <section id="Classes" class="section-container">
+ <h2>Classes</h2>
+ <ul class="class-container">
+ {{#Links}}
+ <li id="{{ID}}" style="max-height: 40px;">
+<a href="{{Link}}"><pre><code class="language-cpp code-clang-doc" >class {{Name}}</code></pre></a>
+ </li>
+ {{/Links}}
+ </ul>
+ </section>
{{/Record}}
{{#Function}}
- <h3 id="Function">Function</h3>
- <div>
- {{#Obj}}
- {{/Obj}}
- </div>
+ <section id="Classes" class="section-container">
+ <h2>Functions</h2>
+ <div>
+ {{#Obj}}
+{{>FunctionPartial}}
+ {{/Obj}}
+ </div>
+ </section>
{{/Function}}
{{#Enums}}
- <h3 id="Enums">Enums</h3>
- <div>
+ <section id="Enums" class="section-container">
+ <h2>Enumerations</h2>
+ <div>
{{#Obj}}
+{{>EnumPartial}}
{{/Obj}}
- </div>
+ </div>
+ </section>
{{/Enums}}
- </nav>
- <div class="sidebar-resizer"></div>
- <main>
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc
- ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
- nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
- nec purus nec nunc ultricies ultricies. Nullam nec purus nec nunc
- ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
- nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
- </main>
- </body>
-</html>
\ No newline at end of file
+ {{#Typedefs}}
+ <section id="Typedefs" class="section-container">
+ <h2>Typedefs</h2>
+ <div>
+ {{#Obj}}
+ {{>TypedefPartial}}
+ {{/Obj}}
+ </div>
+ </section>
+ {{/Typedefs}}
+ </div>
+ </div>
+</main>
\ No newline at end of file
diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
index 6e28aff09f9d95..d640f4065bd6f3 100644
--- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
@@ -118,7 +118,7 @@ void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual) {
CheckBaseInfo(Expected, Actual);
EXPECT_EQ(Expected->DefLoc.has_value(), Actual->DefLoc.has_value());
if (Expected->DefLoc && Actual->DefLoc.has_value()) {
- EXPECT_EQ(Expected->DefLoc->LineNumber, Actual->DefLoc->LineNumber);
+ EXPECT_EQ(Expected->DefLoc->StartLineNumber, Actual->DefLoc->StartLineNumber);
EXPECT_EQ(Expected->DefLoc->Filename, Actual->DefLoc->Filename);
}
ASSERT_EQ(Expected->Loc.size(), Actual->Loc.size());
More information about the cfe-commits
mailing list