96 Q_ASSERT_X(pn,
"NodeExtractor::extractPageMetadata",
97 "PageNode pointer must be non-null");
106 const auto *qcn =
static_cast<
const QmlTypeNode *>(pn);
107 QString suffix = qcn->isQmlBasicType() ?
" QML Value Type"_L1 :
" QML Type"_L1;
108 pm.title = pn->name() + suffix;
109 pm.fullTitle = pm.title;
114 const auto *aggregate =
static_cast<
const Aggregate *>(pn);
115 pm.fullTitle = aggregate->plainFullName();
116 pm.title = pm.fullTitle;
118 pm.title = pn->title();
119 pm.fullTitle = pn->fullTitle();
122 pm.url = hrefResolver->fileName(pn);
123 const QString baseUrl = Config::instance().get(
CONFIG_URL).asString();
124 if (!baseUrl.isEmpty() && !pm.url.isEmpty() && !pm.url.contains(
"://"_L1)) {
126 + (baseUrl.endsWith(u'/') ? QString{} : QStringLiteral(
"/"))
129 pm.since = pn->since();
130 pm.deprecatedSince = pn->deprecatedSince();
131 pm.brief = pn
->doc().briefText().toString();
139 const int headingOffset = [&] {
158 diagnosticHandlerFor(pn));
159 pm.body = contentBuilder.build(firstAtom);
163 const auto *aggregate =
static_cast<
const Aggregate *>(pn);
164 pm.summarySections = extractSummarySections(aggregate, hrefResolver);
165 pm.detailSections = extractDetailSections(aggregate, hrefResolver);
169 const auto *qcn =
static_cast<
const QmlTypeNode *>(pn);
170 pm.qmlTypeData = extractQmlTypeData(qcn, hrefResolver);
174 const auto *cn =
static_cast<
const CollectionNode *>(pn);
175 pm.collectionData = extractCollectionData(cn, hrefResolver);
179 const auto *aggregate =
static_cast<
const Aggregate *>(pn);
180 pm.cppReferenceData = extractCppReferenceData(aggregate, hrefResolver);
199 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
201 if (!qcn->logicalModuleName().isEmpty()) {
202 bool includeImport =
true;
209 QStringList parts = QStringList()
210 <<
"import"_L1 << qcn->logicalModuleName() << qcn->logicalModuleVersion();
211 data.importStatement = parts.join(
' '_L1).trimmed();
231 inheritsInfo.name = base->name();
232 inheritsInfo.href = resolveHref(hrefResolver, base, qcn);
237 inheritsInfo.moduleName = base->logicalModuleName();
239 data.inherits = inheritsInfo;
242 if (!subs.isEmpty()) {
243 QList<IR::QmlTypeData::InheritedByEntry> filteredSubs;
244 for (
const auto *sub : std::as_const(subs)) {
245 const NodeContext context = sub->createContext();
246 if (InclusionFilter::isIncluded(policy, context))
247 filteredSubs.append({sub->name(), resolveHref(hrefResolver, sub, qcn)});
249 std::sort(filteredSubs.begin(), filteredSubs.end(),
252 return a.name < b.name;
254 data.inheritedBy = filteredSubs;
260 if (InclusionFilter::isIncluded(policy, context))
261 data.nativeType = IR::QmlTypeData::NativeTypeInfo{cn->name(), resolveHref(hrefResolver, cn, qcn)};
284 data.logicalModuleName = cn->logicalModuleName();
285 data.logicalModuleVersion = cn->logicalModuleVersion();
286 data.qtVariable = cn->qtVariable();
303 const QString rawCmakePackage = cn->cmakePackage();
304 const QString rawCmakeComponent = cn->cmakeComponent();
305 const QString rawCmakeTargetItem = cn->cmakeTargetItem();
306 if (!rawCmakePackage.isEmpty() || !rawCmakeComponent.isEmpty()) {
307 const QString package = rawCmakePackage.isEmpty()
308 ?
"Qt"_L1 + QString::number(QT_VERSION_MAJOR)
310 data.cmakePackage = package;
311 data.cmakeComponent = rawCmakeComponent;
312 if (!rawCmakeTargetItem.isEmpty())
313 data.cmakeTargetItem = rawCmakeTargetItem;
314 else if (!rawCmakeComponent.isEmpty())
315 data.cmakeTargetItem = package +
"::"_L1 + rawCmakeComponent;
318 data.state = cn->state();
328 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
331 return { node->name(), resolveHref(hrefResolver, node, cn), node->doc().briefText().toString() };
334 auto sortEntries = [](QList<IR::CollectionData::MemberEntry> &entries) {
335 std::sort(entries.begin(), entries.end(),
338 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
344 for (
auto *node : nsMap.values()) {
345 const NodeContext context = node->createContext();
346 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
347 data.namespaces.append(makeMemberEntry(node));
349 sortEntries(data.namespaces);
352 for (
auto *node : classMap.values()) {
353 const NodeContext context = node->createContext();
354 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
355 data.classes.append(makeMemberEntry(node));
357 sortEntries(data.classes);
359 for (
const auto *node : cn->members()) {
360 if (!node->isInAPI())
362 const NodeContext context = node->createContext();
363 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
364 data.members.append(makeMemberEntry(node));
366 sortEntries(data.members);
396 data.typeWord = aggregate->typeWord(
false);
399 auto ancestors = aggregate->plainFullName().split(
"::"_L1);
400 ancestors.pop_back();
401 data.ancestorNames = ancestors;
403 if (aggregate->includeFile())
404 data.headerInclude = *aggregate->includeFile();
406 if (!aggregate->physicalModuleName().isEmpty()) {
409 if (cn && (!cn->cmakeComponent().isEmpty() || !cn->cmakePackage().isEmpty())) {
410 const QString package = cn->cmakePackage().isEmpty()
411 ?
"Qt"_L1 + QString::number(QT_VERSION_MAJOR)
412 : cn->cmakePackage();
414 if (cn->cmakeComponent().isEmpty())
415 findPkg =
"find_package("_L1 + package +
" REQUIRED)"_L1;
417 findPkg =
"find_package("_L1 + package +
" REQUIRED COMPONENTS "_L1
418 + cn->cmakeComponent() +
")"_L1;
421 if (!cn->cmakeTargetItem().isEmpty()) {
422 target = cn->cmakeTargetItem();
423 }
else if (cn->cmakeComponent().isEmpty()) {
424 target = package +
"::"_L1 + package;
426 target = package +
"::"_L1 + cn->cmakeComponent();
429 data.cmakeFindPackage = findPkg;
430 data.cmakeTargetLinkLibraries =
431 "target_link_libraries(mytarget PRIVATE "_L1 + target +
")"_L1;
433 if (cn && !cn->qtVariable().isEmpty())
434 data.qmakeVariable =
"QT += "_L1 + cn->qtVariable();
437 auto statusOpt = formatStatus(aggregate, qdb);
439 data.statusText = *statusOpt;
440 if (aggregate->status() == Status::Deprecated)
441 data.statusCssClass =
"deprecated"_L1;
442 else if (!aggregate->deprecatedSince().isEmpty())
443 data.statusCssClass =
"pending-deprecation"_L1;
444 else if (aggregate->status() == Status::Preliminary)
445 data.statusCssClass =
"preliminary"_L1;
447 data.statusCssClass =
"status"_L1;
451 auto *classNode =
const_cast<ClassNode *>(
static_cast<
const ClassNode *>(aggregate));
453 if (classNode->isQmlNativeType()) {
454 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
455 const NodeContext context = classNode->createContext();
457 QList<QmlTypeNode *> nativeTypes{classNode->qmlNativeTypes().cbegin(),
458 classNode->qmlNativeTypes().cend()};
459 if (!nativeTypes.isEmpty()) {
461 data.qmlNativeType = IR::CppReferenceData::QmlNativeTypeLink{
462 nativeTypes.first()->name(),
463 resolveHref(hrefResolver, nativeTypes.first(), aggregate)
469 const auto *metaTags = classNode->doc().metaTagMap();
470 if (metaTags && metaTags->contains(u"qdoc-suppress-inheritance"_s))
474 const auto &baseClasses = classNode->baseClasses();
475 for (
const auto &bc : baseClasses) {
477 data.baseClasses.append({
478 bc.m_node->plainFullName(),
479 resolveHref(hrefResolver, bc.m_node, aggregate),
485 const auto &derivedClasses = classNode->derivedClasses();
486 for (
const auto &dc : derivedClasses) {
488 data.derivedClasses.append({
489 dc.m_node->plainFullName(),
490 resolveHref(hrefResolver, dc.m_node, aggregate)
494 std::sort(data.derivedClasses.begin(), data.derivedClasses.end(),
497 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
503 data.templateDeclSpans = buildTemplateDeclSpans(&*aggregate->templateDecl());
507 if (selfCategory != ComparisonCategory::None)
508 data.selfComparisonCategory = QString::fromStdString(comparisonCategoryAsString(selfCategory));
510 const auto *comparesMap = aggregate
->doc().comparesWithMap();
511 if (comparesMap && !comparesMap->isEmpty()) {
512 for (
auto [key, description] : comparesMap->asKeyValueRange()) {
513 IR::CppReferenceData::ComparisonEntry entry;
514 entry.category = QString::fromStdString(comparisonCategoryAsString(key));
516 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
517 entry.comparableTypes = types;
519 if (description.firstAtom()->next() != description.lastAtom()) {
520 Text descText = Text::subText(description.firstAtom()->next(),
521 description.lastAtom());
522 entry.description = descText.toString();
524 data.comparisonEntries.append(entry);
532 case Node::NonReentrant:
533 tsInfo.level =
"non-reentrant"_L1;
535 case Node::Reentrant:
536 tsInfo.level =
"reentrant"_L1;
538 case Node::ThreadSafe:
539 tsInfo.level =
"thread-safe"_L1;
545 NodeList reentrant, threadsafe, nonreentrant;
546 bool hasExceptions =
false;
547 for (
const auto *child : aggregate->childNodes()) {
548 if (!child->isDeprecated()) {
549 switch (child->threadSafeness()) {
550 case Node::Reentrant:
551 reentrant.append(
const_cast<Node *>(child));
552 if (ts == Node::ThreadSafe) hasExceptions =
true;
554 case Node::ThreadSafe:
555 threadsafe.append(
const_cast<Node *>(child));
556 if (ts == Node::Reentrant) hasExceptions =
true;
558 case Node::NonReentrant:
559 nonreentrant.append(
const_cast<Node *>(child));
560 hasExceptions =
true;
568 for (
const auto *node : std::as_const(reentrant)) {
569 tsInfo.reentrantExceptions.append({
570 node->plainFullName(),
571 resolveHref(hrefResolver, node, aggregate)
574 for (
const auto *node : std::as_const(threadsafe)) {
575 tsInfo.threadSafeExceptions.append({
576 node->plainFullName(),
577 resolveHref(hrefResolver, node, aggregate)
580 for (
const auto *node : std::as_const(nonreentrant)) {
581 tsInfo.nonReentrantExceptions.append({
582 node->plainFullName(),
583 resolveHref(hrefResolver, node, aggregate)
587 data.threadSafety =
std::move(tsInfo);
590 const QStringList &groupNames = aggregate->groupNames();
591 if (!groupNames.isEmpty()) {
593 for (
const auto &groupName : groupNames) {
594 auto it = groupMap.find(groupName);
595 if (it == groupMap.end() || !*it)
597 CollectionNode *group = *it;
602 qdb->mergeCollections(group);
603 if (group->wasSeen()) {
606 resolveHref(hrefResolver, group, aggregate)
613 const auto *ns =
static_cast<
const NamespaceNode *>(aggregate);
614 if (!ns->hasDoc() && ns->docNode()) {
616 data.fullNamespaceHref = resolveHref(hrefResolver, ns->docNode(), aggregate);
617 data.fullNamespaceModuleName = ns->docNode()->tree()->camelCaseModuleName();
638 QList<IR::SectionIR> result;
639 for (
const auto §ion : sv) {
640 if (section.isEmpty())
643 IR::SectionIR irSection;
644 irSection.title = section.title();
645 irSection.id = TextUtils::asAsciiPrintable(section.title());
646 irSection.singular = section.singular();
647 irSection.plural = section.plural();
653 for (
const auto *member : section.members()) {
654 if (member->isSharedCommentNode()) {
655 const auto *scn =
static_cast<
const SharedCommentNode *>(member);
656 for (
const auto *child : scn->collective()) {
657 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate);
658 irMember.href =
"#"_L1 + hrefResolver->anchorForNode(child);
659 irSection.members.append(irMember);
662 IR::MemberIR irMember = extractMemberIR(member, hrefResolver, aggregate);
663 irMember.href =
"#"_L1 + hrefResolver->anchorForNode(member);
664 irSection.members.append(irMember);
668 for (
const auto *reimpl : section.reimplementedMembers())
669 irSection.reimplementedMembers.append(extractMemberIR(reimpl, hrefResolver, aggregate));
671 for (
const auto &[base, count] : section.inheritedMembers()) {
672 IR::InheritedMembersIR inherited;
673 inherited.className = base->plainFullName();
674 inherited.count = count;
675 inherited.href = resolveHref(hrefResolver, base, aggregate);
676 irSection.inheritedMembers.append(inherited);
679 result.append(irSection);
698 QList<IR::SectionIR> result;
699 for (
const auto §ion : sv) {
700 if (section.isEmpty())
703 IR::SectionIR irSection;
704 irSection.title = section.title();
705 irSection.id = TextUtils::asAsciiPrintable(section.title());
706 irSection.singular = section.singular();
707 irSection.plural = section.plural();
709 for (
const auto *member : section.members()) {
710 if (member->isSharedCommentNode()) {
711 const auto *scn =
static_cast<
const SharedCommentNode *>(member);
713 QList<IR::ContentBlock> sharedBody;
714 const Text &bodyText = scn->doc().body();
715 if (
const Atom *firstAtom = bodyText.firstAtom()) {
716 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
717 diagnosticHandlerFor(scn));
718 sharedBody = contentBuilder.build(firstAtom);
721 QList<IR::ContentBlock> sharedAlso;
722 const QList<Text> &alsoTexts = scn->doc().alsoList();
723 for (
const Text &alsoText : alsoTexts) {
724 if (
const Atom *firstAtom = alsoText.firstAtom()) {
725 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
726 diagnosticHandlerFor(scn));
727 sharedAlso.append(contentBuilder.build(firstAtom));
731 for (
const auto *child : scn->collective()) {
732 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate, MemberExtractionLevel::Detail);
733 irMember.body = sharedBody;
734 irMember.alsoList = sharedAlso;
735 irSection.members.append(irMember);
738 irSection.members.append(extractMemberIR(member, hrefResolver, aggregate, MemberExtractionLevel::Detail));
742 result.append(irSection);
778 member.name = node->name();
779 member.fullName = node->plainFullName();
780 member.href = resolveHref(hrefResolver, node, relative);
788 const auto *fn =
static_cast<
const FunctionNode *>(node);
789 member.signature = fn->signature(
800 for (
int i = 0; i < params
.count(); ++i) {
802 param.type = params
.at(i
).type();
803 param.name = params
.at(i
).name();
804 param.defaultValue = params
.at(i
).defaultValue();
805 member.parameters.append(param);
808 const auto *en =
static_cast<
const EnumNode *>(node);
809 member.signature = en->isScoped()
810 ? QStringLiteral(
"enum class %1").arg(en->name())
811 : QStringLiteral(
"enum %1").arg(en->name());
813 for (
const auto &item : en->items()) {
815 ev.name = item.name();
816 ev.value = item.value();
817 ev.since = item.since();
818 member.enumValues.append(ev);
821 const auto *qpn =
static_cast<
const QmlPropertyNode *>(node);
822 member.signature = qpn->name() +
" : "_L1 + qpn->dataType();
823 member.dataType = qpn->dataType();
829 const auto *pn =
static_cast<
const PropertyNode *>(node);
830 member.signature = pn->name() +
" : "_L1 + pn->qualifiedDataType();
832 const auto *td =
static_cast<
const TypedefNode *>(node);
833 member.signature = td->associatedEnum()
834 ?
"flags "_L1 + td->name()
837 const auto *vn =
static_cast<
const VariableNode *>(node);
838 member.signature = vn->leftType() + vn->name() + vn->rightType();
840 member.signature = node->name();
844 member.anchorId = hrefResolver->anchorForNode(node);
845 member.synopsis = member.signature;
846 member.since = node->since();
847 member.threadSafety = threadSafenessString(node->threadSafeness());
851 member.comparisonCategory = QString::fromStdString(catStr);
854 const auto *fn =
static_cast<
const FunctionNode *>(node);
855 const auto &noexcept_ = fn->getNoexcept();
858 member.noexceptNote = *noexcept_;
865 diagnosticHandlerFor(node));
866 member.body = contentBuilder.build(firstAtom);
869 const QList<Text> &alsoTexts = node
->doc().alsoList();
870 for (
const Text &alsoText : alsoTexts) {
871 if (
const Atom *firstAtom = alsoText.firstAtom()) {
872 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
873 diagnosticHandlerFor(node));
874 QList<IR::ContentBlock> blocks = contentBuilder.build(firstAtom);
875 member.alsoList.append(blocks);
881 member.signatureSpans = buildSignatureSpans(node, hrefResolver, relative, spanStyle);
975 declSpan.text =
"template"_L1;
980 declSpan.children.append(open);
983 for (
const auto ¶m : templateDecl->parameters) {
984 if (param.sfinae_constraint)
987 IR::SignatureSpan comma;
988 comma.role = IR::SpanRole::Text;
989 comma.text =
", "_L1;
990 declSpan.children.append(comma);
993 switch (param.kind) {
994 case RelaxedTemplateParameter::Kind::TypeTemplateParameter:
995 case RelaxedTemplateParameter::Kind::TemplateTemplateParameter: {
996 IR::SignatureSpan kw;
997 kw.role = IR::SpanRole::Text;
998 kw.text =
"typename"_L1;
999 declSpan.children.append(kw);
1002 case RelaxedTemplateParameter::Kind::NonTypeTemplateParameter: {
1003 if (!param.valued_declaration.type.empty()) {
1004 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.type));
1005 declSpan.children.append(typeSpans);
1011 if (param.is_parameter_pack) {
1012 IR::SignatureSpan dots;
1013 dots.role = IR::SpanRole::Text;
1014 dots.text =
"..."_L1;
1015 declSpan.children.append(dots);
1018 if (!param.valued_declaration.name.empty()) {
1019 IR::SignatureSpan space;
1020 space.role = IR::SpanRole::Text;
1021 space.text =
" "_L1;
1022 declSpan.children.append(space);
1024 IR::SignatureSpan nameSpan;
1025 nameSpan.role = IR::SpanRole::Parameter;
1026 nameSpan.text = QString::fromStdString(param.valued_declaration.name);
1027 declSpan.children.append(nameSpan);
1030 if (!param.valued_declaration.initializer.empty()) {
1031 IR::SignatureSpan eq;
1032 eq.role = IR::SpanRole::Text;
1034 declSpan.children.append(eq);
1036 if (param.kind == RelaxedTemplateParameter::Kind::TypeTemplateParameter
1037 || param.kind == RelaxedTemplateParameter::Kind::TemplateTemplateParameter) {
1038 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.initializer));
1039 declSpan.children.append(typeSpans);
1041 IR::SignatureSpan val;
1042 val.role = IR::SpanRole::Text;
1043 val.text = QString::fromStdString(param.valued_declaration.initializer);
1044 declSpan.children.append(val);
1053 close.text =
">"_L1;
1054 declSpan.children.append(close);
1056 if (templateDecl->requires_clause && !templateDecl->requires_clause->empty()) {
1059 req.text =
" requires "_L1 + QString::fromStdString(*templateDecl->requires_clause);
1060 declSpan.children.append(req);
1063 return { declSpan };
1067 const HrefResolver *hrefResolver,
1068 const Node *relative,
1071 Q_UNUSED(hrefResolver);
1072 QList<IR::SignatureSpan> spans;
1074 auto appendText = [&spans](
const QString &text) {
1081 auto appendName = [&spans, node, hrefResolver, relative](
const QString &name) {
1085 span.href = resolveHref(hrefResolver, node, relative);
1089 auto appendTypeSpans = [&spans](
const QString &type,
bool trailingSpace) {
1090 auto typeSpans = buildTypeSpans(type);
1091 spans.append(typeSpans);
1092 if (trailingSpace && !type.isEmpty()
1093 && !type.endsWith(
'*'_L1) && !type.endsWith(
'&'_L1)) {
1096 space.text =
" "_L1;
1097 spans.append(space);
1103 auto extras = buildExtraSpans(node, style);
1104 if (!extras.isEmpty()) {
1105 spans.append(extras);
1111 QString nameText = node->name();
1118 nameText = node->parent()->name() +
"::"_L1 + nameText;
1127 appendText(Node::nodeTypeString(node->nodeType()) +
" "_L1);
1129 appendName(nameText);
1133 span.text = nameText;
1138 const auto *func =
static_cast<
const FunctionNode *>(node);
1141 if (
auto templateDecl = node->templateDecl()) {
1142 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl);
1143 spans.append(tmplSpans);
1149 if (!func->isNonvirtual())
1150 appendText(
"virtual "_L1);
1154 appendTypeSpans(func->returnTypeString(),
true);
1157 appendName(nameText);
1161 span.text = nameText;
1165 if (!func->isMacroWithoutParams()) {
1167 if (!func->parameters().isEmpty()) {
1168 const Parameters ¶meters = func->parameters();
1169 for (
int i = 0; i < parameters
.count(); ++i) {
1171 appendText(
", "_L1);
1173 QString pName = param.name();
1174 QString type = param.type();
1175 QString value = param.defaultValue();
1176 qsizetype insertPos = param.nameInsertionPoint();
1178 appendTypeSpans(type.left(insertPos),
false);
1181 paramSpan.text = pName;
1182 spans.append(paramSpan);
1183 appendTypeSpans(type.mid(insertPos),
false);
1186 appendTypeSpans(type, trailingSpace);
1190 paramSpan.text = pName;
1191 spans.append(paramSpan);
1195 appendText(
" = "_L1 + value);
1201 if (func->isConst())
1202 appendText(
" const"_L1);
1205 if (func->isFinal())
1206 appendText(
" final"_L1);
1207 if (func->isOverride())
1208 appendText(
" override"_L1);
1209 if (func->isPureVirtual())
1210 appendText(
" = 0"_L1);
1212 appendText(
" &"_L1);
1213 else if (func->isRefRef())
1214 appendText(
" &&"_L1);
1216 if (!func->returnType().isEmpty() && func->returnType() !=
"void"_L1) {
1217 appendText(
" : "_L1);
1218 appendTypeSpans(func->returnTypeString(),
false);
1222 appendText(
" &"_L1);
1223 else if (func->isRefRef())
1224 appendText(
" &&"_L1);
1225 if (
const auto &req = func->trailingRequiresClause(); req && !req->isEmpty())
1226 appendText(
" requires "_L1 + *req);
1231 const auto *enume =
static_cast<
const EnumNode *>(node);
1232 appendText(
"enum"_L1);
1233 if (enume->isScoped())
1234 appendText(
" class"_L1);
1235 if (!enume->isAnonymous()) {
1238 appendName(nameText);
1242 span.text = nameText;
1247 appendText(
" { "_L1);
1248 const int MaxEnumValues = 6;
1249 QStringList documentedItems = enume->doc().enumItemNames();
1250 if (documentedItems.isEmpty()) {
1251 const auto &enumItems = enume->items();
1252 for (
const auto &item : enumItems)
1253 documentedItems << item.name();
1255 const QStringList omitItems = enume->doc().omitEnumItemNames();
1256 for (
const auto &item : omitItems)
1257 documentedItems.removeAll(item);
1259 if (documentedItems.size() > MaxEnumValues) {
1260 const QString last = documentedItems.last();
1261 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
1262 documentedItems +=
"..."_L1;
1263 documentedItems += last;
1265 appendText(documentedItems.join(
", "_L1));
1266 if (!documentedItems.isEmpty())
1274 if (
auto templateDecl = node->templateDecl()) {
1275 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl);
1276 spans.append(tmplSpans);
1281 appendName(nameText);
1285 span.text = nameText;
1291 if (
static_cast<
const TypedefNode *>(node)->associatedEnum())
1292 appendText(
"flags "_L1);
1294 appendName(nameText);
1298 span.text = nameText;
1304 const auto *property =
static_cast<
const PropertyNode *>(node);
1306 appendName(nameText);
1310 span.text = nameText;
1313 appendText(
" : "_L1);
1314 appendTypeSpans(property->qualifiedDataType(),
false);
1318 const auto *property =
static_cast<
const QmlPropertyNode *>(node);
1320 appendName(nameText);
1324 span.text = nameText;
1327 appendText(
" : "_L1);
1328 appendTypeSpans(property->dataType(),
false);
1332 const auto *variable =
static_cast<
const VariableNode *>(node);
1335 appendName(nameText);
1339 span.text = nameText;
1342 appendText(
" : "_L1);
1343 appendTypeSpans(variable->dataType(),
false);
1345 appendTypeSpans(variable->leftType(),
true);
1347 appendName(nameText);
1351 span.text = nameText;
1354 appendText(variable->rightType());
1360 appendName(nameText);
1364 span.text = nameText;
1492 result.typeName = qcn->name();
1493 result.typeHref = resolveHref(hrefResolver, qcn, qcn);
1498 if (groupedMembers.isEmpty())
1501 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1504 IR::AllMemberEntry entry;
1505 entry.signatureSpans = buildQmlItemSpans(node, hrefResolver);
1506 entry.signature = plainTextFromSpans(entry.signatureSpans);
1507 entry.href = resolveHref(hrefResolver, node, qcn);
1509 if (node->isQmlProperty()) {
1510 auto *qpn =
static_cast<QmlPropertyNode *>(node);
1511 QStringList qmlHints = qpn->hints();
1512 if (qpn->isAttached() && !qmlHints.contains(
"attached"_L1))
1513 qmlHints <<
"attached"_L1;
1514 for (
const auto &h : std::as_const(qmlHints))
1515 entry.hints.append(h);
1516 }
else if (node->isAttached()) {
1517 entry.hints.append(
"attached"_L1);
1520 if (node->isPropertyGroup()) {
1521 entry.isPropertyGroup =
true;
1522 const auto *scn =
static_cast<SharedCommentNode *>(node);
1523 for (
auto *child : scn->collective()) {
1524 const NodeContext childContext = child->createContext();
1525 if (!InclusionFilter::isIncluded(policy, childContext))
1527 entry.children.append(buildEntry(child));
1534 auto isVisible = [&policy](
Node *node) {
1536 return InclusionFilter::isIncluded(policy, context)
1537 && !(node->isSharingComment() && node->sharedCommentNode()->isPropertyGroup());
1540 for (
const auto &[originType, nodes] : groupedMembers) {
1541 Q_ASSERT(originType);
1542 if (nodes.isEmpty())
1545 IR::MemberGroup group;
1546 if (originType != qcn) {
1547 group.typeName = originType->name();
1548 group.typeHref = resolveHref(hrefResolver, originType, qcn);
1551 for (
auto *node : nodes) {
1552 if (isVisible(node))
1553 group.members.append(buildEntry(node));
1556 result.memberGroups.append(group);
1662 const Config &config = Config::instance();
1666 const QString homepage = config.get(navDot +
CONFIG_HOMEPAGE).asString();
1667 const QString hometitle = config.get(navDot +
CONFIG_HOMETITLE).asString(homepage);
1669 const QString landingtitle = config.get(navDot +
CONFIG_LANDINGTITLE).asString(landingpage);
1673 const QString qmltypestitle = config.get(navDot +
CONFIG_QMLTYPESTITLE).asString(
"QML Types"_L1);
1675 const QString pageTitle = pn->title();
1678 auto resolveCrumb = [&](
const QString &targetName)
1679 -> std::pair<QString, CrumbState> {
1680 const Node *target = qdb->findNodeForTarget(targetName, pn);
1682 return {{}, CrumbState::Unresolved};
1684 return {{}, CrumbState::Current};
1685 return {resolveHref(hrefResolver, target, pn), CrumbState::Link};
1688 if (!homepage.isEmpty()) {
1689 auto [href, state] = resolveCrumb(homepage);
1690 if (state == CrumbState::Current)
1692 nav.breadcrumbs.append({hometitle,
std::move(href), state});
1695 if (!landingpage.isEmpty()) {
1696 auto [href, state] = resolveCrumb(landingpage);
1697 if (state != CrumbState::Current)
1698 nav.breadcrumbs.append({landingtitle,
std::move(href), state});
1702 if (!cppclassespage.isEmpty() && !cppclassestitle.isEmpty()) {
1703 auto [href, state] = resolveCrumb(cppclassespage);
1704 nav.breadcrumbs.append({cppclassestitle,
std::move(href), state});
1708 QString moduleState;
1709 if (moduleNode && !moduleNode->state().isEmpty())
1710 moduleState = QStringLiteral(
" (%1)").arg(moduleNode->state());
1712 if (!pn->physicalModuleName().isEmpty() && moduleNode
1713 && (!moduleState.isEmpty() || moduleNode->title() != cppclassespage)) {
1714 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1715 resolveHref(hrefResolver, moduleNode, pn),
1718 nav.breadcrumbs.append({pn->name(), {}, CrumbState
::Current});
1720 if (!qmltypespage.isEmpty() && !qmltypestitle.isEmpty()) {
1721 auto [href, state] = resolveCrumb(qmltypespage);
1722 nav.breadcrumbs.append({qmltypestitle,
std::move(href), state});
1726 QString moduleState;
1727 if (moduleNode && !moduleNode->state().isEmpty())
1728 moduleState = QStringLiteral(
" (%1)").arg(moduleNode->state());
1731 && (!moduleState.isEmpty() || moduleNode->title() != qmltypespage)) {
1732 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1733 resolveHref(hrefResolver, moduleNode, pn),
1736 nav.breadcrumbs.append({pn->name(), {}, CrumbState
::Current});
1738 auto currentNode = pn;
1739 std::deque<
const Node *> navNodes;
1740 qsizetype navItems = 0;
1742 if (
std::find(navNodes.cbegin(), navNodes.cend(),
1747 if (navNodes.empty()) {
1748 const QStringList groups = pn->groupNames();
1749 for (
const auto &groupName : groups) {
1750 const auto *groupNode = qdb->findNodeByNameAndType(
1751 QStringList{groupName}, &Node::isGroup);
1752 if (groupNode && !groupNode->title().isEmpty()) {
1753 navNodes.push_front(groupNode);
1758 for (
const auto *navNode : navNodes) {
1759 if (navNode->isPageNode())
1760 nav.breadcrumbs.append({navNode->title(),
1761 resolveHref(hrefResolver, navNode, pn),
1764 if (!nav.breadcrumbs.isEmpty())
1765 nav.breadcrumbs.append({pageTitle, {}, CrumbState
::Current});
1768 const auto &linkMap = pn->links();
1769 if (linkMap.contains(Node::PreviousLink)) {
1770 const auto &linkPair = linkMap[Node::PreviousLink];
1771 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1774 if (target && target != pn) {
1775 href = resolveHref(hrefResolver, target, pn);
1776 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1777 ? target->title() : linkPair.second;
1779 href = linkPair.first;
1780 title = linkPair.second;
1784 if (linkMap.contains(Node::NextLink)) {
1785 const auto &linkPair = linkMap[Node::NextLink];
1786 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1789 if (target && target != pn) {
1790 href = resolveHref(hrefResolver, target, pn);
1791 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1792 ? target->title() : linkPair.second;
1794 href = linkPair.first;
1795 title = linkPair.second;
1799 if (linkMap.contains(Node::StartLink)) {
1800 const auto &linkPair = linkMap[Node::StartLink];
1801 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1804 if (target && target != pn) {
1805 href = resolveHref(hrefResolver, target, pn);
1806 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1807 ? target->title() : linkPair.second;
1809 href = linkPair.first;
1810 title = linkPair.second;
1815 const QString formatDot =
"HTML"_L1 + Config::dot;
1816 nav
.tocDepth = config.get(formatDot +
"tocdepth"_L1).asInt();