Skip to content

auto-relates option #882

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

Merged
merged 1 commit into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/mrdocs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
"title": "Use the first line of the comment as the brief",
"type": "boolean"
},
"auto-relates": {
"default": true,
"description": "When set to `true`, MrDocs automatically finds non-member functions that are related to the current class.",
"enum": [
true,
false
],
"title": "Automatically find non-member functions",
"type": "boolean"
},
"base-url": {
"default": "",
"description": "Base URL for links to source code. The base URL is used to create links to the source code in the documentation. The base URL is combined with the path to the source file to create the link.",
Expand Down
29 changes: 29 additions & 0 deletions include/mrdocs/Corpus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,35 @@ class MRDOCS_VISIBLE
return temp;
}

/** Return a qualified name from the specified context.

This function returns the qualified name
of the specified Info `I` from the context
specified by the SymbolID `context`.

If the context is a parent of `I`, the
qualified name is constructed relative to
the context. For instance, if `I` is `A::B::C::D`
and context is `A::B`, the result is `C::D`.

If the context is not a parent of `I`, the
qualified name is constructed relative to
the global namespace with the prefix `::`.
*/
virtual
void
qualifiedName(
Info const& I,
SymbolID const& context,
std::string& result) const = 0;

std::string
qualifiedName(Info const& I, SymbolID const& context) const
{
std::string temp;
qualifiedName(I, context, temp);
return temp;
}
};

//------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions include/mrdocs/Metadata/Info/Record.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ struct RecordInfo final
*/
std::vector<BaseInfo> Bases;

/** List of derived classes
*/
std::vector<SymbolID> Derived;

RecordInterface Interface;

//--------------------------------------------
Expand Down Expand Up @@ -347,6 +351,7 @@ tag_invoke(
io.map("defaultAccess", getDefaultAccessString(I.KeyKind));
io.map("isTypedef", I.IsTypeDef);
io.map("bases", dom::LazyArray(I.Bases, domCorpus));
io.map("derived", dom::LazyArray(I.Derived, domCorpus));
io.map("interface", I.Interface);
io.map("template", I.Template);
}
Expand Down
2 changes: 1 addition & 1 deletion include/mrdocs/Metadata/Javadoc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1808,7 +1808,7 @@ tag_invoke(
io.defer("description", [&I, domCorpus] {
return dom::LazyArray(I.blocks, domCorpus);
});
if (I.brief)
if (I.brief && !I.brief->children.empty())
{
io.map("brief", I.brief);
}
Expand Down
15 changes: 10 additions & 5 deletions include/mrdocs/Metadata/Type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,15 +562,20 @@ innerTypePtr(TypeInfo& TI) noexcept;
The innermost type is the type which is not
modified by any specifiers (e.g. "int" in
"pointer to const int").

If the type has an inner type, we recursively
call this function until we reach the innermost
type. If the type has no inner type, we return
the current type.
*/
MRDOCS_DECL
std::optional<std::reference_wrapper<Polymorphic<TypeInfo> const>>
innermostType(TypeInfo const& TI) noexcept;
Polymorphic<TypeInfo> const&
innermostType(Polymorphic<TypeInfo> const& TI) noexcept;

/// @copydoc innermostType(TypeInfo const&)
/// @copydoc innermostType(Polymorphic<TypeInfo> const&)
MRDOCS_DECL
std::optional<std::reference_wrapper<Polymorphic<TypeInfo>>>
innermostType(TypeInfo& TI) noexcept;
Polymorphic<TypeInfo>&
innermostType(Polymorphic<TypeInfo>& TI) noexcept;

// VFALCO maybe we should rename this to `renderType` or something?
MRDOCS_DECL
Expand Down
21 changes: 14 additions & 7 deletions share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,20 @@
|===

{{/if}}
{{! Relates symbols }}
{{#if symbol.doc.relates }}
{{#> markup/dynamic-level-h }}Non-Member Of{{/markup/dynamic-level-h}}
{{! Derived classes }}
{{#if symbol.derived}}
{{#> markup/dynamic-level-h }}Derived Classes{{/markup/dynamic-level-h}}
[,cols=2]
|===
| Name
| Description
{{#each symbol.doc.relates }}
| {{> javadoc/reference }}
| {{> javadoc/inline-brief symbol.doc.brief }}
{{#each symbol.derived}}
| {{#if url~}}
{{#>markup/a href=url}}{{#>markup/code}}{{> symbol/name-info . nolink=true}}{{/markup/code}}{{/markup/a}}
{{else~}}
{{> symbol/name-info . nolink=true}}
{{/if}}
| {{> javadoc/inline-brief doc.brief }}
{{/each}}
|===

Expand All @@ -101,7 +105,7 @@
{{! Using symbols }}
{{#if symbol.shadows}}
{{#> markup/dynamic-level-h }}Introduced Symbols{{/markup/dynamic-level-h}}

[cols=2]
|===
| Name
{{#each symbol.shadows}}
Expand All @@ -113,6 +117,7 @@
{{! Exceptions }}
{{#if symbol.doc.exceptions}}
{{#> markup/dynamic-level-h }}Exceptions{{/markup/dynamic-level-h}}
[cols=2]
|===
| Name
| Thrown on
Expand Down Expand Up @@ -140,6 +145,7 @@
{{! Template Parameters }}
{{#if symbol.doc.tparams}}
{{#> markup/dynamic-level-h }}Template Parameters{{/markup/dynamic-level-h}}
[cols=2]
|===
| Name
| Description
Expand All @@ -153,6 +159,7 @@
{{! Parameters }}
{{#if symbol.doc.params}}
{{#> markup/dynamic-level-h }}Parameters{{/markup/dynamic-level-h}}
[cols=2]
|===
| Name
| Description
Expand Down
14 changes: 9 additions & 5 deletions share/mrdocs/addons/generator/html/partials/symbol.html.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@
</table>
</div>
{{/if}}
{{! Relates symbols }}
{{#if symbol.doc.relates }}
{{! Derived classes }}
{{#if symbol.derived}}
<div>
{{#> markup/dynamic-level-h }}Non-Member Of{{/markup/dynamic-level-h}}
{{#> markup/dynamic-level-h }}Derived Classes{{/markup/dynamic-level-h}}
<table>
<thead>
<tr>
Expand All @@ -118,8 +118,12 @@
</tr>
</thead>
<tbody>
{{#each symbol.doc.relates }}
<tr><td>{{> javadoc/reference }}</td><td>{{> javadoc/inline-brief symbol.doc.brief }}</td></tr>
{{#each symbol.derived}}
<tr><td>{{#if url~}}
{{#>markup/a href=url}}{{#>markup/code}}{{> symbol/name-info . nolink=true}}{{/markup/code}}{{/markup/a}}
{{else~}}
{{> symbol/name-info . nolink=true}}
{{/if}}</td><td>{{> javadoc/inline-brief doc.brief }}</td></tr>
{{/each}}
</tbody>
</table>
Expand Down
7 changes: 7 additions & 0 deletions src/lib/Lib/ConfigOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@
"details": "When set to `true`, MrDocs uses the first line (until the first dot, question mark, or exclamation mark) of the comment as the brief of the symbol. When set to `false`, a explicit @brief command is required.",
"type": "bool",
"default": true
},
{
"name": "auto-relates",
"brief": "Automatically find non-member functions",
"details": "When set to `true`, MrDocs automatically finds non-member functions that are related to the current class.",
"type": "bool",
"default": true
}
]
},
Expand Down
59 changes: 59 additions & 0 deletions src/lib/Lib/CorpusImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
#include "lib/Metadata/Finalizers/SortMembersFinalizer.hpp"
#include "lib/Metadata/Finalizers/JavadocFinalizer.hpp"
#include "lib/Metadata/Finalizers/NamespacesFinalizer.hpp"
#include "lib/Metadata/Finalizers/DerivedFinalizer.hpp"
#include "lib/Support/Report.hpp"
#include "lib/Support/Chrono.hpp"
#include <mrdocs/Metadata.hpp>
#include <mrdocs/Support/Error.hpp>
#include <mrdocs/Support/ThreadPool.hpp>
#include <mrdocs/Support/Algorithm.hpp>
#include <chrono>

namespace clang::mrdocs {
Expand Down Expand Up @@ -869,6 +871,57 @@ qualifiedName(Info const& I, std::string& result) const
}
}

void
CorpusImpl::
qualifiedName(
Info const& I,
SymbolID const& context,
std::string& result) const
{
if (context == SymbolID::global)
{
qualifiedName(I, result);
return;
}

result.clear();
if (!I.id || I.id == SymbolID::global)
{
return;
}

if (I.Parent &&
!is_one_of(I.Parent, {SymbolID::global, context}) &&
I.id != context)
{
if (Info const* PI = find(I.Parent))
{
qualifiedName(*PI, context, result);
result += "::";
}
}

if (I.id == context)
{
return;
}

if (I.Parent == SymbolID::global)
{
result += "::";
}
if (!I.Name.empty())
{
result += I.Name;
}
else
{
result += "<unnamed ";
result += toString(I.Kind);
result += ">";
}
}

void
CorpusImpl::finalize()
{
Expand All @@ -892,6 +945,12 @@ CorpusImpl::finalize()
finalizer.build();
}

{
report::debug("Finalizing auto-relates");
DerivedFinalizer finalizer(*this);
finalizer.build();
}

if (config->sortMembers)
{
report::debug("Finalizing sorted members");
Expand Down
7 changes: 7 additions & 0 deletions src/lib/Lib/CorpusImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class CorpusImpl final : public Corpus
friend class SortMembersFinalizer;
friend class JavadocFinalizer;
friend class NamespacesFinalizer;
friend class DerivedFinalizer;

public:
/** Constructor.
Expand Down Expand Up @@ -175,6 +176,12 @@ class CorpusImpl final : public Corpus
Info const& I,
std::string& result) const override;

void
qualifiedName(
Info const& I,
SymbolID const& context,
std::string& result) const override;

/** Finalize the corpus.
*/
void
Expand Down
66 changes: 66 additions & 0 deletions src/lib/Metadata/Finalizers/DerivedFinalizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// Licensed 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
//
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#include "DerivedFinalizer.hpp"
#include <mrdocs/Support/Report.hpp>
#include <mrdocs/Support/Algorithm.hpp>
#include <algorithm>

namespace clang::mrdocs {

void
DerivedFinalizer::
build()
{
for (auto& I : corpus_.info_)
{
MRDOCS_ASSERT(I);
MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular);
MRDOCS_CHECK_OR_CONTINUE(I->isRecord());
auto& record = dynamic_cast<RecordInfo&>(*I);
MRDOCS_CHECK_OR_CONTINUE(!record.Bases.empty());
for (BaseInfo& base: record.Bases)
{
MRDOCS_CHECK_OR_CONTINUE(base.Access == AccessKind::Public);
MRDOCS_CHECK_OR_CONTINUE(base.Type);
MRDOCS_CHECK_OR_CONTINUE(base.Type->isNamed());
auto& namedType = dynamic_cast<NamedTypeInfo&>(*base.Type);
MRDOCS_CHECK_OR_CONTINUE(namedType.Name);
SymbolID const namedSymbolID = namedType.Name->id;
MRDOCS_CHECK_OR_CONTINUE(namedSymbolID != SymbolID::invalid);
Info* baseInfoPtr = corpus_.find(namedSymbolID);
MRDOCS_CHECK_OR_CONTINUE(baseInfoPtr);
MRDOCS_CHECK_OR_CONTINUE(baseInfoPtr->isRecord());
MRDOCS_CHECK_OR_CONTINUE(baseInfoPtr->Extraction == ExtractionMode::Regular);
if (auto& baseRecord = dynamic_cast<RecordInfo&>(*baseInfoPtr);
!contains(baseRecord.Derived, record.id))
{
// Insert in order by name
auto const it = std::ranges::lower_bound(
baseRecord.Derived,
record.id,
[&](SymbolID const& lhs, SymbolID const& rhs) {
auto const* lhsRecord = corpus_.find(lhs);
auto const* rhsRecord = corpus_.find(rhs);
MRDOCS_ASSERT(lhsRecord);
MRDOCS_ASSERT(rhsRecord);
if (lhsRecord->Name != rhsRecord->Name)
{
return lhsRecord->Name < rhsRecord->Name;
}
return lhs < rhs;
});
baseRecord.Derived.insert(it, record.id);
}
}
}
}

} // clang::mrdocs
Loading
Loading