-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang-doc] integrate JSON generator with Mustache templates #149006
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
9f05063 to
f88e7bc
Compare
|
@llvm/pr-subscribers-clang-tools-extra Author: Erick Velez (evelez7) ChangesThis patch changes the HTML Mustache generator to use the JSON generator Patch is 85.75 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149006.diff 26 Files Affected:
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index dce34a8434ff8..4efbbd34730cf 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -384,6 +384,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
return decodeRecord(R, I->Path, Blob);
case REFERENCE_FIELD:
return decodeRecord(R, F, Blob);
+ case REFERENCE_FILE:
+ return decodeRecord(R, I->DocumentationFileName, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for Reference");
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index eed23726e17bf..e23511bf63690 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -210,6 +210,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{REFERENCE_TYPE, {"RefType", &genIntAbbrev}},
{REFERENCE_PATH, {"Path", &genStringAbbrev}},
{REFERENCE_FIELD, {"Field", &genIntAbbrev}},
+ {REFERENCE_FILE, {"File", &genStringAbbrev}},
{TEMPLATE_PARAM_CONTENTS, {"Contents", &genStringAbbrev}},
{TEMPLATE_SPECIALIZATION_OF,
{"SpecializationOf", &genSymbolIdAbbrev}},
@@ -286,7 +287,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
// Reference Block
{BI_REFERENCE_BLOCK_ID,
{REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE,
- REFERENCE_PATH, REFERENCE_FIELD}},
+ REFERENCE_PATH, REFERENCE_FIELD, REFERENCE_FILE}},
// Template Blocks.
{BI_TEMPLATE_BLOCK_ID, {}},
{BI_TEMPLATE_PARAM_BLOCK_ID, {TEMPLATE_PARAM_CONTENTS}},
@@ -479,6 +480,7 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
emitRecord((unsigned)R.RefType, REFERENCE_TYPE);
emitRecord(R.Path, REFERENCE_PATH);
emitRecord((unsigned)Field, REFERENCE_FIELD);
+ emitRecord(R.DocumentationFileName, REFERENCE_FILE);
}
void ClangDocBitcodeWriter::emitBlock(const FriendInfo &R) {
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 501af12582a8e..688f886b45308 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -140,6 +140,7 @@ enum RecordId {
REFERENCE_TYPE,
REFERENCE_PATH,
REFERENCE_FIELD,
+ REFERENCE_FILE,
TEMPLATE_PARAM_CONTENTS,
TEMPLATE_SPECIALIZATION_OF,
TYPEDEF_USR,
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 7aeaa1b7cf67d..c640fb4fb613f 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -27,6 +27,9 @@ using namespace llvm::mustache;
namespace clang {
namespace doc {
+static Error generateDocForJSON(json::Value &JSON, StringRef Filename,
+ StringRef Path, raw_fd_ostream &OS,
+ const ClangDocContext &CDCtx);
static Error createFileOpenError(StringRef FileName, std::error_code EC) {
return createFileError("cannot open file " + FileName, EC);
@@ -132,404 +135,75 @@ Error MustacheHTMLGenerator::generateDocs(
return Err;
}
- // Track which directories we already tried to create.
- StringSet<> CreatedDirs;
- // Collect all output by file name and create the necessary directories.
- StringMap<std::vector<doc::Info *>> FileToInfos;
- for (const auto &Group : Infos) {
- llvm::TimeTraceScope TS("setup directories");
- doc::Info *Info = Group.getValue().get();
-
- SmallString<128> Path;
- sys::path::native(RootDir, Path);
- sys::path::append(Path, Info->getRelativeFilePath(""));
- if (!CreatedDirs.contains(Path)) {
- if (std::error_code EC = sys::fs::create_directories(Path))
- return createStringError(EC, "failed to create directory '%s'.",
- Path.c_str());
- CreatedDirs.insert(Path);
- }
+ {
+ llvm::TimeTraceScope TS("Generate JSON for Mustache");
+ if (auto JSONGenerator = findGeneratorByName("json")) {
+ if (Error Err = JSONGenerator.get()->generateDocs(
+ RootDir, std::move(Infos), CDCtx))
+ return Err;
+ } else
+ return JSONGenerator.takeError();
+ }
- sys::path::append(Path, Info->getFileBaseName() + ".html");
- FileToInfos[Path].push_back(Info);
+ StringMap<json::Value> JSONFileMap;
+ {
+ llvm::TimeTraceScope TS("Iterate JSON files");
+ std::error_code EC;
+ sys::fs::directory_iterator JSONIter(RootDir, EC);
+ std::vector<json::Value> JSONFiles;
+ JSONFiles.reserve(Infos.size());
+ if (EC)
+ return createStringError("Failed to create directory iterator.");
+
+ while (JSONIter != sys::fs::directory_iterator()) {
+ if (EC)
+ return createStringError(EC, "Failed to iterate directory");
+
+ auto Path = StringRef(JSONIter->path());
+ if (!Path.ends_with(".json")) {
+ JSONIter.increment(EC);
+ continue;
+ }
+
+ auto File = MemoryBuffer::getFile(Path);
+ if ((EC = File.getError()))
+ continue;
+
+ auto Parsed = json::parse((*File)->getBuffer());
+ if (!Parsed)
+ return Parsed.takeError();
+ ;
+ JSONFileMap.try_emplace(Path, *Parsed);
+ JSONIter.increment(EC);
+ }
}
{
llvm::TimeTraceScope TS("Generate Docs");
- for (const auto &Group : FileToInfos) {
+ for (const auto &File : JSONFileMap) {
llvm::TimeTraceScope TS("Info to Doc");
std::error_code FileErr;
- raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_None);
+ auto JSON = File.getValue();
+ auto Path = File.getKey().str();
+ SmallString<16> HTMLPath(Path.begin(), Path.end());
+ sys::path::replace_extension(HTMLPath, "html");
+ raw_fd_ostream InfoOS(HTMLPath, FileErr, sys::fs::OF_None);
if (FileErr)
- return createFileOpenError(Group.getKey(), FileErr);
+ return createFileOpenError(File.getKey(), FileErr);
- for (const auto &Info : Group.getValue())
- if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
- return Err;
+ if (Error Err = generateDocForJSON(JSON, sys::path::stem(HTMLPath),
+ HTMLPath, InfoOS, CDCtx))
+ return Err;
}
}
return Error::success();
}
-static json::Value
-extractValue(const Location &L,
- std::optional<StringRef> RepositoryUrl = std::nullopt) {
- Object Obj = Object();
- // TODO: Consider using both Start/End line numbers to improve location report
- Obj.insert({"LineNumber", L.StartLineNumber});
- Obj.insert({"Filename", L.Filename});
-
- if (!L.IsFileInRootDir || !RepositoryUrl)
- return Obj;
- SmallString<128> FileURL(*RepositoryUrl);
- sys::path::append(FileURL, sys::path::Style::posix, L.Filename);
- FileURL += "#" + std::to_string(L.StartLineNumber);
- Obj.insert({"FileURL", FileURL});
-
- return Obj;
-}
-
-static json::Value extractValue(const Reference &I,
- StringRef CurrentDirectory) {
- SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
- sys::path::append(Path, I.getFileBaseName() + ".html");
- sys::path::native(Path, sys::path::Style::posix);
- Object Obj = Object();
- Obj.insert({"Link", Path});
- Obj.insert({"Name", I.Name});
- Obj.insert({"QualName", I.QualName});
- Obj.insert({"ID", toHex(toStringRef(I.USR))});
- return Obj;
-}
-
-static json::Value extractValue(const TypedefInfo &I) {
- // Not Supported
- return nullptr;
-}
-
-static json::Value extractValue(const CommentInfo &I) {
- Object Obj = Object();
-
- json::Value ChildVal = Object();
- Object &Child = *ChildVal.getAsObject();
-
- json::Value ChildArr = Array();
- auto &CARef = *ChildArr.getAsArray();
- CARef.reserve(I.Children.size());
- for (const auto &C : I.Children)
- CARef.emplace_back(extractValue(*C));
-
- switch (I.Kind) {
- case CommentKind::CK_TextComment: {
- Obj.insert({commentKindToString(I.Kind), I.Text});
- return Obj;
- }
-
- case CommentKind::CK_BlockCommandComment: {
- Child.insert({"Command", I.Name});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_InlineCommandComment: {
- json::Value ArgsArr = Array();
- auto &ARef = *ArgsArr.getAsArray();
- ARef.reserve(I.Args.size());
- for (const auto &Arg : I.Args)
- ARef.emplace_back(Arg);
- Child.insert({"Command", I.Name});
- Child.insert({"Args", ArgsArr});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_ParamCommandComment:
- case CommentKind::CK_TParamCommandComment: {
- Child.insert({"ParamName", I.ParamName});
- Child.insert({"Direction", I.Direction});
- Child.insert({"Explicit", I.Explicit});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_VerbatimBlockComment: {
- Child.insert({"Text", I.Text});
- if (!I.CloseName.empty())
- Child.insert({"CloseName", I.CloseName});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_VerbatimBlockLineComment:
- case CommentKind::CK_VerbatimLineComment: {
- Child.insert({"Text", I.Text});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_HTMLStartTagComment: {
- json::Value AttrKeysArray = json::Array();
- json::Value AttrValuesArray = json::Array();
- auto &KeyArr = *AttrKeysArray.getAsArray();
- auto &ValArr = *AttrValuesArray.getAsArray();
- KeyArr.reserve(I.AttrKeys.size());
- ValArr.reserve(I.AttrValues.size());
- for (const auto &K : I.AttrKeys)
- KeyArr.emplace_back(K);
- for (const auto &V : I.AttrValues)
- ValArr.emplace_back(V);
- Child.insert({"Name", I.Name});
- Child.insert({"SelfClosing", I.SelfClosing});
- Child.insert({"AttrKeys", AttrKeysArray});
- Child.insert({"AttrValues", AttrValuesArray});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_HTMLEndTagComment: {
- Child.insert({"Name", I.Name});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_FullComment:
- case CommentKind::CK_ParagraphComment: {
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_Unknown: {
- Obj.insert({commentKindToString(I.Kind), I.Text});
- return Obj;
- }
- }
- llvm_unreachable("Unknown comment kind encountered.");
-}
-
-static void maybeInsertLocation(std::optional<Location> Loc,
- const ClangDocContext &CDCtx, Object &Obj) {
- if (!Loc)
- return;
- Location L = *Loc;
- Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)});
-}
-
-static void extractDescriptionFromInfo(ArrayRef<CommentInfo> Descriptions,
- json::Object &EnumValObj) {
- if (Descriptions.empty())
- return;
- json::Value DescArr = Array();
- json::Array &DescARef = *DescArr.getAsArray();
- DescARef.reserve(Descriptions.size());
- for (const CommentInfo &Child : Descriptions)
- DescARef.emplace_back(extractValue(Child));
- EnumValObj.insert({"EnumValueComments", DescArr});
-}
-
-static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
- const ClangDocContext &CDCtx) {
- Object Obj = Object();
- Obj.insert({"Name", I.Name});
- Obj.insert({"ID", toHex(toStringRef(I.USR))});
- Obj.insert({"Access", getAccessSpelling(I.Access).str()});
- Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
-
- json::Value ParamArr = Array();
- json::Array &ParamARef = *ParamArr.getAsArray();
- ParamARef.reserve(I.Params.size());
- for (const auto Val : enumerate(I.Params)) {
- json::Value V = Object();
- auto &VRef = *V.getAsObject();
- VRef.insert({"Name", Val.value().Name});
- VRef.insert({"Type", Val.value().Type.Name});
- VRef.insert({"End", Val.index() + 1 == I.Params.size()});
- ParamARef.emplace_back(V);
- }
- Obj.insert({"Params", ParamArr});
-
- maybeInsertLocation(I.DefLoc, CDCtx, Obj);
- return Obj;
-}
-
-static json::Value extractValue(const EnumInfo &I,
- const ClangDocContext &CDCtx) {
- Object Obj = Object();
- std::string EnumType = I.Scoped ? "enum class " : "enum ";
- EnumType += I.Name;
- bool HasComment = llvm::any_of(
- I.Members, [](const EnumValueInfo &M) { return !M.Description.empty(); });
- Obj.insert({"EnumName", EnumType});
- Obj.insert({"HasComment", HasComment});
- Obj.insert({"ID", toHex(toStringRef(I.USR))});
- json::Value EnumArr = Array();
- json::Array &EnumARef = *EnumArr.getAsArray();
- EnumARef.reserve(I.Members.size());
- for (const EnumValueInfo &M : I.Members) {
- json::Value EnumValue = Object();
- auto &EnumValObj = *EnumValue.getAsObject();
- EnumValObj.insert({"Name", M.Name});
- if (!M.ValueExpr.empty())
- EnumValObj.insert({"ValueExpr", M.ValueExpr});
- else
- EnumValObj.insert({"Value", M.Value});
-
- extractDescriptionFromInfo(M.Description, EnumValObj);
- EnumARef.emplace_back(EnumValue);
- }
- Obj.insert({"EnumValues", EnumArr});
-
- extractDescriptionFromInfo(I.Description, Obj);
- maybeInsertLocation(I.DefLoc, CDCtx, Obj);
-
- return Obj;
-}
-
-static void extractScopeChildren(const ScopeChildren &S, Object &Obj,
- StringRef ParentInfoDir,
- const ClangDocContext &CDCtx) {
- json::Value NamespaceArr = Array();
- json::Array &NamespaceARef = *NamespaceArr.getAsArray();
- NamespaceARef.reserve(S.Namespaces.size());
- for (const Reference &Child : S.Namespaces)
- NamespaceARef.emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!NamespaceARef.empty())
- Obj.insert({"Namespace", Object{{"Links", NamespaceArr}}});
-
- json::Value RecordArr = Array();
- json::Array &RecordARef = *RecordArr.getAsArray();
- RecordARef.reserve(S.Records.size());
- for (const Reference &Child : S.Records)
- RecordARef.emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!RecordARef.empty())
- Obj.insert({"Record", Object{{"Links", RecordArr}}});
-
- json::Value FunctionArr = Array();
- json::Array &FunctionARef = *FunctionArr.getAsArray();
- FunctionARef.reserve(S.Functions.size());
-
- json::Value PublicFunctionArr = Array();
- json::Array &PublicFunctionARef = *PublicFunctionArr.getAsArray();
- PublicFunctionARef.reserve(S.Functions.size());
-
- json::Value ProtectedFunctionArr = Array();
- json::Array &ProtectedFunctionARef = *ProtectedFunctionArr.getAsArray();
- ProtectedFunctionARef.reserve(S.Functions.size());
-
- for (const FunctionInfo &Child : S.Functions) {
- json::Value F = extractValue(Child, ParentInfoDir, CDCtx);
- AccessSpecifier Access = Child.Access;
- if (Access == AccessSpecifier::AS_public)
- PublicFunctionARef.emplace_back(F);
- else if (Access == AccessSpecifier::AS_protected)
- ProtectedFunctionARef.emplace_back(F);
- else
- FunctionARef.emplace_back(F);
- }
-
- if (!FunctionARef.empty())
- Obj.insert({"Function", Object{{"Obj", FunctionArr}}});
-
- if (!PublicFunctionARef.empty())
- Obj.insert({"PublicFunction", Object{{"Obj", PublicFunctionArr}}});
-
- if (!ProtectedFunctionARef.empty())
- Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunctionArr}}});
-
- json::Value EnumArr = Array();
- auto &EnumARef = *EnumArr.getAsArray();
- EnumARef.reserve(S.Enums.size());
- for (const EnumInfo &Child : S.Enums)
- EnumARef.emplace_back(extractValue(Child, CDCtx));
-
- if (!EnumARef.empty())
- Obj.insert({"Enums", Object{{"Obj", EnumArr}}});
-
- json::Value TypedefArr = Array();
- auto &TypedefARef = *TypedefArr.getAsArray();
- TypedefARef.reserve(S.Typedefs.size());
- for (const TypedefInfo &Child : S.Typedefs)
- TypedefARef.emplace_back(extractValue(Child));
-
- if (!TypedefARef.empty())
- Obj.insert({"Typedefs", Object{{"Obj", TypedefArr}}});
-}
-
-static json::Value extractValue(const NamespaceInfo &I,
- const ClangDocContext &CDCtx) {
- Object NamespaceValue = Object();
- std::string InfoTitle = I.Name.empty() ? "Global Namespace"
- : (Twine("namespace ") + I.Name).str();
-
- SmallString<64> BasePath = I.getRelativeFilePath("");
- NamespaceValue.insert({"NamespaceTitle", InfoTitle});
- NamespaceValue.insert({"NamespacePath", BasePath});
-
- extractDescriptionFromInfo(I.Description, NamespaceValue);
- extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
- return NamespaceValue;
-}
-
-static json::Value extractValue(const RecordInfo &I,
- const ClangDocContext &CDCtx) {
- Object RecordValue = Object();
- extractDescriptionFromInfo(I.Description, RecordValue);
- RecordValue.insert({"Name", I.Name});
- RecordValue.insert({"FullName", I.FullName});
- RecordValue.insert({"RecordType", getTagType(I.TagType)});
-
- maybeInsertLocation(I.DefLoc, CDCtx, RecordValue);
-
- SmallString<64> BasePath = I.getRelativeFilePath("");
- extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
- json::Value PublicMembers = Array();
- json::Array &PubMemberRef = *PublicMembers.getAsArray();
- PubMemberRef.reserve(I.Members.size());
- json::Value ProtectedMembers = Array();
- json::Array &ProtMemberRef = *ProtectedMembers.getAsArray();
- ProtMemberRef.reserve(I.Members.size());
- json::Value PrivateMembers = Array();
- json::Array &PrivMemberRef = *PrivateMembers.getAsArray();
- PrivMemberRef.reserve(I.Members.size());
- for (const MemberTypeInfo &Member : I.Members) {
- json::Value MemberValue = Object();
- auto &MVRef = *MemberValue.getAsObject();
- MVRef.insert({"Name", Member.Name});
- MVRef.insert({"Type", Member.Type.Name});
- extractDescriptionFromInfo(Member.Description, MVRef);
-
- if (Member.Access == AccessSpecifier::AS_public)
- PubMemberRef.emplace_back(MemberValue);
- else if (Member.Access == AccessSpecifier::AS_protected)
- ProtMemberRef.emplace_back(MemberValue);
- else if (Member.Access == AccessSpecifier::AS_private)
- ProtMemberRef.emplace_back(MemberValue);
- }
- if (!PubMemberRef.empty())
- RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
- if (!ProtMemberRef.empty())
- RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
- if (!PrivMemberRef.empty())
- RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
-
- return RecordValue;
-}
-
static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
- Info *I) {
+ StringRef JSONPath) {
V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
json::Value StylesheetArr = Array();
- auto InfoPath = I->getRelativeFilePath("");
- SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+ SmallString<128> RelativePath("./");
sys::path::native(RelativePath, sys::path::Style::posix);
auto *SSA = StylesheetArr.getAsArray();
@@ -555,38 +229,43 @@ static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
return Error::success();
}
-Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
- const ClangDocContext &CDCtx) {
- switch (I->IT) {
- case InfoType::IT_namespace: {
- json::Value V =
- extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- if (auto Err = setupTemplateValue(...
[truncated]
|
8d6dd85 to
f525329
Compare
cb2bf64 to
af831e0
Compare
|
Looking at the patch, I'm wondering if we can decouple the changes to output and add things like missing features in separate patches? There seems to be more changing than I expected in the template and tests. So if we can have a transition thats basically a NOP (or as close as we can get). and then fix up the template and tests after, I think that may help ease review and may make it easier to reason about changes in output. |
I do see a few unnecessary changes that I let slip pass, so I can eliminate those, but the output changes are mostly necessary to make the basic test pass. The JSON produced by the JSON and Mustache backends had big discrepancies. I added all the "Has*" booleans in JSON because Mustache had nested "Obj" arrays in all of its objects. So, for example, it would check for the existence of "PublicMembers" and then iterate over "Obj" in the template. Eliminating the "Has*" booleans means I'd actually have to add nested "Obj" arrays to all of the JSON objects and then change the JSON tests or else basic test fails. Or it would use "ID" as its key for the USR, where the JSON backend uses "USR". I'll look over it to see if I can eliminate any non-necessary stuff. |
af831e0 to
ae9f3eb
Compare
ae9f3eb to
6eba85a
Compare
| // CHECK-NEXT: <div> | ||
| // CHECK-NEXT: <pre> | ||
| // CHECK-NEXT: <code class="language-cpp code-clang-doc"> | ||
| // CHECK-NEXT: enum Color |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we'd want to drop this? Writing out that its an enum seems good, and IIRC was nicer in the generated HTML.
| // CHECK-NEXT: </table> | ||
| // CHECK-NEXT: <div> | ||
| // CHECK-NEXT: Defined at line 5 of file {{.*}}mustache-index.cpp | ||
| // CHECK: Defined at line 5 of file {{.*}}mustache-index.cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's in between if this isn't CHECK-NEXT? can we include those lines? is it 1-2 or many that are missing?
|
Is it possible to do all the JSON refactoring as the first PR, and then do the mustache integration after? I believe those can be split w/o too much trouble. |

This patch changes the HTML Mustache generator to use the JSON generator
and consume its output to generate its templates.
Changes were made to the Mustache templates to rectify mismatched block names. Instead of nesting arrays inside an "Obj" array in the JSON object, I opted for booleans in the object for more idiomatic templates. We also keep track of the physical filenames to maintain linking in documentation.