@@ -278,6 +278,64 @@ static Object serializeComment(const CommentInfo &I, Object &Description) {
278278 llvm_unreachable (" Unknown comment kind encountered." );
279279}
280280
281+ // / Creates Contexts for namespaces and records to allow for navigation.
282+ static void generateContext (const Info &I, Object &Obj) {
283+ json::Value ContextArray = json::Array ();
284+ auto &ContextArrayRef = *ContextArray.getAsArray ();
285+ ContextArrayRef.reserve (I.Contexts .size ());
286+
287+ std::string CurrentRelativePath;
288+ bool PreviousRecord = false ;
289+ for (const auto &Current : I.Contexts ) {
290+ json::Value ContextVal = Object ();
291+ Object &Context = *ContextVal.getAsObject ();
292+ serializeReference (Current, Context);
293+
294+ if (ContextArrayRef.empty () && I.IT == InfoType::IT_record) {
295+ if (Current.DocumentationFileName == " index" ) {
296+ // If the record's immediate context is a namespace, then the
297+ // "index.html" is in the same directory.
298+ PreviousRecord = false ;
299+ Context[" RelativePath" ] = " ./" ;
300+ } else {
301+ // If the immediate context is a record, then the file is one level
302+ // above
303+ PreviousRecord = true ;
304+ CurrentRelativePath += " ../" ;
305+ Context[" RelativePath" ] = CurrentRelativePath;
306+ }
307+ ContextArrayRef.push_back (ContextVal);
308+ continue ;
309+ }
310+
311+ if (PreviousRecord && (Current.DocumentationFileName == " index" )) {
312+ // If the previous Context was a record then we already went up a level,
313+ // so the current namespace index is in the same directory.
314+ PreviousRecord = false ;
315+ } else if (Current.DocumentationFileName != " index" ) {
316+ // If the current Context is a record but the previous wasn't a record,
317+ // then the namespace index is located one level above.
318+ PreviousRecord = true ;
319+ CurrentRelativePath += " ../" ;
320+ } else {
321+ // The current Context is a namespace and so was the previous Context.
322+ PreviousRecord = false ;
323+ CurrentRelativePath += " ../" ;
324+ // If this namespace is the global namespace, then its documentation
325+ // name needs to be changed to link correctly.
326+ if (Current.QualName == " GlobalNamespace" && Current.RelativePath != " ./" )
327+ Context[" DocumentationFileName" ] =
328+ SmallString<16 >(" GlobalNamespace/index" );
329+ }
330+ Context[" RelativePath" ] = CurrentRelativePath;
331+ ContextArrayRef.insert (ContextArrayRef.begin (), ContextVal);
332+ }
333+
334+ ContextArrayRef.back ().getAsObject ()->insert ({" End" , true });
335+ Obj[" Contexts" ] = ContextArray;
336+ Obj[" HasContexts" ] = true ;
337+ }
338+
281339static void
282340serializeCommonAttributes (const Info &I, json::Object &Obj,
283341 const std::optional<StringRef> RepositoryUrl) {
@@ -323,6 +381,9 @@ serializeCommonAttributes(const Info &I, json::Object &Obj,
323381 Obj[" Location" ] =
324382 serializeLocation (Symbol->DefLoc .value (), RepositoryUrl);
325383 }
384+
385+ if (!I.Contexts .empty ())
386+ generateContext (I, Obj);
326387}
327388
328389static void serializeReference (const Reference &Ref, Object &ReferenceObj) {
@@ -335,7 +396,7 @@ static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
335396
336397 // If the reference is a nested class it will be put into a folder named
337398 // after the parent class. We can get that name from the path's stem.
338- if (Ref.Path != " GlobalNamespace" )
399+ if (Ref.Path != " GlobalNamespace" && !Ref. Path . empty () )
339400 ReferenceObj[" PathStem" ] = sys::path::stem (Ref.Path );
340401 }
341402}
@@ -730,6 +791,29 @@ static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir) {
730791 return Error::success ();
731792}
732793
794+ static void serializeContexts (Info *I,
795+ StringMap<std::unique_ptr<Info>> &Infos) {
796+ if (I->USR == GlobalNamespaceID)
797+ return ;
798+ auto ParentUSR = I->ParentUSR ;
799+
800+ while (true ) {
801+ auto &ParentInfo = Infos.at (llvm::toHex (ParentUSR));
802+
803+ if (ParentInfo && ParentInfo->USR == GlobalNamespaceID) {
804+ Context GlobalRef (ParentInfo->USR , " Global Namespace" ,
805+ InfoType::IT_namespace, " GlobalNamespace" , " " ,
806+ SmallString<16 >(" index" ));
807+ I->Contexts .push_back (GlobalRef);
808+ return ;
809+ }
810+
811+ Context ParentRef (*ParentInfo);
812+ I->Contexts .push_back (ParentRef);
813+ ParentUSR = ParentInfo->ParentUSR ;
814+ }
815+ }
816+
733817Error JSONGenerator::generateDocumentation (
734818 StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
735819 const ClangDocContext &CDCtx, std::string DirName) {
@@ -763,9 +847,12 @@ Error JSONGenerator::generateDocumentation(
763847 if (FileErr)
764848 return createFileError (" cannot open file " + Group.getKey (), FileErr);
765849
766- for (const auto &Info : Group.getValue ())
850+ for (const auto &Info : Group.getValue ()) {
851+ if (Info->IT == InfoType::IT_record || Info->IT == InfoType::IT_namespace)
852+ serializeContexts (Info, Infos);
767853 if (Error Err = generateDocForInfo (Info, InfoOS, CDCtx))
768854 return Err;
855+ }
769856 }
770857
771858 return serializeIndex (CDCtx, RootDir);
0 commit comments