20#include "ir/contentbuilder.h"
21#include "ir/signaturespan.h"
40#include <QRegularExpression>
46using namespace Qt::Literals;
51 return [loc](QtMsgType type,
const QString &message) {
67 auto result = resolver->hrefForNode(target, relative);
68 if (
const auto *href = std::get_if<QString>(&result))
79const Node *findConceptNodeForRelative(
const Node *relative,
const QString &name)
85 return qdb->findConceptNode(name);
106void appendRequiresSpansWithLinks(QList<IR::SignatureSpan> &out,
107 const QString &requiresText,
108 const QStringList &conceptNames,
109 const HrefResolver *hrefResolver,
110 const Node *relative)
112 auto emitFallback = [&out, &requiresText]() {
115 req.text =
" requires "_L1 + requiresText;
119 if (conceptNames.isEmpty() || requiresText.isEmpty()) {
124 QStringList sorted = conceptNames;
125 std::sort(sorted.begin(), sorted.end(),
126 [](
const QString &a,
const QString &b) {
return a.size() > b.size(); });
133 QHash<QString, QString> targetForDisplay;
134 QStringList escapedAlternatives;
135 escapedAlternatives.reserve(sorted.size() * 2);
136 for (
const QString &name : sorted) {
137 if (!targetForDisplay.contains(name)) {
138 targetForDisplay.insert(name, name);
139 escapedAlternatives.append(QRegularExpression::escape(name));
141 const QString tail = name.section(
"::"_L1, -1);
142 if (tail != name && !targetForDisplay.contains(tail)) {
143 targetForDisplay.insert(tail, name);
144 escapedAlternatives.append(QRegularExpression::escape(tail));
147 const QRegularExpression conceptRegex(
148 u"\\b("_s + escapedAlternatives.join(u"|"_s) + u")\\b"_s);
149 if (!conceptRegex.isValid()) {
154 auto it = conceptRegex.globalMatch(requiresText);
164 lead.text =
" requires "_L1;
168 while (it.hasNext()) {
169 const auto match = it.next();
170 if (match.capturedStart() > pos) {
173 textSpan.text = requiresText.mid(pos, match.capturedStart() - pos);
174 out.append(textSpan);
176 const QString matched = match.captured(1);
177 const QString target = targetForDisplay.value(matched, matched);
178 const Node *cn = findConceptNodeForRelative(relative, target);
179 const QString href = cn ? resolveHref(hrefResolver, cn, relative) : QString();
183 if (href.isEmpty()) {
192 pos = match.capturedEnd();
194 if (pos < requiresText.size()) {
197 tail.text = requiresText.mid(pos);
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
227 Q_ASSERT_X(pn,
"NodeExtractor::extractPageMetadata",
228 "PageNode pointer must be non-null");
237 const auto *qcn =
static_cast<
const QmlTypeNode *>(pn);
238 QString suffix = qcn->isQmlBasicType() ?
" QML Value Type"_L1 :
" QML Type"_L1;
239 pm.title = pn->name() + suffix;
240 pm.fullTitle = pm.title;
245 const auto *aggregate =
static_cast<
const Aggregate *>(pn);
246 pm.fullTitle = aggregate->plainFullName();
247 pm.title = pm.fullTitle;
249 pm.title = pn->title();
250 pm.fullTitle = pn->fullTitle();
253 pm.url = hrefResolver->fileName(pn);
254 const QString baseUrl = Config::instance().get(
CONFIG_URL).asString();
255 if (!baseUrl.isEmpty() && !pm.url.isEmpty() && !pm.url.contains(
"://"_L1)) {
257 + (baseUrl.endsWith(u'/') ? QString{} : QStringLiteral(
"/"))
260 pm.since = pn->since();
261 pm.deprecatedSince = pn->deprecatedSince();
262 pm.brief = pn
->doc().briefText().toString();
270 const int headingOffset = [&] {
289 diagnosticHandlerFor(pn));
290 pm.body = contentBuilder.build(firstAtom);
294 const auto *aggregate =
static_cast<
const Aggregate *>(pn);
295 pm.summarySections = extractSummarySections(aggregate, hrefResolver);
296 pm.detailSections = extractDetailSections(aggregate, hrefResolver);
300 const auto *qcn =
static_cast<
const QmlTypeNode *>(pn);
301 pm.qmlTypeData = extractQmlTypeData(qcn, hrefResolver);
305 const auto *cn =
static_cast<
const CollectionNode *>(pn);
306 pm.collectionData = extractCollectionData(cn, hrefResolver);
310 const auto *aggregate =
static_cast<
const Aggregate *>(pn);
311 pm.cppReferenceData = extractCppReferenceData(aggregate, hrefResolver);
320
321
322
323
324
325
326
330 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
332 if (!qcn->logicalModuleName().isEmpty()) {
333 bool includeImport =
true;
340 QStringList parts = QStringList()
341 <<
"import"_L1 << qcn->logicalModuleName() << qcn->logicalModuleVersion();
342 data.importStatement = parts.join(
' '_L1).trimmed();
362 inheritsInfo.name = base->name();
363 inheritsInfo.href = resolveHref(hrefResolver, base, qcn);
368 inheritsInfo.moduleName = base->logicalModuleName();
370 data.inherits = inheritsInfo;
373 if (!subs.isEmpty()) {
374 QList<IR::QmlTypeData::InheritedByEntry> filteredSubs;
375 for (
const auto *sub : std::as_const(subs)) {
376 const NodeContext context = sub->createContext();
377 if (InclusionFilter::isIncluded(policy, context))
378 filteredSubs.append({sub->name(), resolveHref(hrefResolver, sub, qcn)});
380 std::sort(filteredSubs.begin(), filteredSubs.end(),
383 return a.name < b.name;
385 data.inheritedBy = filteredSubs;
391 if (InclusionFilter::isIncluded(policy, context))
392 data.nativeType = IR::QmlTypeData::NativeTypeInfo{cn->name(), resolveHref(hrefResolver, cn, qcn)};
399
400
401
402
403
404
405
406
407
408
409
410
415 data.logicalModuleName = cn->logicalModuleName();
416 data.logicalModuleVersion = cn->logicalModuleVersion();
417 data.qtVariable = cn->qtVariable();
434 const QString rawCmakePackage = cn->cmakePackage();
435 const QString rawCmakeComponent = cn->cmakeComponent();
436 const QString rawCmakeTargetItem = cn->cmakeTargetItem();
437 if (!rawCmakePackage.isEmpty() || !rawCmakeComponent.isEmpty()) {
438 const QString package = rawCmakePackage.isEmpty()
439 ?
"Qt"_L1 + QString::number(QT_VERSION_MAJOR)
441 data.cmakePackage = package;
442 data.cmakeComponent = rawCmakeComponent;
443 if (!rawCmakeTargetItem.isEmpty())
444 data.cmakeTargetItem = rawCmakeTargetItem;
445 else if (!rawCmakeComponent.isEmpty())
446 data.cmakeTargetItem = package +
"::"_L1 + rawCmakeComponent;
449 data.state = cn->state();
460 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
463 return { node->name(), resolveHref(hrefResolver, node, cn), node->doc().briefText().toString() };
466 auto sortEntries = [](QList<IR::CollectionData::MemberEntry> &entries) {
467 std::sort(entries.begin(), entries.end(),
470 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
476 for (
auto *node : nsMap.values()) {
477 const NodeContext context = node->createContext();
478 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
479 data.namespaces.append(makeMemberEntry(node));
481 sortEntries(data.namespaces);
484 for (
auto *node : classMap.values()) {
485 const NodeContext context = node->createContext();
486 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
487 data.classes.append(makeMemberEntry(node));
489 sortEntries(data.classes);
491 for (
const auto *node : cn->members()) {
492 if (!node->isInAPI())
494 const NodeContext context = node->createContext();
495 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
496 data.members.append(makeMemberEntry(node));
498 sortEntries(data.members);
505 const HrefResolver *hrefResolver,
506 const Node *relative);
509
510
511
512
513
514
515
516
517
518
519
520
521
530 data.typeWord = aggregate->typeWord(
false);
533 auto ancestors = aggregate->plainFullName().split(
"::"_L1);
534 ancestors.pop_back();
535 data.ancestorNames = ancestors;
537 if (aggregate->includeFile())
538 data.headerInclude = *aggregate->includeFile();
540 if (!aggregate->physicalModuleName().isEmpty()) {
543 if (cn && (!cn->cmakeComponent().isEmpty() || !cn->cmakePackage().isEmpty())) {
544 const QString package = cn->cmakePackage().isEmpty()
545 ?
"Qt"_L1 + QString::number(QT_VERSION_MAJOR)
546 : cn->cmakePackage();
548 if (cn->cmakeComponent().isEmpty())
549 findPkg =
"find_package("_L1 + package +
" REQUIRED)"_L1;
551 findPkg =
"find_package("_L1 + package +
" REQUIRED COMPONENTS "_L1
552 + cn->cmakeComponent() +
")"_L1;
555 if (!cn->cmakeTargetItem().isEmpty()) {
556 target = cn->cmakeTargetItem();
557 }
else if (cn->cmakeComponent().isEmpty()) {
558 target = package +
"::"_L1 + package;
560 target = package +
"::"_L1 + cn->cmakeComponent();
563 data.cmakeFindPackage = findPkg;
564 data.cmakeTargetLinkLibraries =
565 "target_link_libraries(mytarget PRIVATE "_L1 + target +
")"_L1;
567 if (cn && !cn->qtVariable().isEmpty())
568 data.qmakeVariable =
"QT += "_L1 + cn->qtVariable();
571 auto statusOpt = formatStatus(aggregate, qdb);
573 data.statusText = *statusOpt;
574 if (aggregate->status() == Status::Deprecated)
575 data.statusCssClass =
"deprecated"_L1;
576 else if (!aggregate->deprecatedSince().isEmpty())
577 data.statusCssClass =
"pending-deprecation"_L1;
578 else if (aggregate->status() == Status::Preliminary)
579 data.statusCssClass =
"preliminary"_L1;
581 data.statusCssClass =
"status"_L1;
585 auto *classNode =
const_cast<ClassNode *>(
static_cast<
const ClassNode *>(aggregate));
587 if (classNode->isQmlNativeType()) {
588 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
589 const NodeContext context = classNode->createContext();
591 QList<QmlTypeNode *> nativeTypes{classNode->qmlNativeTypes().cbegin(),
592 classNode->qmlNativeTypes().cend()};
593 if (!nativeTypes.isEmpty()) {
595 data.qmlNativeType = IR::CppReferenceData::QmlNativeTypeLink{
596 nativeTypes.first()->name(),
597 resolveHref(hrefResolver, nativeTypes.first(), aggregate)
603 const auto *metaTags = classNode->doc().metaTagMap();
604 if (metaTags && metaTags->contains(u"qdoc-suppress-inheritance"_s))
608 const auto &baseClasses = classNode->baseClasses();
609 for (
const auto &bc : baseClasses) {
611 data.baseClasses.append({
612 bc.m_node->plainFullName(),
613 resolveHref(hrefResolver, bc.m_node, aggregate),
619 const auto &derivedClasses = classNode->derivedClasses();
620 for (
const auto &dc : derivedClasses) {
622 data.derivedClasses.append({
623 dc.m_node->plainFullName(),
624 resolveHref(hrefResolver, dc.m_node, aggregate)
628 std::sort(data.derivedClasses.begin(), data.derivedClasses.end(),
631 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
638 data.templateDeclSpans = buildTemplateDeclSpans(&templateDecl, hrefResolver, aggregate);
639 data.referencedConcepts.reserve(
int(templateDecl.referenced_concepts.size()));
640 for (
const auto &s : templateDecl.referenced_concepts)
641 data.referencedConcepts.append(QString::fromStdString(s));
645 if (selfCategory != ComparisonCategory::None)
646 data.selfComparisonCategory = QString::fromStdString(comparisonCategoryAsString(selfCategory));
648 const auto *comparesMap = aggregate
->doc().comparesWithMap();
649 if (comparesMap && !comparesMap->isEmpty()) {
650 for (
auto [key, description] : comparesMap->asKeyValueRange()) {
651 IR::CppReferenceData::ComparisonEntry entry;
652 entry.category = QString::fromStdString(comparisonCategoryAsString(key));
654 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
655 entry.comparableTypes = types;
657 if (description.firstAtom()->next() != description.lastAtom()) {
658 Text descText = Text::subText(description.firstAtom()->next(),
659 description.lastAtom());
660 entry.description = descText.toString();
662 data.comparisonEntries.append(entry);
670 case Node::NonReentrant:
671 tsInfo.level =
"non-reentrant"_L1;
673 case Node::Reentrant:
674 tsInfo.level =
"reentrant"_L1;
676 case Node::ThreadSafe:
677 tsInfo.level =
"thread-safe"_L1;
683 NodeList reentrant, threadsafe, nonreentrant;
684 bool hasExceptions =
false;
685 for (
const auto *child : aggregate->childNodes()) {
686 if (!child->isDeprecated()) {
687 switch (child->threadSafeness()) {
688 case Node::Reentrant:
689 reentrant.append(
const_cast<Node *>(child));
690 if (ts == Node::ThreadSafe) hasExceptions =
true;
692 case Node::ThreadSafe:
693 threadsafe.append(
const_cast<Node *>(child));
694 if (ts == Node::Reentrant) hasExceptions =
true;
696 case Node::NonReentrant:
697 nonreentrant.append(
const_cast<Node *>(child));
698 hasExceptions =
true;
706 for (
const auto *node : std::as_const(reentrant)) {
707 tsInfo.reentrantExceptions.append({
708 node->plainFullName(),
709 resolveHref(hrefResolver, node, aggregate)
712 for (
const auto *node : std::as_const(threadsafe)) {
713 tsInfo.threadSafeExceptions.append({
714 node->plainFullName(),
715 resolveHref(hrefResolver, node, aggregate)
718 for (
const auto *node : std::as_const(nonreentrant)) {
719 tsInfo.nonReentrantExceptions.append({
720 node->plainFullName(),
721 resolveHref(hrefResolver, node, aggregate)
725 data.threadSafety =
std::move(tsInfo);
728 const QStringList &groupNames = aggregate->groupNames();
729 if (!groupNames.isEmpty()) {
731 for (
const auto &groupName : groupNames) {
732 auto it = groupMap.find(groupName);
733 if (it == groupMap.end() || !*it)
735 CollectionNode *group = *it;
740 qdb->mergeCollections(group);
741 if (group->wasSeen()) {
744 resolveHref(hrefResolver, group, aggregate)
751 const auto *ns =
static_cast<
const NamespaceNode *>(aggregate);
752 if (!ns->hasDoc() && ns->docNode()) {
754 data.fullNamespaceHref = resolveHref(hrefResolver, ns->docNode(), aggregate);
755 data.fullNamespaceModuleName = ns->docNode()->tree()->camelCaseModuleName();
763
764
765
766
767
768
769
776 QList<IR::SectionIR> result;
777 for (
const auto §ion : sv) {
778 if (section.isEmpty())
781 IR::SectionIR irSection;
782 irSection.title = section.title();
783 irSection.id = TextUtils::asAsciiPrintable(section.title());
784 irSection.singular = section.singular();
785 irSection.plural = section.plural();
791 for (
const auto *member : section.members()) {
792 if (member->isSharedCommentNode()) {
793 const auto *scn =
static_cast<
const SharedCommentNode *>(member);
794 for (
const auto *child : scn->collective()) {
795 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate);
796 irMember.href =
"#"_L1 + hrefResolver->anchorForNode(child);
797 irSection.members.append(irMember);
800 IR::MemberIR irMember = extractMemberIR(member, hrefResolver, aggregate);
801 irMember.href =
"#"_L1 + hrefResolver->anchorForNode(member);
802 irSection.members.append(irMember);
806 for (
const auto *reimpl : section.reimplementedMembers())
807 irSection.reimplementedMembers.append(extractMemberIR(reimpl, hrefResolver, aggregate));
809 for (
const auto &[base, count] : section.inheritedMembers()) {
810 IR::InheritedMembersIR inherited;
811 inherited.className = base->plainFullName();
812 inherited.count = count;
813 inherited.href = resolveHref(hrefResolver, base, aggregate);
814 irSection.inheritedMembers.append(inherited);
817 result.append(irSection);
823
824
825
826
827
828
829
830
836 QList<IR::SectionIR> result;
837 for (
const auto §ion : sv) {
838 if (section.isEmpty())
841 IR::SectionIR irSection;
842 irSection.title = section.title();
843 irSection.id = TextUtils::asAsciiPrintable(section.title());
844 irSection.singular = section.singular();
845 irSection.plural = section.plural();
847 for (
const auto *member : section.members()) {
848 if (member->isSharedCommentNode()) {
849 const auto *scn =
static_cast<
const SharedCommentNode *>(member);
851 QList<IR::ContentBlock> sharedBody;
852 const Text &bodyText = scn->doc().body();
853 if (
const Atom *firstAtom = bodyText.firstAtom()) {
854 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
855 diagnosticHandlerFor(scn));
856 sharedBody = contentBuilder.build(firstAtom);
859 QList<IR::ContentBlock> sharedAlso;
860 const QList<Text> &alsoTexts = scn->doc().alsoList();
861 for (
const Text &alsoText : alsoTexts) {
862 if (
const Atom *firstAtom = alsoText.firstAtom()) {
863 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
864 diagnosticHandlerFor(scn));
865 sharedAlso.append(contentBuilder.build(firstAtom));
869 for (
const auto *child : scn->collective()) {
870 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate, MemberExtractionLevel::Detail);
871 irMember.body = sharedBody;
872 irMember.alsoList = sharedAlso;
873 irSection.members.append(irMember);
876 irSection.members.append(extractMemberIR(member, hrefResolver, aggregate, MemberExtractionLevel::Detail));
880 result.append(irSection);
888 case Node::Reentrant:
889 return "reentrant"_L1;
890 case Node::ThreadSafe:
891 return "thread-safe"_L1;
898
899
900
901
902
903
904
905
906
907
908
909
910
916 member.name = node->name();
917 member.fullName = node->plainFullName();
918 member.href = resolveHref(hrefResolver, node, relative);
926 const auto *fn =
static_cast<
const FunctionNode *>(node);
927 member.signature = fn->signature(
938 for (
int i = 0; i < params
.count(); ++i) {
940 param.type = params
.at(i
).type();
941 param.name = params
.at(i
).name();
942 param.defaultValue = params
.at(i
).defaultValue();
943 member.parameters.append(param);
946 const auto *en =
static_cast<
const EnumNode *>(node);
947 member.signature = en->isScoped()
948 ? QStringLiteral(
"enum class %1").arg(en->name())
949 : QStringLiteral(
"enum %1").arg(en->name());
951 for (
const auto &item : en->items()) {
953 ev.name = item.name();
954 ev.value = item.value();
955 ev.since = item.since();
956 member.enumValues.append(ev);
959 const auto *qpn =
static_cast<
const QmlPropertyNode *>(node);
960 member.signature = qpn->name() +
" : "_L1 + qpn->dataType();
961 member.dataType = qpn->dataType();
967 const auto *pn =
static_cast<
const PropertyNode *>(node);
968 member.signature = pn->name() +
" : "_L1 + pn->qualifiedDataType();
970 const auto *td =
static_cast<
const TypedefNode *>(node);
971 member.signature = td->associatedEnum()
972 ?
"flags "_L1 + td->name()
975 const auto *vn =
static_cast<
const VariableNode *>(node);
976 member.signature = vn->leftType() + vn->name() + vn->rightType();
978 member.signature = node->name();
982 member.anchorId = hrefResolver->anchorForNode(node);
983 member.synopsis = member.signature;
984 member.since = node->since();
985 member.threadSafety = threadSafenessString(node->threadSafeness());
989 member.comparisonCategory = QString::fromStdString(catStr);
992 const auto *fn =
static_cast<
const FunctionNode *>(node);
993 const auto &noexcept_ = fn->getNoexcept();
996 member.noexceptNote = *noexcept_;
1003 diagnosticHandlerFor(node));
1004 member.body = contentBuilder.build(firstAtom);
1007 const QList<Text> &alsoTexts = node
->doc().alsoList();
1008 for (
const Text &alsoText : alsoTexts) {
1009 if (
const Atom *firstAtom = alsoText.firstAtom()) {
1010 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
1011 diagnosticHandlerFor(node));
1012 QList<IR::ContentBlock> blocks = contentBuilder.build(firstAtom);
1013 member.alsoList.append(blocks);
1019 member.signatureSpans = buildSignatureSpans(node, hrefResolver, relative, spanStyle);
1026 QList<IR::SignatureSpan> spans;
1027 QString pendingWord;
1029 for (
int i = 0; i <= typeString.size(); ++i) {
1031 if (i != typeString.size())
1032 ch = typeString.at(i);
1034 QChar lower = ch.toLower();
1035 if ((lower >=
'a'_L1 && lower <=
'z'_L1) || ch.digitValue() >= 0
1036 || ch ==
'_'_L1 || ch ==
':'_L1) {
1039 if (!pendingWord.isEmpty()) {
1040 bool isProbablyType = (pendingWord !=
"const"_L1);
1043 span.text = pendingWord;
1046 pendingWord.clear();
1051 span.text = QString(ch);
1061 QString extraStr = CodeMarker::extraSynopsis(node, style);
1062 if (extraStr.isEmpty())
1068 static const QRegularExpression extrefRegex(
1069 u"<@extref target=\"([^\"]+)\">([^<]+)</@extref>"_s);
1071 QList<IR::SignatureSpan> spans;
1076 auto it = extrefRegex.globalMatch(extraStr);
1077 while (it.hasNext()) {
1078 auto match = it.next();
1079 if (match.capturedStart() > pos) {
1082 textSpan.text = extraStr.mid(pos, match.capturedStart() - pos);
1083 wrapper.children.append(textSpan);
1087 ref.text = match.captured(2);
1088 ref.href =
"https://en.cppreference.com/w/cpp/language/"_L1 + match.captured(1);
1089 wrapper.children.append(ref);
1090 pos = match.capturedEnd();
1092 if (pos < extraStr.size()) {
1095 textSpan.text = extraStr.mid(pos);
1096 wrapper.children.append(textSpan);
1099 if (wrapper.children.isEmpty()) {
1100 wrapper.text = extraStr;
1102 spans.append(wrapper);
1107 const HrefResolver *hrefResolver,
1108 const Node *relative)
1115 declSpan.text =
"template"_L1;
1120 declSpan.children.append(open);
1123 for (
const auto ¶m : templateDecl->parameters) {
1124 if (param.sfinae_constraint)
1127 IR::SignatureSpan comma;
1128 comma.role = IR::SpanRole::Text;
1129 comma.text =
", "_L1;
1130 declSpan.children.append(comma);
1133 switch (param.kind) {
1134 case RelaxedTemplateParameter::Kind::TypeTemplateParameter: {
1135 if (param.concept_name) {
1136 const QString fq = QString::fromStdString(*param.concept_name);
1137 const QString unqualified = fq.section(
"::"_L1, -1);
1140 const Node *conceptNode = findConceptNodeForRelative(relative, fq);
1141 IR::SignatureSpan link;
1142 link.role = IR::SpanRole::Link;
1143 link.text = unqualified;
1144 link.href = resolveHref(hrefResolver, conceptNode, relative);
1145 declSpan.children.append(link);
1147 IR::SignatureSpan kw;
1148 kw.role = IR::SpanRole::Text;
1149 kw.text =
"typename"_L1;
1150 declSpan.children.append(kw);
1154 case RelaxedTemplateParameter::Kind::TemplateTemplateParameter: {
1155 IR::SignatureSpan kw;
1156 kw.role = IR::SpanRole::Text;
1157 kw.text =
"typename"_L1;
1158 declSpan.children.append(kw);
1161 case RelaxedTemplateParameter::Kind::NonTypeTemplateParameter: {
1162 if (!param.valued_declaration.type.empty()) {
1163 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.type));
1164 declSpan.children.append(typeSpans);
1170 if (param.is_parameter_pack) {
1171 IR::SignatureSpan dots;
1172 dots.role = IR::SpanRole::Text;
1173 dots.text =
"..."_L1;
1174 declSpan.children.append(dots);
1177 if (!param.valued_declaration.name.empty()) {
1178 IR::SignatureSpan space;
1179 space.role = IR::SpanRole::Text;
1180 space.text =
" "_L1;
1181 declSpan.children.append(space);
1183 IR::SignatureSpan nameSpan;
1184 nameSpan.role = IR::SpanRole::Parameter;
1185 nameSpan.text = QString::fromStdString(param.valued_declaration.name);
1186 declSpan.children.append(nameSpan);
1189 if (!param.valued_declaration.initializer.empty()) {
1190 IR::SignatureSpan eq;
1191 eq.role = IR::SpanRole::Text;
1193 declSpan.children.append(eq);
1195 if (param.kind == RelaxedTemplateParameter::Kind::TypeTemplateParameter
1196 || param.kind == RelaxedTemplateParameter::Kind::TemplateTemplateParameter) {
1197 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.initializer));
1198 declSpan.children.append(typeSpans);
1200 IR::SignatureSpan val;
1201 val.role = IR::SpanRole::Text;
1202 val.text = QString::fromStdString(param.valued_declaration.initializer);
1203 declSpan.children.append(val);
1212 close.text =
">"_L1;
1213 declSpan.children.append(close);
1215 if (templateDecl->requires_clause && !templateDecl->requires_clause->empty()) {
1216 const QString reqText = QString::fromStdString(*templateDecl->requires_clause);
1217 QStringList conceptNames;
1218 conceptNames.reserve(
int(templateDecl->referenced_concepts.size()));
1219 for (
const auto &s : templateDecl->referenced_concepts)
1220 conceptNames.append(QString::fromStdString(s));
1221 appendRequiresSpansWithLinks(declSpan.children, reqText, conceptNames,
1222 hrefResolver, relative);
1225 return { declSpan };
1229 const HrefResolver *hrefResolver,
1230 const Node *relative,
1233 QList<IR::SignatureSpan> spans;
1235 auto appendText = [&spans](
const QString &text) {
1242 auto appendName = [&spans, node, hrefResolver, relative](
const QString &name) {
1246 span.href = resolveHref(hrefResolver, node, relative);
1250 auto appendTypeSpans = [&spans](
const QString &type,
bool trailingSpace) {
1251 auto typeSpans = buildTypeSpans(type);
1252 spans.append(typeSpans);
1253 if (trailingSpace && !type.isEmpty()
1254 && !type.endsWith(
'*'_L1) && !type.endsWith(
'&'_L1)) {
1257 space.text =
" "_L1;
1258 spans.append(space);
1264 auto extras = buildExtraSpans(node, style);
1265 if (!extras.isEmpty()) {
1266 spans.append(extras);
1272 QString nameText = node->name();
1279 nameText = node->parent()->name() +
"::"_L1 + nameText;
1288 appendText(Node::nodeTypeString(node->nodeType()) +
" "_L1);
1290 appendName(nameText);
1294 span.text = nameText;
1299 const auto *func =
static_cast<
const FunctionNode *>(node);
1302 if (
auto templateDecl = node->templateDecl()) {
1303 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl, hrefResolver, relative);
1304 spans.append(tmplSpans);
1310 if (!func->isNonvirtual())
1311 appendText(
"virtual "_L1);
1315 appendTypeSpans(func->returnTypeString(),
true);
1318 appendName(nameText);
1322 span.text = nameText;
1326 if (!func->isMacroWithoutParams()) {
1328 if (!func->parameters().isEmpty()) {
1329 const Parameters ¶meters = func->parameters();
1330 for (
int i = 0; i < parameters
.count(); ++i) {
1332 appendText(
", "_L1);
1334 QString pName = param.name();
1335 QString type = param.type();
1336 QString value = param.defaultValue();
1337 qsizetype insertPos = param.nameInsertionPoint();
1339 appendTypeSpans(type.left(insertPos),
false);
1342 paramSpan.text = pName;
1343 spans.append(paramSpan);
1344 appendTypeSpans(type.mid(insertPos),
false);
1347 appendTypeSpans(type, trailingSpace);
1351 paramSpan.text = pName;
1352 spans.append(paramSpan);
1356 appendText(
" = "_L1 + value);
1362 if (func->isConst())
1363 appendText(
" const"_L1);
1366 if (func->isFinal())
1367 appendText(
" final"_L1);
1368 if (func->isOverride())
1369 appendText(
" override"_L1);
1370 if (func->isPureVirtual())
1371 appendText(
" = 0"_L1);
1373 appendText(
" &"_L1);
1374 else if (func->isRefRef())
1375 appendText(
" &&"_L1);
1377 if (!func->returnType().isEmpty() && func->returnType() !=
"void"_L1) {
1378 appendText(
" : "_L1);
1379 appendTypeSpans(func->returnTypeString(),
false);
1383 appendText(
" &"_L1);
1384 else if (func->isRefRef())
1385 appendText(
" &&"_L1);
1386 if (
const auto &req = func->trailingRequiresClause(); req && !req->isEmpty())
1387 appendRequiresSpansWithLinks(spans, *req, func->referencedConcepts(),
1388 hrefResolver, relative);
1393 const auto *enume =
static_cast<
const EnumNode *>(node);
1394 appendText(
"enum"_L1);
1395 if (enume->isScoped())
1396 appendText(
" class"_L1);
1397 if (!enume->isAnonymous()) {
1400 appendName(nameText);
1404 span.text = nameText;
1409 appendText(
" { "_L1);
1410 const int MaxEnumValues = 6;
1411 QStringList documentedItems = enume->doc().enumItemNames();
1412 if (documentedItems.isEmpty()) {
1413 const auto &enumItems = enume->items();
1414 for (
const auto &item : enumItems)
1415 documentedItems << item.name();
1417 const QStringList omitItems = enume->doc().omitEnumItemNames();
1418 for (
const auto &item : omitItems)
1419 documentedItems.removeAll(item);
1421 if (documentedItems.size() > MaxEnumValues) {
1422 const QString last = documentedItems.last();
1423 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
1424 documentedItems +=
"..."_L1;
1425 documentedItems += last;
1427 appendText(documentedItems.join(
", "_L1));
1428 if (!documentedItems.isEmpty())
1436 if (
auto templateDecl = node->templateDecl()) {
1437 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl, hrefResolver, relative);
1438 spans.append(tmplSpans);
1443 appendName(nameText);
1447 span.text = nameText;
1453 if (
static_cast<
const TypedefNode *>(node)->associatedEnum())
1454 appendText(
"flags "_L1);
1456 appendName(nameText);
1460 span.text = nameText;
1466 const auto *property =
static_cast<
const PropertyNode *>(node);
1468 appendName(nameText);
1472 span.text = nameText;
1475 appendText(
" : "_L1);
1476 appendTypeSpans(property->qualifiedDataType(),
false);
1480 const auto *property =
static_cast<
const QmlPropertyNode *>(node);
1482 appendName(nameText);
1486 span.text = nameText;
1489 appendText(
" : "_L1);
1490 appendTypeSpans(property->dataType(),
false);
1494 const auto *variable =
static_cast<
const VariableNode *>(node);
1497 appendName(nameText);
1501 span.text = nameText;
1504 appendText(
" : "_L1);
1505 appendTypeSpans(variable->dataType(),
false);
1507 appendTypeSpans(variable->leftType(),
true);
1509 appendName(nameText);
1513 span.text = nameText;
1516 appendText(variable->rightType());
1522 appendName(nameText);
1526 span.text = nameText;
1536 const HrefResolver *hrefResolver)
1538 QList<IR::SignatureSpan> spans;
1540 auto appendText = [&spans](
const QString &text) {
1547 auto appendTypeSpans = [&spans](
const QString &type,
bool trailingSpace) {
1548 auto typeSpans = buildTypeSpans(type);
1549 spans.append(typeSpans);
1550 if (trailingSpace && !type.isEmpty()
1551 && !type.endsWith(
'*'_L1) && !type.endsWith(
'&'_L1)) {
1554 space.text =
" "_L1;
1555 spans.append(space);
1561 nameSpan.text = node->name();
1562 nameSpan.href = resolveHref(hrefResolver, node, node->parent());
1565 const auto *pn =
static_cast<
const QmlPropertyNode *>(node);
1566 spans.append(nameSpan);
1567 appendText(
" : "_L1);
1568 appendTypeSpans(pn->dataType(),
false);
1570 const auto *func =
static_cast<
const FunctionNode *>(node);
1571 if (!func->returnType().isEmpty())
1572 appendTypeSpans(func->returnTypeString(),
true);
1573 spans.append(nameSpan);
1575 if (!func->parameters().isEmpty()) {
1576 const Parameters ¶meters = func->parameters();
1577 for (
int i = 0; i < parameters
.count(); ++i) {
1579 appendText(
", "_L1);
1580 QString pName = parameters
.at(i
).name();
1581 QString type = parameters
.at(i
).type();
1582 if (!pName.isEmpty()) {
1583 appendTypeSpans(type,
true);
1586 paramSpan.text = pName;
1587 spans.append(paramSpan);
1591 paramSpan.text = type;
1592 spans.append(paramSpan);
1598 spans.append(nameSpan);
1601 auto extras = buildExtraSpans(node, Section::Summary);
1602 if (!extras.isEmpty()) {
1604 spans.append(extras);
1613 for (
const auto &span : spans)
1614 result += span.plainText();
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1633 const HrefResolver *hrefResolver,
1634 const Node *relative,
1637 if (node->isQmlNode() && !node->isEnumType())
1638 return buildQmlItemSpans(node, hrefResolver);
1639 return buildCppSynopsisSpans(node, hrefResolver, relative, style);
1643
1644
1645
1646
1647
1648
1649
1650
1654 result.typeName = qcn->name();
1655 result.typeHref = resolveHref(hrefResolver, qcn, qcn);
1660 if (groupedMembers.isEmpty())
1663 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1666 IR::AllMemberEntry entry;
1667 entry.signatureSpans = buildQmlItemSpans(node, hrefResolver);
1668 entry.signature = plainTextFromSpans(entry.signatureSpans);
1669 entry.href = resolveHref(hrefResolver, node, qcn);
1671 if (node->isQmlProperty()) {
1672 auto *qpn =
static_cast<QmlPropertyNode *>(node);
1673 QStringList qmlHints = qpn->hints();
1674 if (qpn->isAttached() && !qmlHints.contains(
"attached"_L1))
1675 qmlHints <<
"attached"_L1;
1676 for (
const auto &h : std::as_const(qmlHints))
1677 entry.hints.append(h);
1678 }
else if (node->isAttached()) {
1679 entry.hints.append(
"attached"_L1);
1682 if (node->isPropertyGroup()) {
1683 entry.isPropertyGroup =
true;
1684 const auto *scn =
static_cast<SharedCommentNode *>(node);
1685 for (
auto *child : scn->collective()) {
1686 const NodeContext childContext = child->createContext();
1687 if (!InclusionFilter::isIncluded(policy, childContext))
1689 entry.children.append(buildEntry(child));
1696 auto isVisible = [&policy](
Node *node) {
1698 return InclusionFilter::isIncluded(policy, context)
1699 && !(node->isSharingComment() && node->sharedCommentNode()->isPropertyGroup());
1702 for (
const auto &[originType, nodes] : groupedMembers) {
1703 Q_ASSERT(originType);
1704 if (nodes.isEmpty())
1707 IR::MemberGroup group;
1708 if (originType != qcn) {
1709 group.typeName = originType->name();
1710 group.typeHref = resolveHref(hrefResolver, originType, qcn);
1713 for (
auto *node : nodes) {
1714 if (isVisible(node))
1715 group.members.append(buildEntry(node));
1718 result.memberGroups.append(group);
1725
1726
1727
1728
1729
1730
1731
1735 result.typeName = aggregate->plainFullName();
1736 result.typeHref = resolveHref(hrefResolver, aggregate, aggregate);
1744 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1746 for (
const auto *node : allMembers.members()) {
1747 if (node->name().isEmpty())
1749 const NodeContext context = node->createContext();
1750 if (!InclusionFilter::isIncluded(policy, context))
1753 IR::AllMemberEntry entry;
1754 entry.signatureSpans = buildSignatureSpans(node, hrefResolver, aggregate, Section::AllMembers);
1755 entry.signature = plainTextFromSpans(entry.signatureSpans);
1756 entry.href = resolveHref(hrefResolver, node, aggregate);
1757 result.members.append(entry);
1764
1765
1766
1767
1768
1769
1770
1771
1775 const auto *qcn =
static_cast<
const QmlTypeNode *>(pn);
1776 if (qcn->isQmlBasicType())
1777 return std::nullopt;
1778 auto result = extractQmlAllMembersIR(qcn, hrefResolver);
1779 bool hasMember =
false;
1780 for (
const auto &group : std::as_const(result.memberGroups)) {
1781 if (!group.members.isEmpty()) {
1787 return std::nullopt;
1792 const auto *aggregate =
static_cast<
const Aggregate *>(pn);
1793 auto result = extractCppAllMembersIR(aggregate, hrefResolver);
1794 if (result.members.isEmpty())
1795 return std::nullopt;
1799 return std::nullopt;
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1824 const Config &config = Config::instance();
1828 const QString homepage = config.get(navDot +
CONFIG_HOMEPAGE).asString();
1829 const QString hometitle = config.get(navDot +
CONFIG_HOMETITLE).asString(homepage);
1831 const QString landingtitle = config.get(navDot +
CONFIG_LANDINGTITLE).asString(landingpage);
1835 const QString qmltypestitle = config.get(navDot +
CONFIG_QMLTYPESTITLE).asString(
"QML Types"_L1);
1837 const QString pageTitle = pn->title();
1840 auto resolveCrumb = [&](
const QString &targetName)
1841 -> std::pair<QString, CrumbState> {
1842 const Node *target = qdb->findNodeForTarget(targetName, pn);
1844 return {{}, CrumbState::Unresolved};
1846 return {{}, CrumbState::Current};
1847 return {resolveHref(hrefResolver, target, pn), CrumbState::Link};
1850 if (!homepage.isEmpty()) {
1851 auto [href, state] = resolveCrumb(homepage);
1852 if (state == CrumbState::Current)
1854 nav.breadcrumbs.append({hometitle,
std::move(href), state});
1857 if (!landingpage.isEmpty()) {
1858 auto [href, state] = resolveCrumb(landingpage);
1859 if (state != CrumbState::Current)
1860 nav.breadcrumbs.append({landingtitle,
std::move(href), state});
1864 if (!cppclassespage.isEmpty() && !cppclassestitle.isEmpty()) {
1865 auto [href, state] = resolveCrumb(cppclassespage);
1866 nav.breadcrumbs.append({cppclassestitle,
std::move(href), state});
1870 QString moduleState;
1871 if (moduleNode && !moduleNode->state().isEmpty())
1872 moduleState = QStringLiteral(
" (%1)").arg(moduleNode->state());
1874 if (!pn->physicalModuleName().isEmpty() && moduleNode
1875 && (!moduleState.isEmpty() || moduleNode->title() != cppclassespage)) {
1876 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1877 resolveHref(hrefResolver, moduleNode, pn),
1880 nav.breadcrumbs.append({pn->name(), {}, CrumbState
::Current});
1882 if (!qmltypespage.isEmpty() && !qmltypestitle.isEmpty()) {
1883 auto [href, state] = resolveCrumb(qmltypespage);
1884 nav.breadcrumbs.append({qmltypestitle,
std::move(href), state});
1888 QString moduleState;
1889 if (moduleNode && !moduleNode->state().isEmpty())
1890 moduleState = QStringLiteral(
" (%1)").arg(moduleNode->state());
1893 && (!moduleState.isEmpty() || moduleNode->title() != qmltypespage)) {
1894 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1895 resolveHref(hrefResolver, moduleNode, pn),
1898 nav.breadcrumbs.append({pn->name(), {}, CrumbState
::Current});
1900 auto currentNode = pn;
1901 std::deque<
const Node *> navNodes;
1902 qsizetype navItems = 0;
1904 if (
std::find(navNodes.cbegin(), navNodes.cend(),
1909 if (navNodes.empty()) {
1910 const QStringList groups = pn->groupNames();
1911 for (
const auto &groupName : groups) {
1912 const auto *groupNode = qdb->findNodeByNameAndType(
1913 QStringList{groupName}, &Node::isGroup);
1914 if (groupNode && !groupNode->title().isEmpty()) {
1915 navNodes.push_front(groupNode);
1920 for (
const auto *navNode : navNodes) {
1921 if (navNode->isPageNode())
1922 nav.breadcrumbs.append({navNode->title(),
1923 resolveHref(hrefResolver, navNode, pn),
1926 if (!nav.breadcrumbs.isEmpty())
1927 nav.breadcrumbs.append({pageTitle, {}, CrumbState
::Current});
1930 const auto &linkMap = pn->links();
1931 if (linkMap.contains(Node::PreviousLink)) {
1932 const auto &linkPair = linkMap[Node::PreviousLink];
1933 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1936 if (target && target != pn) {
1937 href = resolveHref(hrefResolver, target, pn);
1938 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1939 ? target->title() : linkPair.second;
1941 href = linkPair.first;
1942 title = linkPair.second;
1946 if (linkMap.contains(Node::NextLink)) {
1947 const auto &linkPair = linkMap[Node::NextLink];
1948 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1951 if (target && target != pn) {
1952 href = resolveHref(hrefResolver, target, pn);
1953 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1954 ? target->title() : linkPair.second;
1956 href = linkPair.first;
1957 title = linkPair.second;
1961 if (linkMap.contains(Node::StartLink)) {
1962 const auto &linkPair = linkMap[Node::StartLink];
1963 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1966 if (target && target != pn) {
1967 href = resolveHref(hrefResolver, target, pn);
1968 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1969 ? target->title() : linkPair.second;
1971 href = linkPair.first;
1972 title = linkPair.second;
1977 const QString formatDot =
"HTML"_L1 + Config::dot;
1978 nav
.tocDepth = config.get(formatDot +
"tocdepth"_L1).asInt();
bool hasObsoleteMembers() const
Returns true if this aggregate contains at least one child that is marked obsolete.
The Atom class is the fundamental unit for representing documents internally.
The ClassNode represents a C++ class.
A class for holding the members of a collection of doc pages.
NodeMap getMembers(NodeType type) const
The Config class contains the configuration variables for controlling how qdoc produces documentation...
const Location & location() const
Returns the starting location of a qdoc comment.
const Text & body() const
Text briefText(bool inclusive=false) const
Converts Atom chains to QList<IR::ContentBlock> trees.
static bool isIncluded(const InclusionPolicy &policy, const NodeContext &context)
The Location class provides a way to mark a location in a file.
A PageNode is a Node that generates a documentation page.
const PageNode * navigationParent() const
bool noAutoList() const
Returns the value of the no auto-list flag.
The Parameter class describes one function parameter.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
const CollectionNode * getModuleNode(const Node *relative)
Returns the collection node representing the module that relative node belongs to,...
const CNMap & groups()
Returns a const reference to the collection of all group nodes in the primary tree.
ClassNode * classNode() const override
If this is a QmlTypeNode, this function returns the pointer to the C++ ClassNode that this QML type r...
static void subclasses(const Node *base, NodeList &subs, bool recurse=false)
Loads the list subs with the nodes of all the subclasses of base.
QmlTypeNode * qmlBaseNode() const override
If this Aggregate is a QmlTypeNode, this function returns a pointer to the QmlTypeNode that is its ba...
CollectionNode * logicalModule() const override
If this is a QmlTypeNode, a pointer to its QML module is returned, which is a pointer to a Collection...
A class for containing the elements of one documentation section.
ClassNodesList & classNodesList()
A class for creating vectors of collections for documentation.
SectionVector & summarySections()
Sections(const Aggregate *aggregate)
This constructor builds the section vectors based on the type of the aggregate node.
SectionVector & detailsSections()
Section & allMembersSection()
const Atom * firstAtom() const
static std::string comparisonCategoryAsString(ComparisonCategory category)
#define CONFIG_CPPCLASSESTITLE
#define CONFIG_QMLTYPESPAGE
#define CONFIG_CPPCLASSESPAGE
#define CONFIG_NAVIGATION
#define CONFIG_LANDINGPAGE
#define CONFIG_LANDINGTITLE
#define CONFIG_QMLTYPESTITLE
QMap< QString, Node * > NodeMap
QList< ClassNodes > ClassNodesList
A single entry in an all-members listing page.
Intermediate representation of the all-members listing page.
Intermediate representation of a single documentable member.
Intermediate representation of a function parameter.
Represents a single span within a structured signature.
The Node class is the base class for all the nodes in QDoc's parse tree.
const Doc & doc() const
Returns a reference to the node's Doc data member.
bool isQmlNode() const
Returns true if this node's Genus value is QML.
bool isGroup() const
Returns true if the node type is Group.
bool isNamespace() const
Returns true if the node type is Namespace.
bool isTypedef() const
Returns true if the node type is Typedef.
bool isQmlBasicType() const
Returns true if the node type is QmlBasicType.
ComparisonCategory comparisonCategory() const
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
bool isHeader() const
Returns true if the node type is HeaderFile.
NodeType nodeType() const override
Returns this node's type.
Genus genus() const override
Returns this node's Genus.
bool isEnumType() const
Returns true if the node type is Enum.
virtual Status status() const
Returns the node's status value.
Aggregate * parent() const
Returns the node's parent pointer.
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
static bool nodeNameLessThan(const Node *first, const Node *second)
Returns true if the node n1 is less than node n2.
bool isProxyNode() const
Returns true if the node type is Proxy.
const std::optional< RelaxedTemplateDeclaration > & templateDecl() const
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
ThreadSafeness threadSafeness() const
Returns the thread safeness value for whatever this node represents.
bool isProperty() const
Returns true if the node type is Property.
NodeContext createContext() const
bool isModule() const
Returns true if the node type is Module.
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
bool isRelatedNonmember() const
Returns true if this is a related nonmember of something.
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
bool isQmlModule() const
Returns true if the node type is QmlModule.
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
A class for parsing and managing a function parameter list.
const Parameter & at(int i) const