[clang-doc] Add a breadcrumb navigation bar (#173297)
This patch adds a breadcrumb navigation bar to the `<navbar>` element. Now, you can navigate between the different scopes of a record or namespace. This is done by keeping track of a Decl's parent Decl through its USR. That allows us to traverse the set of `Info`s through a directed graph during JSON generation to create `Context`s. A context is just a `Reference` that holds a relative path to a scope's file from a particular `Info`.
This commit is contained in:
parent
4998280c3f
commit
abee8a8e13
@ -159,6 +159,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
|
||||
return decodeRecord(R, I->Name, Blob);
|
||||
case NAMESPACE_PATH:
|
||||
return decodeRecord(R, I->Path, Blob);
|
||||
case NAMESPACE_PARENT_USR:
|
||||
return decodeRecord(R, I->ParentUSR, Blob);
|
||||
default:
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"invalid field for NamespaceInfo");
|
||||
@ -184,6 +186,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
|
||||
return decodeRecord(R, I->IsTypeDef, Blob);
|
||||
case RECORD_MANGLED_NAME:
|
||||
return decodeRecord(R, I->MangledName, Blob);
|
||||
case RECORD_PARENT_USR:
|
||||
return decodeRecord(R, I->ParentUSR, Blob);
|
||||
default:
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"invalid field for RecordInfo");
|
||||
|
||||
@ -174,6 +174,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
|
||||
{NAMESPACE_USR, {"USR", &genSymbolIdAbbrev}},
|
||||
{NAMESPACE_NAME, {"Name", &genStringAbbrev}},
|
||||
{NAMESPACE_PATH, {"Path", &genStringAbbrev}},
|
||||
{NAMESPACE_PARENT_USR, {"ParentUSR", &genSymbolIdAbbrev}},
|
||||
{ENUM_USR, {"USR", &genSymbolIdAbbrev}},
|
||||
{ENUM_NAME, {"Name", &genStringAbbrev}},
|
||||
{ENUM_DEFLOCATION, {"DefLocation", &genLocationAbbrev}},
|
||||
@ -190,6 +191,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
|
||||
{RECORD_TAG_TYPE, {"TagType", &genIntAbbrev}},
|
||||
{RECORD_IS_TYPE_DEF, {"IsTypeDef", &genBoolAbbrev}},
|
||||
{RECORD_MANGLED_NAME, {"MangledName", &genStringAbbrev}},
|
||||
{RECORD_PARENT_USR, {"ParentUSR", &genSymbolIdAbbrev}},
|
||||
{BASE_RECORD_USR, {"USR", &genSymbolIdAbbrev}},
|
||||
{BASE_RECORD_NAME, {"Name", &genStringAbbrev}},
|
||||
{BASE_RECORD_PATH, {"Path", &genStringAbbrev}},
|
||||
@ -270,12 +272,12 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
|
||||
{TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}},
|
||||
// Namespace Block
|
||||
{BI_NAMESPACE_BLOCK_ID,
|
||||
{NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
|
||||
{NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH, NAMESPACE_PARENT_USR}},
|
||||
// Record Block
|
||||
{BI_RECORD_BLOCK_ID,
|
||||
{RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION,
|
||||
RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF,
|
||||
RECORD_MANGLED_NAME}},
|
||||
RECORD_MANGLED_NAME, RECORD_PARENT_USR}},
|
||||
// BaseRecord Block
|
||||
{BI_BASE_RECORD_BLOCK_ID,
|
||||
{BASE_RECORD_USR, BASE_RECORD_NAME, BASE_RECORD_PATH,
|
||||
@ -570,6 +572,7 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) {
|
||||
emitRecord(I.USR, NAMESPACE_USR);
|
||||
emitRecord(I.Name, NAMESPACE_NAME);
|
||||
emitRecord(I.Path, NAMESPACE_PATH);
|
||||
emitRecord(I.ParentUSR, NAMESPACE_PARENT_USR);
|
||||
for (const auto &N : I.Namespace)
|
||||
emitBlock(N, FieldId::F_namespace);
|
||||
for (const auto &CI : I.Description)
|
||||
@ -624,6 +627,7 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
|
||||
emitRecord(I.Name, RECORD_NAME);
|
||||
emitRecord(I.Path, RECORD_PATH);
|
||||
emitRecord(I.MangledName, RECORD_MANGLED_NAME);
|
||||
emitRecord(I.ParentUSR, RECORD_PARENT_USR);
|
||||
for (const auto &N : I.Namespace)
|
||||
emitBlock(N, FieldId::F_namespace);
|
||||
for (const auto &CI : I.Description)
|
||||
|
||||
@ -108,6 +108,7 @@ enum RecordId {
|
||||
NAMESPACE_USR,
|
||||
NAMESPACE_NAME,
|
||||
NAMESPACE_PATH,
|
||||
NAMESPACE_PARENT_USR,
|
||||
ENUM_USR,
|
||||
ENUM_NAME,
|
||||
ENUM_DEFLOCATION,
|
||||
@ -124,6 +125,7 @@ enum RecordId {
|
||||
RECORD_TAG_TYPE,
|
||||
RECORD_IS_TYPE_DEF,
|
||||
RECORD_MANGLED_NAME,
|
||||
RECORD_PARENT_USR,
|
||||
BASE_RECORD_USR,
|
||||
BASE_RECORD_NAME,
|
||||
BASE_RECORD_PATH,
|
||||
|
||||
@ -278,6 +278,64 @@ static Object serializeComment(const CommentInfo &I, Object &Description) {
|
||||
llvm_unreachable("Unknown comment kind encountered.");
|
||||
}
|
||||
|
||||
/// Creates Contexts for namespaces and records to allow for navigation.
|
||||
static void generateContext(const Info &I, Object &Obj) {
|
||||
json::Value ContextArray = json::Array();
|
||||
auto &ContextArrayRef = *ContextArray.getAsArray();
|
||||
ContextArrayRef.reserve(I.Contexts.size());
|
||||
|
||||
std::string CurrentRelativePath;
|
||||
bool PreviousRecord = false;
|
||||
for (const auto &Current : I.Contexts) {
|
||||
json::Value ContextVal = Object();
|
||||
Object &Context = *ContextVal.getAsObject();
|
||||
serializeReference(Current, Context);
|
||||
|
||||
if (ContextArrayRef.empty() && I.IT == InfoType::IT_record) {
|
||||
if (Current.DocumentationFileName == "index") {
|
||||
// If the record's immediate context is a namespace, then the
|
||||
// "index.html" is in the same directory.
|
||||
PreviousRecord = false;
|
||||
Context["RelativePath"] = "./";
|
||||
} else {
|
||||
// If the immediate context is a record, then the file is one level
|
||||
// above
|
||||
PreviousRecord = true;
|
||||
CurrentRelativePath += "../";
|
||||
Context["RelativePath"] = CurrentRelativePath;
|
||||
}
|
||||
ContextArrayRef.push_back(ContextVal);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PreviousRecord && (Current.DocumentationFileName == "index")) {
|
||||
// If the previous Context was a record then we already went up a level,
|
||||
// so the current namespace index is in the same directory.
|
||||
PreviousRecord = false;
|
||||
} else if (Current.DocumentationFileName != "index") {
|
||||
// If the current Context is a record but the previous wasn't a record,
|
||||
// then the namespace index is located one level above.
|
||||
PreviousRecord = true;
|
||||
CurrentRelativePath += "../";
|
||||
} else {
|
||||
// The current Context is a namespace and so was the previous Context.
|
||||
PreviousRecord = false;
|
||||
CurrentRelativePath += "../";
|
||||
// If this namespace is the global namespace, then its documentation
|
||||
// name needs to be changed to link correctly.
|
||||
if (Current.QualName == "GlobalNamespace" && Current.RelativePath != "./")
|
||||
Context["DocumentationFileName"] =
|
||||
SmallString<16>("GlobalNamespace/index");
|
||||
}
|
||||
Context["RelativePath"] = CurrentRelativePath;
|
||||
ContextArrayRef.insert(ContextArrayRef.begin(), ContextVal);
|
||||
}
|
||||
|
||||
ContextArrayRef.back().getAsObject()->insert({"End", true});
|
||||
Obj["Contexts"] = ContextArray;
|
||||
Obj["HasContexts"] = true;
|
||||
}
|
||||
|
||||
static void
|
||||
serializeCommonAttributes(const Info &I, json::Object &Obj,
|
||||
const std::optional<StringRef> RepositoryUrl) {
|
||||
@ -323,6 +381,9 @@ serializeCommonAttributes(const Info &I, json::Object &Obj,
|
||||
Obj["Location"] =
|
||||
serializeLocation(Symbol->DefLoc.value(), RepositoryUrl);
|
||||
}
|
||||
|
||||
if (!I.Contexts.empty())
|
||||
generateContext(I, Obj);
|
||||
}
|
||||
|
||||
static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
|
||||
@ -335,7 +396,7 @@ static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
|
||||
|
||||
// If the reference is a nested class it will be put into a folder named
|
||||
// after the parent class. We can get that name from the path's stem.
|
||||
if (Ref.Path != "GlobalNamespace")
|
||||
if (Ref.Path != "GlobalNamespace" && !Ref.Path.empty())
|
||||
ReferenceObj["PathStem"] = sys::path::stem(Ref.Path);
|
||||
}
|
||||
}
|
||||
@ -730,6 +791,29 @@ static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
static void serializeContexts(Info *I,
|
||||
StringMap<std::unique_ptr<Info>> &Infos) {
|
||||
if (I->USR == GlobalNamespaceID)
|
||||
return;
|
||||
auto ParentUSR = I->ParentUSR;
|
||||
|
||||
while (true) {
|
||||
auto &ParentInfo = Infos.at(llvm::toHex(ParentUSR));
|
||||
|
||||
if (ParentInfo && ParentInfo->USR == GlobalNamespaceID) {
|
||||
Context GlobalRef(ParentInfo->USR, "Global Namespace",
|
||||
InfoType::IT_namespace, "GlobalNamespace", "",
|
||||
SmallString<16>("index"));
|
||||
I->Contexts.push_back(GlobalRef);
|
||||
return;
|
||||
}
|
||||
|
||||
Context ParentRef(*ParentInfo);
|
||||
I->Contexts.push_back(ParentRef);
|
||||
ParentUSR = ParentInfo->ParentUSR;
|
||||
}
|
||||
}
|
||||
|
||||
Error JSONGenerator::generateDocumentation(
|
||||
StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
|
||||
const ClangDocContext &CDCtx, std::string DirName) {
|
||||
@ -763,9 +847,12 @@ Error JSONGenerator::generateDocumentation(
|
||||
if (FileErr)
|
||||
return createFileError("cannot open file " + Group.getKey(), FileErr);
|
||||
|
||||
for (const auto &Info : Group.getValue())
|
||||
for (const auto &Info : Group.getValue()) {
|
||||
if (Info->IT == InfoType::IT_record || Info->IT == InfoType::IT_namespace)
|
||||
serializeContexts(Info, Infos);
|
||||
if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
|
||||
return Err;
|
||||
}
|
||||
}
|
||||
|
||||
return serializeIndex(CDCtx, RootDir);
|
||||
|
||||
@ -273,6 +273,10 @@ void Info::mergeBase(Info &&Other) {
|
||||
llvm::sort(Description);
|
||||
auto Last = llvm::unique(Description);
|
||||
Description.erase(Last, Description.end());
|
||||
if (ParentUSR == EmptySID)
|
||||
ParentUSR = Other.ParentUSR;
|
||||
if (DocumentationFileName.empty())
|
||||
DocumentationFileName = Other.DocumentationFileName;
|
||||
}
|
||||
|
||||
bool Info::mergeable(const Info &Other) {
|
||||
|
||||
@ -165,6 +165,16 @@ struct Reference {
|
||||
SmallString<16> DocumentationFileName;
|
||||
};
|
||||
|
||||
// A Context is a reference that holds a relative path from a certain Info's
|
||||
// location.
|
||||
struct Context : public Reference {
|
||||
Context(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName,
|
||||
StringRef Path, SmallString<16> DocumentationFileName)
|
||||
: Reference(USR, Name, IT, QualName, Path, DocumentationFileName) {}
|
||||
explicit Context(const Info &I);
|
||||
SmallString<128> RelativePath;
|
||||
};
|
||||
|
||||
// Holds the children of a record or namespace.
|
||||
struct ScopeChildren {
|
||||
// Namespaces and Records are references because they will be properly
|
||||
@ -356,13 +366,21 @@ struct Info {
|
||||
// Unique identifier for the decl described by this Info.
|
||||
SymbolID USR = SymbolID();
|
||||
|
||||
// Currently only used for namespaces and records.
|
||||
SymbolID ParentUSR = SymbolID();
|
||||
|
||||
// InfoType of this particular Info.
|
||||
InfoType IT = InfoType::IT_default;
|
||||
|
||||
// Comment description of this decl.
|
||||
std::vector<CommentInfo> Description;
|
||||
|
||||
SmallVector<Context, 4> Contexts;
|
||||
};
|
||||
|
||||
inline Context::Context(const Info &I)
|
||||
: Reference(I.USR, I.Name, I.IT, I.Name, I.Path, I.DocumentationFileName) {}
|
||||
|
||||
// Info for namespaces.
|
||||
struct NamespaceInfo : public Info {
|
||||
NamespaceInfo(SymbolID USR = SymbolID(), StringRef Name = StringRef(),
|
||||
|
||||
@ -697,10 +697,46 @@ static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,
|
||||
return TemplateParamInfo(Str);
|
||||
}
|
||||
|
||||
// Check if the DeclKind is one for which we support contextual relationships.
|
||||
// There might be other ContextDecls, like blocks, that we currently don't
|
||||
// handle at all.
|
||||
static bool isSupportedContext(Decl::Kind DeclKind) {
|
||||
switch (DeclKind) {
|
||||
case Decl::Kind::Record:
|
||||
case Decl::Kind::CXXRecord:
|
||||
case Decl::Kind::ClassTemplateSpecialization:
|
||||
case Decl::Kind::ClassTemplatePartialSpecialization:
|
||||
case Decl::Kind::Namespace:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void findParent(Info &I, const Decl *D) {
|
||||
assert(D && "Invalid Decl");
|
||||
|
||||
// Only walk up contexts if D is a record or namespace.
|
||||
if (!isSupportedContext(D->getKind()))
|
||||
return;
|
||||
|
||||
const DeclContext *ParentCtx = dyn_cast<DeclContext>(D)->getLexicalParent();
|
||||
while (ParentCtx) {
|
||||
if (isSupportedContext(ParentCtx->getDeclKind())) {
|
||||
// Break when we reach the first record or namespace.
|
||||
I.ParentUSR = getUSRForDecl(dyn_cast<Decl>(ParentCtx));
|
||||
break;
|
||||
}
|
||||
ParentCtx = ParentCtx->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void populateInfo(Info &I, const T *D, const FullComment *C,
|
||||
bool &IsInAnonymousNamespace) {
|
||||
I.USR = getUSRForDecl(D);
|
||||
findParent(I, D);
|
||||
|
||||
if (auto ConversionDecl = dyn_cast_or_null<CXXConversionDecl>(D);
|
||||
ConversionDecl && ConversionDecl->getConversionType()
|
||||
.getTypePtr()
|
||||
|
||||
@ -112,7 +112,6 @@ body, html {
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 60px; /* Adjust as needed */
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -255,6 +254,38 @@ body, html {
|
||||
color:var(--text1)
|
||||
}
|
||||
|
||||
.navbar-breadcrumb-container {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: var(--surface2);
|
||||
padding: 0.5rem 1rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
border-bottom: 1px solid var(--text2);
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid var(--text2);
|
||||
border-bottom: 1px solid var(--text2);
|
||||
}
|
||||
|
||||
.navbar-breadcrumb-item {
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: var(--surface1);
|
||||
border: 1px solid var(--text2);
|
||||
border-radius: 4px;
|
||||
color: var(--text1);
|
||||
font-size: 0.9rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.navbar-breadcrumb-item:hover {
|
||||
background: var(--brand);
|
||||
color: var(--text1-inverse);
|
||||
border-color: var(--brand);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hero__container {
|
||||
margin-top:1rem;
|
||||
display:flex;
|
||||
@ -317,9 +348,7 @@ body, html {
|
||||
max-width: 2048px;
|
||||
margin-left:auto;
|
||||
margin-right:auto;
|
||||
margin-top:0;
|
||||
margin-bottom: 1rem;
|
||||
padding:1rem 2rem
|
||||
}
|
||||
|
||||
@media(max-width:768px) {
|
||||
@ -404,9 +433,9 @@ body, html {
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
top: 60px;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
background-color: var(--surface1);
|
||||
display: flex;
|
||||
@ -414,6 +443,7 @@ body, html {
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar h2 {
|
||||
@ -445,8 +475,8 @@ body, html {
|
||||
|
||||
/* Content */
|
||||
.content {
|
||||
top: 60px;
|
||||
background-color: var(--text1-inverse);
|
||||
padding: 20px;
|
||||
left: 250px;
|
||||
position: relative;
|
||||
width: calc(100% - 250px);
|
||||
|
||||
@ -12,5 +12,12 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{{#HasContexts}}
|
||||
<div class="navbar-breadcrumb-container">
|
||||
{{#Contexts}}
|
||||
<a href="{{RelativePath}}{{DocumentationFileName}}.html"><div class="navbar-breadcrumb-item">{{Name}}</div></a>{{^End}}::{{/End}}
|
||||
{{/Contexts}}
|
||||
</div>
|
||||
{{/HasContexts}}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@ -29,6 +29,9 @@ HTML-SHAPE: <a href="../index.html" class="navbar__link">Hom
|
||||
HTML-SHAPE: </li>
|
||||
HTML-SHAPE: </ul>
|
||||
HTML-SHAPE: </div>
|
||||
HTML-SHAPE: <div class="navbar-breadcrumb-container">
|
||||
HTML-SHAPE: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
|
||||
HTML-SHAPE: </div>
|
||||
HTML-SHAPE: </div>
|
||||
HTML-SHAPE: </nav>
|
||||
HTML-SHAPE: <main>
|
||||
@ -136,6 +139,9 @@ HTML-CALC: <a href="../index.html" class="navbar__link">Home
|
||||
HTML-CALC: </li>
|
||||
HTML-CALC: </ul>
|
||||
HTML-CALC: </div>
|
||||
HTML-CALC: <div class="navbar-breadcrumb-container">
|
||||
HTML-CALC: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
|
||||
HTML-CALC: </div>
|
||||
HTML-CALC: </div>
|
||||
HTML-CALC: </nav>
|
||||
HTML-CALC: <main>
|
||||
@ -347,6 +353,9 @@ HTML-RECTANGLE: <a href="../index.html" class="navbar__link"
|
||||
HTML-RECTANGLE: </li>
|
||||
HTML-RECTANGLE: </ul>
|
||||
HTML-RECTANGLE: </div>
|
||||
HTML-RECTANGLE: <div class="navbar-breadcrumb-container">
|
||||
HTML-RECTANGLE: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
|
||||
HTML-RECTANGLE: </div>
|
||||
HTML-RECTANGLE: </div>
|
||||
HTML-RECTANGLE: </nav>
|
||||
HTML-RECTANGLE: <main>
|
||||
@ -458,6 +467,9 @@ HTML-CIRCLE: <a href="../index.html" class="navbar__link">Ho
|
||||
HTML-CIRCLE: </li>
|
||||
HTML-CIRCLE: </ul>
|
||||
HTML-CIRCLE: </div>
|
||||
HTML-CIRCLE: <div class="navbar-breadcrumb-container">
|
||||
HTML-CIRCLE: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
|
||||
HTML-CIRCLE: </div>
|
||||
HTML-CIRCLE: </div>
|
||||
HTML-CIRCLE: </nav>
|
||||
HTML-CIRCLE: <main>
|
||||
|
||||
@ -38,6 +38,16 @@ private:
|
||||
};
|
||||
|
||||
// CHECK: {
|
||||
// CHECK-NEXT: "Contexts": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "DocumentationFileName": "index",
|
||||
// CHECK-NEXT: "End": true,
|
||||
// CHECK-NEXT: "Name": "Global Namespace",
|
||||
// CHECK-NEXT: "QualName": "GlobalNamespace",
|
||||
// CHECK-NEXT: "RelativePath": "./",
|
||||
// CHECK-NEXT: "USR": "0000000000000000000000000000000000000000"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ],
|
||||
// CHECK-NEXT: "Description": {
|
||||
// CHECK-NEXT: "BriefComments": [
|
||||
// CHECK-NEXT: [
|
||||
@ -156,6 +166,7 @@ private:
|
||||
// CHECK-NEXT: "USR": "0000000000000000000000000000000000000000"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ],
|
||||
// CHECK-NEXT: "HasContexts": true,
|
||||
// CHECK-NEXT: "HasEnums": true,
|
||||
// CHECK-NEXT: "HasFriends": true,
|
||||
// CHECK-NEXT: "HasPrivateMembers": true,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: clang-doc --output=%t --format=html --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/json/nested/index.json --check-prefix=NESTED
|
||||
// RUN: FileCheck %s < %t/json/nested/inner/index.json --check-prefix=INNER
|
||||
|
||||
@ -7,6 +8,7 @@ namespace nested {
|
||||
int Global;
|
||||
namespace inner {
|
||||
int InnerGlobal;
|
||||
namespace inner_inner {}
|
||||
} // namespace inner
|
||||
} // namespace nested
|
||||
|
||||
@ -17,7 +19,7 @@ namespace nested {
|
||||
// NESTED-NEXT: "IsStatic": false,
|
||||
// NESTED-NEXT: "Location": {
|
||||
// NESTED-NEXT: "Filename": "{{.*}}nested-namespace.cpp",
|
||||
// NESTED-NEXT: "LineNumber": 7
|
||||
// NESTED-NEXT: "LineNumber": 8
|
||||
// NESTED-NEXT: },
|
||||
// NESTED-NEXT: "Name": "Global",
|
||||
// NESTED-NEXT: "Namespace": [
|
||||
@ -31,7 +33,7 @@ namespace nested {
|
||||
// INNER-NEXT: "IsStatic": false,
|
||||
// INNER-NEXT: "Location": {
|
||||
// INNER-NEXT: "Filename": "{{.*}}nested-namespace.cpp",
|
||||
// INNER-NEXT: "LineNumber": 9
|
||||
// INNER-NEXT: "LineNumber": 10
|
||||
// INNER-NEXT: },
|
||||
// INNER-NEXT: "Name": "InnerGlobal",
|
||||
// INNER-NEXT: "Namespace": [
|
||||
|
||||
@ -63,6 +63,9 @@ class AnonClass {};
|
||||
// MD-ANON-INDEX: ### anonFunction
|
||||
// MD-ANON-INDEX: *void anonFunction()*
|
||||
|
||||
// HTML-ANON-INDEX: <div class="navbar-breadcrumb-container">
|
||||
// HTML-ANON-INDEX: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
|
||||
// HTML-ANON-INDEX: </div>
|
||||
// HTML-ANON-INDEX: <h2>@nonymous_namespace</h2>
|
||||
// HTML-ANON-INDEX: <h2>Inner Classes</h2>
|
||||
// HTML-ANON-INDEX: <ul class="class-container">
|
||||
@ -90,6 +93,10 @@ class ClassInPrimaryNamespace {};
|
||||
// MD-PRIMARY-CLASS: # class ClassInPrimaryNamespace
|
||||
// MD-PRIMARY-CLASS: Class in PrimaryNamespace
|
||||
|
||||
// HTML-PRIMARY-CLASS: <div class="navbar-breadcrumb-container">
|
||||
// HTML-PRIMARY-CLASS: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
|
||||
// HTML-PRIMARY-CLASS: <a href="./index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>
|
||||
// HTML-PRIMARY-CLASS: </div>
|
||||
// HTML-PRIMARY-CLASS: <h1 class="hero__title-large">class ClassInPrimaryNamespace</h1>
|
||||
|
||||
// Nested namespace
|
||||
@ -107,6 +114,11 @@ class ClassInNestedNamespace {};
|
||||
// MD-NESTED-CLASS: # class ClassInNestedNamespace
|
||||
// MD-NESTED-CLASS: Class in NestedNamespace
|
||||
|
||||
// HTML-NESTED-CLASS: <div class="navbar-breadcrumb-container">
|
||||
// HTML-NESTED-CLASS: <a href="../../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
|
||||
// HTML-NESTED-CLASS: <a href="../index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>::
|
||||
// HTML-NESTED-CLASS: <a href="./index.html"><div class="navbar-breadcrumb-item">NestedNamespace</div></a>
|
||||
// HTML-NESTED-CLASS: </div>
|
||||
// HTML-NESTED-CLASS: <h1 class="hero__title-large">class ClassInNestedNamespace</h1>
|
||||
} // namespace NestedNamespace
|
||||
|
||||
@ -119,6 +131,10 @@ class ClassInNestedNamespace {};
|
||||
// MD-NESTED-INDEX: *void functionInNestedNamespace()*
|
||||
// MD-NESTED-INDEX: Function in NestedNamespace
|
||||
|
||||
// HTML-NESTED-INDEX: <div class="navbar-breadcrumb-container">
|
||||
// HTML-NESTED-INDEX: <a href="../../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
|
||||
// HTML-NESTED-INDEX: <a href="../index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>
|
||||
// HTML-NESTED-INDEX: </div>
|
||||
// HTML-NESTED-INDEX: <h2>NestedNamespace</h2>
|
||||
// HTML-NESTED-INDEX: <h2>Inner Classes</h2>
|
||||
// HTML-NESTED-INDEX: <ul class="class-container">
|
||||
@ -134,7 +150,7 @@ class ClassInNestedNamespace {};
|
||||
// HTML-NESTED-INDEX: <p> Function in NestedNamespace</p>
|
||||
// HTML-NESTED-INDEX: </div>
|
||||
// HTML-NESTED-INDEX: </div>
|
||||
// HTML-NESTED-INDEX: <p>Defined at line 98 of file {{.*}}namespace.cpp</p>
|
||||
// HTML-NESTED-INDEX: <p>Defined at line 105 of file {{.*}}namespace.cpp</p>
|
||||
// HTML-NESTED-INDEX: </div>
|
||||
} // namespace PrimaryNamespace
|
||||
|
||||
@ -149,6 +165,9 @@ class ClassInNestedNamespace {};
|
||||
// MD-PRIMARY-INDEX: *void functionInPrimaryNamespace()*
|
||||
// MD-PRIMARY-INDEX: Function in PrimaryNamespace
|
||||
|
||||
// HTML-PRIMARY-INDEX: <div class="navbar-breadcrumb-container">
|
||||
// HTML-PRIMARY-INDEX: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
|
||||
// HTML-PRIMARY-INDEX: </div>
|
||||
// HTML-PRIMARY-INDEX: <h2>PrimaryNamespace</h2>
|
||||
// HTML-PRIMARY-INDEX-NOT: <h2 id="Namespaces">Namespaces</h2>
|
||||
// HTML-PRIMARY-INDEX-NOT: <a href="NestedNamespace{{[\/]}}index.html">NestedNamespace</a>
|
||||
@ -166,7 +185,7 @@ class ClassInNestedNamespace {};
|
||||
// HTML-PRIMARY-INDEX: <p> Function in PrimaryNamespace</p>
|
||||
// HTML-PRIMARY-INDEX: </div>
|
||||
// HTML-PRIMARY-INDEX: </div>
|
||||
// HTML-PRIMARY-INDEX: <p>Defined at line 81 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
|
||||
// HTML-PRIMARY-INDEX: <p>Defined at line 84 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
|
||||
// HTML-PRIMARY-INDEX: </div>
|
||||
// AnotherNamespace
|
||||
namespace AnotherNamespace {
|
||||
@ -183,6 +202,10 @@ class ClassInAnotherNamespace {};
|
||||
// MD-ANOTHER-CLASS: # class ClassInAnotherNamespace
|
||||
// MD-ANOTHER-CLASS: Class in AnotherNamespace
|
||||
|
||||
// HTML-ANOTHER-CLASS: <div class="navbar-breadcrumb-container">
|
||||
// HTML-ANOTHER-CLASS: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
|
||||
// HTML-ANOTHER-CLASS: <a href="./index.html"><div class="navbar-breadcrumb-item">AnotherNamespace</div></a>
|
||||
// HTML-ANOTHER-CLASS: </div>
|
||||
// HTML-ANOTHER-CLASS: <h1 class="hero__title-large">class ClassInAnotherNamespace</h1>
|
||||
|
||||
} // namespace AnotherNamespace
|
||||
@ -196,6 +219,9 @@ class ClassInAnotherNamespace {};
|
||||
// MD-ANOTHER-INDEX: *void functionInAnotherNamespace()*
|
||||
// MD-ANOTHER-INDEX: Function in AnotherNamespace
|
||||
|
||||
// HTML-ANOTHER-INDEX: <div class="navbar-breadcrumb-container">
|
||||
// HTML-ANOTHER-INDEX: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
|
||||
// HTML-ANOTHER-INDEX: </div>
|
||||
// HTML-ANOTHER-INDEX: <h2>AnotherNamespace</h2>
|
||||
// HTML-ANOTHER-INDEX: <h2>Inner Classes</h2>
|
||||
// HTML-ANOTHER-INDEX: <ul class="class-container">
|
||||
@ -211,7 +237,7 @@ class ClassInAnotherNamespace {};
|
||||
// HTML-ANOTHER-INDEX: <p> Function in AnotherNamespace</p>
|
||||
// HTML-ANOTHER-INDEX: </div>
|
||||
// HTML-ANOTHER-INDEX: </div>
|
||||
// HTML-ANOTHER-INDEX: <p>Defined at line 174 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
|
||||
// HTML-ANOTHER-INDEX: <p>Defined at line 193 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
|
||||
// HTML-ANOTHER-INDEX: </div>
|
||||
// HTML-ANOTHER-INDEX: </div>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user