193 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
195 if (!qcn->logicalModuleName().isEmpty()) {
196 bool includeImport =
true;
203 QStringList parts = QStringList()
204 <<
"import"_L1 << qcn->logicalModuleName() << qcn->logicalModuleVersion();
205 data.importStatement = parts.join(
' '_L1).trimmed();
225 inheritsInfo.name = base->name();
226 inheritsInfo.href = resolveHref(hrefResolver, base, qcn);
231 inheritsInfo.moduleName = base->logicalModuleName();
233 data.inherits = inheritsInfo;
236 if (!subs.isEmpty()) {
237 QList<IR::QmlTypeData::InheritedByEntry> filteredSubs;
238 for (
const auto *sub : std::as_const(subs)) {
239 const NodeContext context = sub->createContext();
240 if (InclusionFilter::isIncluded(policy, context))
241 filteredSubs.append({sub->name(), resolveHref(hrefResolver, sub, qcn)});
243 std::sort(filteredSubs.begin(), filteredSubs.end(),
246 return a.name < b.name;
248 data.inheritedBy = filteredSubs;
254 if (InclusionFilter::isIncluded(policy, context))
255 data.nativeType = IR::QmlTypeData::NativeTypeInfo{cn->name(), resolveHref(hrefResolver, cn, qcn)};
278 data.logicalModuleName = cn->logicalModuleName();
279 data.logicalModuleVersion = cn->logicalModuleVersion();
280 data.qtVariable = cn->qtVariable();
297 const QString rawCmakePackage = cn->cmakePackage();
298 const QString rawCmakeComponent = cn->cmakeComponent();
299 const QString rawCmakeTargetItem = cn->cmakeTargetItem();
300 if (!rawCmakePackage.isEmpty() || !rawCmakeComponent.isEmpty()) {
301 const QString package = rawCmakePackage.isEmpty()
302 ?
"Qt"_L1 + QString::number(QT_VERSION_MAJOR)
304 data.cmakePackage = package;
305 data.cmakeComponent = rawCmakeComponent;
306 if (!rawCmakeTargetItem.isEmpty())
307 data.cmakeTargetItem = rawCmakeTargetItem;
308 else if (!rawCmakeComponent.isEmpty())
309 data.cmakeTargetItem = package +
"::"_L1 + rawCmakeComponent;
312 data.state = cn->state();
322 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
325 return { node->name(), resolveHref(hrefResolver, node, cn), node->doc().briefText().toString() };
328 auto sortEntries = [](QList<IR::CollectionData::MemberEntry> &entries) {
329 std::sort(entries.begin(), entries.end(),
332 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
338 for (
auto *node : nsMap.values()) {
339 const NodeContext context = node->createContext();
340 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
341 data.namespaces.append(makeMemberEntry(node));
343 sortEntries(data.namespaces);
346 for (
auto *node : classMap.values()) {
347 const NodeContext context = node->createContext();
348 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
349 data.classes.append(makeMemberEntry(node));
351 sortEntries(data.classes);
353 for (
const auto *node : cn->members()) {
354 if (!node->isInAPI())
356 const NodeContext context = node->createContext();
357 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
358 data.members.append(makeMemberEntry(node));
360 sortEntries(data.members);
390 data.typeWord = aggregate->typeWord(
false);
393 auto ancestors = aggregate->plainFullName().split(
"::"_L1);
394 ancestors.pop_back();
395 data.ancestorNames = ancestors;
397 if (aggregate->includeFile())
398 data.headerInclude = *aggregate->includeFile();
400 if (!aggregate->physicalModuleName().isEmpty()) {
403 if (cn && (!cn->cmakeComponent().isEmpty() || !cn->cmakePackage().isEmpty())) {
404 const QString package = cn->cmakePackage().isEmpty()
405 ?
"Qt"_L1 + QString::number(QT_VERSION_MAJOR)
406 : cn->cmakePackage();
408 if (cn->cmakeComponent().isEmpty())
409 findPkg =
"find_package("_L1 + package +
" REQUIRED)"_L1;
411 findPkg =
"find_package("_L1 + package +
" REQUIRED COMPONENTS "_L1
412 + cn->cmakeComponent() +
")"_L1;
415 if (!cn->cmakeTargetItem().isEmpty()) {
416 target = cn->cmakeTargetItem();
417 }
else if (cn->cmakeComponent().isEmpty()) {
418 target = package +
"::"_L1 + package;
420 target = package +
"::"_L1 + cn->cmakeComponent();
423 data.cmakeFindPackage = findPkg;
424 data.cmakeTargetLinkLibraries =
425 "target_link_libraries(mytarget PRIVATE "_L1 + target +
")"_L1;
427 if (cn && !cn->qtVariable().isEmpty())
428 data.qmakeVariable =
"QT += "_L1 + cn->qtVariable();
431 auto statusOpt = formatStatus(aggregate, qdb);
433 data.statusText = *statusOpt;
434 if (aggregate->status() == Status::Deprecated)
435 data.statusCssClass =
"deprecated"_L1;
436 else if (!aggregate->deprecatedSince().isEmpty())
437 data.statusCssClass =
"pending-deprecation"_L1;
438 else if (aggregate->status() == Status::Preliminary)
439 data.statusCssClass =
"preliminary"_L1;
441 data.statusCssClass =
"status"_L1;
445 auto *classNode =
const_cast<ClassNode *>(
static_cast<
const ClassNode *>(aggregate));
447 if (classNode->isQmlNativeType()) {
448 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
449 const NodeContext context = classNode->createContext();
451 QList<QmlTypeNode *> nativeTypes{classNode->qmlNativeTypes().cbegin(),
452 classNode->qmlNativeTypes().cend()};
453 if (!nativeTypes.isEmpty()) {
455 data.qmlNativeType = IR::CppReferenceData::QmlNativeTypeLink{
456 nativeTypes.first()->name(),
457 resolveHref(hrefResolver, nativeTypes.first(), aggregate)
463 const auto *metaTags = classNode->doc().metaTagMap();
464 if (metaTags && metaTags->contains(u"qdoc-suppress-inheritance"_s))
468 const auto &baseClasses = classNode->baseClasses();
469 for (
const auto &bc : baseClasses) {
471 data.baseClasses.append({
472 bc.m_node->plainFullName(),
473 resolveHref(hrefResolver, bc.m_node, aggregate),
479 const auto &derivedClasses = classNode->derivedClasses();
480 for (
const auto &dc : derivedClasses) {
482 data.derivedClasses.append({
483 dc.m_node->plainFullName(),
484 resolveHref(hrefResolver, dc.m_node, aggregate)
488 std::sort(data.derivedClasses.begin(), data.derivedClasses.end(),
491 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
497 data.templateDeclSpans = buildTemplateDeclSpans(&*aggregate->templateDecl());
501 if (selfCategory != ComparisonCategory::None)
502 data.selfComparisonCategory = QString::fromStdString(comparisonCategoryAsString(selfCategory));
504 const auto *comparesMap = aggregate
->doc().comparesWithMap();
505 if (comparesMap && !comparesMap->isEmpty()) {
506 for (
auto [key, description] : comparesMap->asKeyValueRange()) {
507 IR::CppReferenceData::ComparisonEntry entry;
508 entry.category = QString::fromStdString(comparisonCategoryAsString(key));
510 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
511 entry.comparableTypes = types;
513 if (description.firstAtom()->next() != description.lastAtom()) {
514 Text descText = Text::subText(description.firstAtom()->next(),
515 description.lastAtom());
516 entry.description = descText.toString();
518 data.comparisonEntries.append(entry);
526 case Node::NonReentrant:
527 tsInfo.level =
"non-reentrant"_L1;
529 case Node::Reentrant:
530 tsInfo.level =
"reentrant"_L1;
532 case Node::ThreadSafe:
533 tsInfo.level =
"thread-safe"_L1;
539 NodeList reentrant, threadsafe, nonreentrant;
540 bool hasExceptions =
false;
541 for (
const auto *child : aggregate->childNodes()) {
542 if (!child->isDeprecated()) {
543 switch (child->threadSafeness()) {
544 case Node::Reentrant:
545 reentrant.append(
const_cast<Node *>(child));
546 if (ts == Node::ThreadSafe) hasExceptions =
true;
548 case Node::ThreadSafe:
549 threadsafe.append(
const_cast<Node *>(child));
550 if (ts == Node::Reentrant) hasExceptions =
true;
552 case Node::NonReentrant:
553 nonreentrant.append(
const_cast<Node *>(child));
554 hasExceptions =
true;
562 for (
const auto *node : std::as_const(reentrant)) {
563 tsInfo.reentrantExceptions.append({
564 node->plainFullName(),
565 resolveHref(hrefResolver, node, aggregate)
568 for (
const auto *node : std::as_const(threadsafe)) {
569 tsInfo.threadSafeExceptions.append({
570 node->plainFullName(),
571 resolveHref(hrefResolver, node, aggregate)
574 for (
const auto *node : std::as_const(nonreentrant)) {
575 tsInfo.nonReentrantExceptions.append({
576 node->plainFullName(),
577 resolveHref(hrefResolver, node, aggregate)
581 data.threadSafety =
std::move(tsInfo);
584 const QStringList &groupNames = aggregate->groupNames();
585 if (!groupNames.isEmpty()) {
587 for (
const auto &groupName : groupNames) {
588 auto it = groupMap.find(groupName);
589 if (it == groupMap.end() || !*it)
591 CollectionNode *group = *it;
596 qdb->mergeCollections(group);
597 if (group->wasSeen()) {
600 resolveHref(hrefResolver, group, aggregate)
607 const auto *ns =
static_cast<
const NamespaceNode *>(aggregate);
608 if (!ns->hasDoc() && ns->docNode()) {
610 data.fullNamespaceHref = resolveHref(hrefResolver, ns->docNode(), aggregate);
611 data.fullNamespaceModuleName = ns->docNode()->tree()->camelCaseModuleName();
632 QList<IR::SectionIR> result;
633 for (
const auto §ion : sv) {
634 if (section.isEmpty())
637 IR::SectionIR irSection;
638 irSection.title = section.title();
639 irSection.id = TextUtils::asAsciiPrintable(section.title());
640 irSection.singular = section.singular();
641 irSection.plural = section.plural();
647 for (
const auto *member : section.members()) {
648 if (member->isSharedCommentNode()) {
649 const auto *scn =
static_cast<
const SharedCommentNode *>(member);
650 for (
const auto *child : scn->collective()) {
651 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate);
652 irMember.href =
"#"_L1 + hrefResolver->anchorForNode(child);
653 irSection.members.append(irMember);
656 IR::MemberIR irMember = extractMemberIR(member, hrefResolver, aggregate);
657 irMember.href =
"#"_L1 + hrefResolver->anchorForNode(member);
658 irSection.members.append(irMember);
662 for (
const auto *reimpl : section.reimplementedMembers())
663 irSection.reimplementedMembers.append(extractMemberIR(reimpl, hrefResolver, aggregate));
665 for (
const auto &[base, count] : section.inheritedMembers()) {
666 IR::InheritedMembersIR inherited;
667 inherited.className = base->plainFullName();
668 inherited.count = count;
669 inherited.href = resolveHref(hrefResolver, base, aggregate);
670 irSection.inheritedMembers.append(inherited);
673 result.append(irSection);
692 QList<IR::SectionIR> result;
693 for (
const auto §ion : sv) {
694 if (section.isEmpty())
697 IR::SectionIR irSection;
698 irSection.title = section.title();
699 irSection.id = TextUtils::asAsciiPrintable(section.title());
700 irSection.singular = section.singular();
701 irSection.plural = section.plural();
703 for (
const auto *member : section.members()) {
704 if (member->isSharedCommentNode()) {
705 const auto *scn =
static_cast<
const SharedCommentNode *>(member);
707 QList<IR::ContentBlock> sharedBody;
708 const Text &bodyText = scn->doc().body();
709 if (
const Atom *firstAtom = bodyText.firstAtom()) {
710 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
711 diagnosticHandlerFor(scn));
712 sharedBody = contentBuilder.build(firstAtom);
715 QList<IR::ContentBlock> sharedAlso;
716 const QList<Text> &alsoTexts = scn->doc().alsoList();
717 for (
const Text &alsoText : alsoTexts) {
718 if (
const Atom *firstAtom = alsoText.firstAtom()) {
719 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
720 diagnosticHandlerFor(scn));
721 sharedAlso.append(contentBuilder.build(firstAtom));
725 for (
const auto *child : scn->collective()) {
726 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate, MemberExtractionLevel::Detail);
727 irMember.body = sharedBody;
728 irMember.alsoList = sharedAlso;
729 irSection.members.append(irMember);
732 irSection.members.append(extractMemberIR(member, hrefResolver, aggregate, MemberExtractionLevel::Detail));
736 result.append(irSection);
772 member.name = node->name();
773 member.fullName = node->plainFullName();
774 member.href = resolveHref(hrefResolver, node, relative);
782 const auto *fn =
static_cast<
const FunctionNode *>(node);
783 member.signature = fn->signature(
794 for (
int i = 0; i < params
.count(); ++i) {
796 param.type = params
.at(i
).type();
797 param.name = params
.at(i
).name();
798 param.defaultValue = params
.at(i
).defaultValue();
799 member.parameters.append(param);
802 const auto *en =
static_cast<
const EnumNode *>(node);
803 member.signature = en->isScoped()
804 ? QStringLiteral(
"enum class %1").arg(en->name())
805 : QStringLiteral(
"enum %1").arg(en->name());
807 for (
const auto &item : en->items()) {
809 ev.name = item.name();
810 ev.value = item.value();
811 ev.since = item.since();
812 member.enumValues.append(ev);
815 const auto *qpn =
static_cast<
const QmlPropertyNode *>(node);
816 member.signature = qpn->name() +
" : "_L1 + qpn->dataType();
817 member.dataType = qpn->dataType();
823 const auto *pn =
static_cast<
const PropertyNode *>(node);
824 member.signature = pn->name() +
" : "_L1 + pn->qualifiedDataType();
826 const auto *td =
static_cast<
const TypedefNode *>(node);
827 member.signature = td->associatedEnum()
828 ?
"flags "_L1 + td->name()
831 const auto *vn =
static_cast<
const VariableNode *>(node);
832 member.signature = vn->leftType() + vn->name() + vn->rightType();
834 member.signature = node->name();
838 member.anchorId = hrefResolver->anchorForNode(node);
839 member.synopsis = member.signature;
840 member.since = node->since();
841 member.threadSafety = threadSafenessString(node->threadSafeness());
845 member.comparisonCategory = QString::fromStdString(catStr);
848 const auto *fn =
static_cast<
const FunctionNode *>(node);
849 const auto &noexcept_ = fn->getNoexcept();
852 member.noexceptNote = *noexcept_;
859 diagnosticHandlerFor(node));
860 member.body = contentBuilder.build(firstAtom);
863 const QList<Text> &alsoTexts = node
->doc().alsoList();
864 for (
const Text &alsoText : alsoTexts) {
865 if (
const Atom *firstAtom = alsoText.firstAtom()) {
866 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
867 diagnosticHandlerFor(node));
868 QList<IR::ContentBlock> blocks = contentBuilder.build(firstAtom);
869 member.alsoList.append(blocks);
875 member.signatureSpans = buildSignatureSpans(node, hrefResolver, relative, spanStyle);
969 declSpan.text =
"template"_L1;
974 declSpan.children.append(open);
977 for (
const auto ¶m : templateDecl->parameters) {
978 if (param.sfinae_constraint)
981 IR::SignatureSpan comma;
982 comma.role = IR::SpanRole::Text;
983 comma.text =
", "_L1;
984 declSpan.children.append(comma);
987 switch (param.kind) {
988 case RelaxedTemplateParameter::Kind::TypeTemplateParameter:
989 case RelaxedTemplateParameter::Kind::TemplateTemplateParameter: {
990 IR::SignatureSpan kw;
991 kw.role = IR::SpanRole::Text;
992 kw.text =
"typename"_L1;
993 declSpan.children.append(kw);
996 case RelaxedTemplateParameter::Kind::NonTypeTemplateParameter: {
997 if (!param.valued_declaration.type.empty()) {
998 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.type));
999 declSpan.children.append(typeSpans);
1005 if (param.is_parameter_pack) {
1006 IR::SignatureSpan dots;
1007 dots.role = IR::SpanRole::Text;
1008 dots.text =
"..."_L1;
1009 declSpan.children.append(dots);
1012 if (!param.valued_declaration.name.empty()) {
1013 IR::SignatureSpan space;
1014 space.role = IR::SpanRole::Text;
1015 space.text =
" "_L1;
1016 declSpan.children.append(space);
1018 IR::SignatureSpan nameSpan;
1019 nameSpan.role = IR::SpanRole::Parameter;
1020 nameSpan.text = QString::fromStdString(param.valued_declaration.name);
1021 declSpan.children.append(nameSpan);
1024 if (!param.valued_declaration.initializer.empty()) {
1025 IR::SignatureSpan eq;
1026 eq.role = IR::SpanRole::Text;
1028 declSpan.children.append(eq);
1030 if (param.kind == RelaxedTemplateParameter::Kind::TypeTemplateParameter
1031 || param.kind == RelaxedTemplateParameter::Kind::TemplateTemplateParameter) {
1032 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.initializer));
1033 declSpan.children.append(typeSpans);
1035 IR::SignatureSpan val;
1036 val.role = IR::SpanRole::Text;
1037 val.text = QString::fromStdString(param.valued_declaration.initializer);
1038 declSpan.children.append(val);
1047 close.text =
">"_L1;
1048 declSpan.children.append(close);
1050 if (templateDecl->requires_clause && !templateDecl->requires_clause->empty()) {
1053 req.text =
" requires "_L1 + QString::fromStdString(*templateDecl->requires_clause);
1054 declSpan.children.append(req);
1057 return { declSpan };
1061 const HrefResolver *hrefResolver,
1062 const Node *relative,
1065 Q_UNUSED(hrefResolver);
1066 QList<IR::SignatureSpan> spans;
1068 auto appendText = [&spans](
const QString &text) {
1075 auto appendName = [&spans, node, hrefResolver, relative](
const QString &name) {
1079 span.href = resolveHref(hrefResolver, node, relative);
1083 auto appendTypeSpans = [&spans](
const QString &type,
bool trailingSpace) {
1084 auto typeSpans = buildTypeSpans(type);
1085 spans.append(typeSpans);
1086 if (trailingSpace && !type.isEmpty()
1087 && !type.endsWith(
'*'_L1) && !type.endsWith(
'&'_L1)) {
1090 space.text =
" "_L1;
1091 spans.append(space);
1097 auto extras = buildExtraSpans(node, style);
1098 if (!extras.isEmpty()) {
1099 spans.append(extras);
1105 QString nameText = node->name();
1112 nameText = node->parent()->name() +
"::"_L1 + nameText;
1121 appendText(Node::nodeTypeString(node->nodeType()) +
" "_L1);
1123 appendName(nameText);
1127 span.text = nameText;
1132 const auto *func =
static_cast<
const FunctionNode *>(node);
1135 if (
auto templateDecl = node->templateDecl()) {
1136 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl);
1137 spans.append(tmplSpans);
1143 if (!func->isNonvirtual())
1144 appendText(
"virtual "_L1);
1148 appendTypeSpans(func->returnTypeString(),
true);
1151 appendName(nameText);
1155 span.text = nameText;
1159 if (!func->isMacroWithoutParams()) {
1161 if (!func->parameters().isEmpty()) {
1162 const Parameters ¶meters = func->parameters();
1163 for (
int i = 0; i < parameters
.count(); ++i) {
1165 appendText(
", "_L1);
1167 QString pName = param.name();
1168 QString type = param.type();
1169 QString value = param.defaultValue();
1170 qsizetype insertPos = param.nameInsertionPoint();
1172 appendTypeSpans(type.left(insertPos),
false);
1175 paramSpan.text = pName;
1176 spans.append(paramSpan);
1177 appendTypeSpans(type.mid(insertPos),
false);
1180 appendTypeSpans(type, trailingSpace);
1184 paramSpan.text = pName;
1185 spans.append(paramSpan);
1189 appendText(
" = "_L1 + value);
1195 if (func->isConst())
1196 appendText(
" const"_L1);
1199 if (func->isFinal())
1200 appendText(
" final"_L1);
1201 if (func->isOverride())
1202 appendText(
" override"_L1);
1203 if (func->isPureVirtual())
1204 appendText(
" = 0"_L1);
1206 appendText(
" &"_L1);
1207 else if (func->isRefRef())
1208 appendText(
" &&"_L1);
1210 if (!func->returnType().isEmpty() && func->returnType() !=
"void"_L1) {
1211 appendText(
" : "_L1);
1212 appendTypeSpans(func->returnTypeString(),
false);
1216 appendText(
" &"_L1);
1217 else if (func->isRefRef())
1218 appendText(
" &&"_L1);
1219 if (
const auto &req = func->trailingRequiresClause(); req && !req->isEmpty())
1220 appendText(
" requires "_L1 + *req);
1225 const auto *enume =
static_cast<
const EnumNode *>(node);
1226 appendText(
"enum"_L1);
1227 if (enume->isScoped())
1228 appendText(
" class"_L1);
1229 if (!enume->isAnonymous()) {
1232 appendName(nameText);
1236 span.text = nameText;
1241 appendText(
" { "_L1);
1242 const int MaxEnumValues = 6;
1243 QStringList documentedItems = enume->doc().enumItemNames();
1244 if (documentedItems.isEmpty()) {
1245 const auto &enumItems = enume->items();
1246 for (
const auto &item : enumItems)
1247 documentedItems << item.name();
1249 const QStringList omitItems = enume->doc().omitEnumItemNames();
1250 for (
const auto &item : omitItems)
1251 documentedItems.removeAll(item);
1253 if (documentedItems.size() > MaxEnumValues) {
1254 const QString last = documentedItems.last();
1255 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
1256 documentedItems +=
"..."_L1;
1257 documentedItems += last;
1259 appendText(documentedItems.join(
", "_L1));
1260 if (!documentedItems.isEmpty())
1268 if (
auto templateDecl = node->templateDecl()) {
1269 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl);
1270 spans.append(tmplSpans);
1275 appendName(nameText);
1279 span.text = nameText;
1285 if (
static_cast<
const TypedefNode *>(node)->associatedEnum())
1286 appendText(
"flags "_L1);
1288 appendName(nameText);
1292 span.text = nameText;
1298 const auto *property =
static_cast<
const PropertyNode *>(node);
1300 appendName(nameText);
1304 span.text = nameText;
1307 appendText(
" : "_L1);
1308 appendTypeSpans(property->qualifiedDataType(),
false);
1312 const auto *property =
static_cast<
const QmlPropertyNode *>(node);
1314 appendName(nameText);
1318 span.text = nameText;
1321 appendText(
" : "_L1);
1322 appendTypeSpans(property->dataType(),
false);
1326 const auto *variable =
static_cast<
const VariableNode *>(node);
1329 appendName(nameText);
1333 span.text = nameText;
1336 appendText(
" : "_L1);
1337 appendTypeSpans(variable->dataType(),
false);
1339 appendTypeSpans(variable->leftType(),
true);
1341 appendName(nameText);
1345 span.text = nameText;
1348 appendText(variable->rightType());
1354 appendName(nameText);
1358 span.text = nameText;
1486 result.typeName = qcn->name();
1487 result.typeHref = resolveHref(hrefResolver, qcn, qcn);
1492 if (groupedMembers.isEmpty())
1495 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1498 IR::AllMemberEntry entry;
1499 entry.signatureSpans = buildQmlItemSpans(node, hrefResolver);
1500 entry.signature = plainTextFromSpans(entry.signatureSpans);
1501 entry.href = resolveHref(hrefResolver, node, qcn);
1503 if (node->isQmlProperty()) {
1504 auto *qpn =
static_cast<QmlPropertyNode *>(node);
1505 QStringList qmlHints = qpn->hints();
1506 if (qpn->isAttached() && !qmlHints.contains(
"attached"_L1))
1507 qmlHints <<
"attached"_L1;
1508 for (
const auto &h : std::as_const(qmlHints))
1509 entry.hints.append(h);
1510 }
else if (node->isAttached()) {
1511 entry.hints.append(
"attached"_L1);
1514 if (node->isPropertyGroup()) {
1515 entry.isPropertyGroup =
true;
1516 const auto *scn =
static_cast<SharedCommentNode *>(node);
1517 for (
auto *child : scn->collective()) {
1518 const NodeContext childContext = child->createContext();
1519 if (!InclusionFilter::isIncluded(policy, childContext))
1521 entry.children.append(buildEntry(child));
1528 auto isVisible = [&policy](
Node *node) {
1530 return InclusionFilter::isIncluded(policy, context)
1531 && !(node->isSharingComment() && node->sharedCommentNode()->isPropertyGroup());
1534 for (
const auto &[originType, nodes] : groupedMembers) {
1535 Q_ASSERT(originType);
1536 if (nodes.isEmpty())
1539 IR::MemberGroup group;
1540 if (originType != qcn) {
1541 group.typeName = originType->name();
1542 group.typeHref = resolveHref(hrefResolver, originType, qcn);
1545 for (
auto *node : nodes) {
1546 if (isVisible(node))
1547 group.members.append(buildEntry(node));
1550 result.memberGroups.append(group);
1656 const Config &config = Config::instance();
1660 const QString homepage = config.get(navDot +
CONFIG_HOMEPAGE).asString();
1661 const QString hometitle = config.get(navDot +
CONFIG_HOMETITLE).asString(homepage);
1663 const QString landingtitle = config.get(navDot +
CONFIG_LANDINGTITLE).asString(landingpage);
1667 const QString qmltypestitle = config.get(navDot +
CONFIG_QMLTYPESTITLE).asString(
"QML Types"_L1);
1669 const QString pageTitle = pn->title();
1672 auto resolveCrumb = [&](
const QString &targetName)
1673 -> std::pair<QString, CrumbState> {
1674 const Node *target = qdb->findNodeForTarget(targetName, pn);
1676 return {{}, CrumbState::Unresolved};
1678 return {{}, CrumbState::Current};
1679 return {resolveHref(hrefResolver, target, pn), CrumbState::Link};
1682 if (!homepage.isEmpty()) {
1683 auto [href, state] = resolveCrumb(homepage);
1684 if (state == CrumbState::Current)
1686 nav.breadcrumbs.append({hometitle,
std::move(href), state});
1689 if (!landingpage.isEmpty()) {
1690 auto [href, state] = resolveCrumb(landingpage);
1691 if (state != CrumbState::Current)
1692 nav.breadcrumbs.append({landingtitle,
std::move(href), state});
1696 if (!cppclassespage.isEmpty() && !cppclassestitle.isEmpty()) {
1697 auto [href, state] = resolveCrumb(cppclassespage);
1698 nav.breadcrumbs.append({cppclassestitle,
std::move(href), state});
1702 QString moduleState;
1703 if (moduleNode && !moduleNode->state().isEmpty())
1704 moduleState = QStringLiteral(
" (%1)").arg(moduleNode->state());
1706 if (!pn->physicalModuleName().isEmpty() && moduleNode
1707 && (!moduleState.isEmpty() || moduleNode->title() != cppclassespage)) {
1708 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1709 resolveHref(hrefResolver, moduleNode, pn),
1712 nav.breadcrumbs.append({pn->name(), {}, CrumbState
::Current});
1714 if (!qmltypespage.isEmpty() && !qmltypestitle.isEmpty()) {
1715 auto [href, state] = resolveCrumb(qmltypespage);
1716 nav.breadcrumbs.append({qmltypestitle,
std::move(href), state});
1720 QString moduleState;
1721 if (moduleNode && !moduleNode->state().isEmpty())
1722 moduleState = QStringLiteral(
" (%1)").arg(moduleNode->state());
1725 && (!moduleState.isEmpty() || moduleNode->title() != qmltypespage)) {
1726 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1727 resolveHref(hrefResolver, moduleNode, pn),
1730 nav.breadcrumbs.append({pn->name(), {}, CrumbState
::Current});
1732 auto currentNode = pn;
1733 std::deque<
const Node *> navNodes;
1734 qsizetype navItems = 0;
1736 if (
std::find(navNodes.cbegin(), navNodes.cend(),
1741 if (navNodes.empty()) {
1742 const QStringList groups = pn->groupNames();
1743 for (
const auto &groupName : groups) {
1744 const auto *groupNode = qdb->findNodeByNameAndType(
1745 QStringList{groupName}, &Node::isGroup);
1746 if (groupNode && !groupNode->title().isEmpty()) {
1747 navNodes.push_front(groupNode);
1752 for (
const auto *navNode : navNodes) {
1753 if (navNode->isPageNode())
1754 nav.breadcrumbs.append({navNode->title(),
1755 resolveHref(hrefResolver, navNode, pn),
1758 if (!nav.breadcrumbs.isEmpty())
1759 nav.breadcrumbs.append({pageTitle, {}, CrumbState
::Current});
1762 const auto &linkMap = pn->links();
1763 if (linkMap.contains(Node::PreviousLink)) {
1764 const auto &linkPair = linkMap[Node::PreviousLink];
1765 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1768 if (target && target != pn) {
1769 href = resolveHref(hrefResolver, target, pn);
1770 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1771 ? target->title() : linkPair.second;
1773 href = linkPair.first;
1774 title = linkPair.second;
1778 if (linkMap.contains(Node::NextLink)) {
1779 const auto &linkPair = linkMap[Node::NextLink];
1780 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1783 if (target && target != pn) {
1784 href = resolveHref(hrefResolver, target, pn);
1785 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1786 ? target->title() : linkPair.second;
1788 href = linkPair.first;
1789 title = linkPair.second;
1793 if (linkMap.contains(Node::StartLink)) {
1794 const auto &linkPair = linkMap[Node::StartLink];
1795 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1798 if (target && target != pn) {
1799 href = resolveHref(hrefResolver, target, pn);
1800 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1801 ? target->title() : linkPair.second;
1803 href = linkPair.first;
1804 title = linkPair.second;
1809 const QString formatDot =
"HTML"_L1 + Config::dot;
1810 nav
.tocDepth = config.get(formatDot +
"tocdepth"_L1).asInt();