37#include <QtCore/qdebug.h>
38#include <QtCore/qdir.h>
39#include <QtCore/qregularexpression.h>
41#ifndef QT_BOOTSTRAPPED
42# include "QtCore/qurl.h"
48using namespace std::literals::string_literals;
52using namespace Qt::StringLiterals;
55QMap<QString, QMap<QString, QString>>
Generator::s_fmtLeftMaps;
56QMap<QString, QMap<QString, QString>>
Generator::s_fmtRightMaps;
57QList<Generator *>
Generator::s_generators;
64QHash<QString, QString>
Generator::s_outputPrefixes;
65QHash<QString, QString>
Generator::s_outputSuffixes;
73static QRegularExpression
tag(
"</?@[^>]*>");
80
81
82
83
84
90 names.unite(p->templateDecl()->parameterNames());
96
97
98
99
100
101
102
103
107
108
109
111 const QSet<QString> &documentedNames,
112 const QSet<QString> &allowedNames,
115 for (
const auto &name : documentedNames) {
116 if (!allowedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
117 const auto message = (context == ValidationContext::TemplateDoc)
118 ?
"No such template parameter '%1' in %2"_L1
119 :
"No such parameter '%1' in %2"_L1;
120 node->doc().location().warning(message.arg(name, node->plainFullName()),
121 suggestName(name, allowedNames));
127
128
129
130
131
136 s_generators.prepend(
this);
140
141
142
145 s_generators.removeAll(
this);
149 const Node *actualNode)
151 if (actualNode ==
nullptr)
152 actualNode = apparentNode;
154 addNodeLink(text, actualNode, apparentNode->plainFullName(relative));
158 const Node *actualNode)
160 if (actualNode ==
nullptr)
161 actualNode = apparentNode;
163 addNodeLink(text, actualNode, fullName);
167
168
169
170
177
178
179
180
186 for (
const auto &node : nodes) {
187 text << Atom(Atom::ListItemNumber, QString::number(++count));
188 text << Atom(Atom::ListItemLeft, QString(
"bullet"));
189 appendSignature(text, node);
190 text << Atom(Atom::ListItemRight, QString(
"bullet"));
198 QMap<QString, Text> classMap;
199 for (
const auto &relatedClass : rc) {
200 ClassNode *rcn = relatedClass.m_node;
201 if (rcn && rcn->isInAPI()) {
203 appendFullName(className, rcn, cn);
204 classMap[className.toString().toLower()] = className;
209 const QStringList classNames = classMap.keys();
210 for (
const auto &className : classNames) {
211 text << classMap[className];
212 text << TextUtils::comma(index++, classNames.size());
220 QMap<QString, Text> classMap;
222 QStringList typeNames(knownTypes);
223 for (
const auto sub : subs)
224 typeNames << sub->name();
226 for (
const auto sub : subs) {
228 appendFullName(full_name, sub, base);
230 if (typeNames.count(sub->name()) > 1)
231 full_name << Atom(Atom::String,
" (%1)"_L1.arg(sub->logicalModuleName()));
232 classMap[full_name.toString().toLower()] = full_name;
236 const auto &names = classMap.keys();
237 for (
const auto &name : names)
238 text << classMap[name] << TextUtils::comma(index++, names.size());
243
244
245
246
247
248
249
257 if (s_outFileNames.contains(fileName) && !node
->isAttribution() && !fileName.contains(
"-attribution-"_L1))
258 node
->location().warning(
"Already generated %1 for this project"_L1.arg(fileName));
260 QString path = outputDir() + QLatin1Char(
'/') + fileName;
263 auto outFile =
new QFile(outPath);
266 const QString warningText {
"Output file already exists, overwriting %1"_L1.arg(outFile->fileName())};
267 if (qEnvironmentVariableIsSet(
"QDOC_ALL_OVERWRITES_ARE_WARNINGS"))
270 qCDebug(lcQdoc) << qUtf8Printable(warningText);
273 if (!outFile->open(QFile::WriteOnly | QFile::Text)) {
275 QStringLiteral(
"Cannot open output file '%1'").arg(outFile->fileName()));
278 qCDebug(lcQdoc,
"Writing: %s", qPrintable(path));
279 s_outFileNames << fileName;
280 s_trademarks.clear();
285
286
287
288
291 QFile *outFile = openSubPageFile(
static_cast<
const PageNode*>(node), fileName);
292 auto *out =
new QTextStream(outFile);
293 outStreamStack.push(out);
297
298
299
300
303 outStreamStack.top()->flush();
304 delete outStreamStack.top()->device();
305 delete outStreamStack.pop();
314 return node->fileNameBase();
316 QString result = Utilities::computeFileBase(
318 [](
const Node *n) {
return outputPrefix(n); },
319 [](
const Node *n) {
return outputSuffix(n); });
321 const_cast<
Node *>(node)->setFileNameBase(result);
326
327
328
329
330
333 return Utilities::linkForExampleFile(path, s_project, fileExt.isEmpty() ? fileExtension() : fileExt);
337
338
339
342 return Utilities::exampleFileTitle(relative->files(), relative->images(), fileName);
346
347
348
349
350
353 if (!node->url().isEmpty())
360 QFileInfo originalName(node->name());
361 QString suffix = originalName.suffix();
362 if (!suffix.isEmpty() && suffix !=
"html") {
364 QString name = fileBase(node);
365 return name + QLatin1Char(
'.') + suffix;
369 QString name = fileBase(node) + QLatin1Char(
'.');
370 return name + (extension.isNull() ? fileExtension() : extension);
374
375
376
377
378
379
380
381
382QString
Generator::cleanRef(
const QString &ref,
bool xmlCompliant)
393 clean.reserve(ref.size() + 20);
394 const QChar c = ref[0];
395 const uint u = c.unicode();
397 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (!xmlCompliant && u >=
'0' && u <=
'9')) {
399 }
else if (xmlCompliant && u >=
'0' && u <=
'9') {
400 clean += QLatin1Char(
'A') + c;
401 }
else if (u ==
'~') {
403 }
else if (u ==
'_') {
404 clean +=
"underscore.";
406 clean += QLatin1Char(
'A');
409 for (
int i = 1; i < ref.size(); i++) {
410 const QChar c = ref[i];
411 const uint u = c.unicode();
412 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (u >=
'0' && u <=
'9') || u ==
'-'
413 || u ==
'_' || (xmlCompliant && u ==
':') || u ==
'.') {
415 }
else if (c.isSpace()) {
416 clean += QLatin1Char(
'-');
417 }
else if (u ==
'!') {
419 }
else if (u ==
'&') {
421 }
else if (u ==
'<') {
423 }
else if (u ==
'=') {
425 }
else if (u ==
'>') {
427 }
else if (u ==
'#') {
428 clean += QLatin1Char(
'#');
430 clean += QLatin1Char(
'-');
431 clean += QString::number(
static_cast<
int>(u), 16);
439 return s_fmtLeftMaps[format()];
444 return s_fmtRightMaps[format()];
448
449
454 if (!node->url().isEmpty())
462
463
464
465 if (!fileBase(node).isEmpty())
466 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
470 return fileBase(node) + QLatin1Char(
'.') + fileExtension();
472 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
473 }
else if (fileBase(node).isEmpty())
476 Node *parentNode =
nullptr;
480 if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
481 parentName = fullDocumentLocation(node->parent());
485 case NodeType::Class:
486 case NodeType::Struct:
487 case NodeType::Union:
488 case NodeType::Namespace:
489 case NodeType::Proxy:
490 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
493 const auto *fn =
static_cast<
const FunctionNode *>(node);
494 switch (fn->metaness()) {
496 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal";
499 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal-handler";
502 anchorRef = QLatin1Char(
'#') + node->name() +
"-method";
506 anchorRef =
"#dtor." + fn->name().mid(1);
507 else if (
const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty())
508 return fullDocumentLocation(p);
509 else if (fn->overloadNumber() > 0)
510 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name()) + QLatin1Char(
'-')
511 + QString::number(fn->overloadNumber());
513 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name());
519
520
521
522
525 anchorRef = QLatin1Char(
'#') + node->name() +
"-enum";
528 const auto *tdef =
static_cast<
const TypedefNode *>(node);
529 if (tdef->associatedEnum())
530 return fullDocumentLocation(tdef->associatedEnum());
533 anchorRef = QLatin1Char(
'#') + node->name() +
"-typedef";
536 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
544 anchorRef = QLatin1Char(
'#') + node->name() +
"-attached-prop";
546 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
549 anchorRef = QLatin1Char(
'#') + node->name() +
"-var";
557 parentName = fileBase(node);
558 parentName.replace(QLatin1Char(
'/'), QLatin1Char(
'-'))
559 .replace(QLatin1Char(
'.'), QLatin1Char(
'-'));
560 parentName += QLatin1Char(
'.') + fileExtension();
568 parentName.replace(QLatin1Char(
'.') + fileExtension(),
569 "-obsolete." + fileExtension());
572 return parentName.toLower() + anchorRef;
576
577
578
579
580
581
582
585 QList<Text> alsoList = node
->doc().alsoList();
586 supplementAlsoList(node, alsoList);
588 if (!alsoList.isEmpty()) {
595 for (
const auto &also : std::as_const(alsoList)) {
597 const Atom *atom = also.firstAtom();
598 QString link = atom->string();
599 if (!used.contains(link)) {
604 if (m_qdb->findNodeForAtom(atom, node, ref) == node && ref.isEmpty())
605 node->doc().location().warning(
"Redundant link to self in \\sa command for %1"_L1.arg(node->name()));
610 for (
const auto &also : std::as_const(items))
611 text << also << TextUtils::separator(i++, items.size());
619 bool generate,
int &numAtoms)
621 while (atom !=
nullptr) {
623 int numAtoms0 = numAtoms;
624 bool rightFormat = canHandleFormat(atom->string());
639 if (generate && numAtoms0 == numAtoms) {
640 relative
->location().warning(QStringLiteral(
"Output format %1 not handled %2")
641 .arg(format(), outFileName()));
642 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
652 n += generateAtom(atom, relative, marker);
664
665
666
669 const FunctionNode *fn = node->isFunction() ?
static_cast<
const FunctionNode *>(node) :
nullptr;
672
673
674
678 text <<
"Destroys the instance of ";
679 text << fn
->parent()->name() <<
".";
681 text <<
" The destructor is virtual.";
687 text <<
"Default-constructs an instance of "
694 text <<
"Copy-constructs an instance of "
701 text <<
"Move-constructs an instance of "
708 text <<
"Copy-assigns "
711 <<
" to this " << fn
->parent()->name() <<
" instance.";
717 text <<
"Move-assigns "
720 <<
" to this " << fn
->parent()->name() <<
" instance.";
725 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
729 QStringLiteral(
"No documentation for %1 '%2'")
734 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
738 QStringLiteral(
"No documentation for '%1'").arg(node->plainSignature()));
743 generateReimplementsClause(fn, marker);
745 if (
static_cast<
const PropertyNode *>(node)->propertyType() != PropertyNode::PropertyType::StandardProperty)
770 const auto *enume =
static_cast<
const EnumNode *>(node);
772 QSet<QString> definedItems;
773 const QList<EnumItem> &items = enume->items();
774 for (
const auto &item : items)
775 definedItems.insert(item.name());
777 const auto &documentedItemList = enume->doc().enumItemNames();
778 QSet<QString> documentedItems(documentedItemList.cbegin(), documentedItemList.cend());
779 const QSet<QString> allItems = definedItems + documentedItems;
780 if (allItems.size() > definedItems.size()
781 || allItems.size() > documentedItems.size()) {
782 for (
const auto &it : allItems) {
783 if (!definedItems.contains(it)) {
784 node->doc().location().warning(
785 QStringLiteral(
"No such enum item '%1' in %2")
786 .arg(it, node->plainFullName()),
787 QStringLiteral(
"Maybe you meant '%1'?")
788 .arg(suggestName(it, definedItems, documentedItems)));
789 }
else if (!documentedItems.contains(it)) {
790 node->doc().location().warning(
791 QStringLiteral(
"Undocumented enum item '%1' in %2")
792 .arg(it, node->plainFullName()));
805 const QSet<QString> requiredFunctionParams = fn
->parameters().getNames();
806 const QSet<QString> requiredTemplateParams = fn->templateDecl()
807 ? fn->templateDecl()->requiredParameterNamesForFunctions()
809 const QSet<QString> requiredNames = requiredFunctionParams + requiredTemplateParams;
813 const QSet<QString> ownTemplateParams = fn->templateDecl()
814 ? fn->templateDecl()->parameterNames()
816 const QSet<QString> allowedNames = requiredNames + ownTemplateParams
817 + inheritedTemplateParamNames(fn);
819 const QSet<QString> documentedNames = fn
->doc().parameterNames();
822 for (
const auto &name : requiredNames) {
823 if (!documentedNames.contains(name)) {
824 if (fn->isActive() || fn->isPreliminary()) {
827 if (!fn->isMarkedReimp() && !fn->isOverload()
828 && !(fn->isSomeCtor() && fn->hasOverloads())) {
830 const bool isTemplateParam = requiredTemplateParams.contains(name);
831 fn->doc().location().warning(
832 "Undocumented %1 '%2' in %3"_L1
833 .arg(isTemplateParam ?
"template parameter"_L1
835 name, node->plainFullName()));
841 warnAboutUnknownDocumentedParams(fn, documentedNames, allowedNames,
844
845
846
847
850 if (!fn
->doc().body().contains(
"return"))
852 QStringLiteral(
"Undocumented return value "
853 "(hint: use 'return' or 'returns' in the text"));
856 if (
auto *qpn =
static_cast<
const QmlPropertyNode *>(node); !qpn->validateDataType())
857 qpn->doc().location().warning(
"Invalid QML property type: %1"_L1.arg(qpn->dataType()));
863 const QSet<QString> requiredNames = node->templateDecl()->parameterNames();
864 const QSet<QString> allowedNames = requiredNames + inheritedTemplateParamNames(node);
865 const QSet<QString> documentedNames = node
->doc().parameterNames();
868 for (
const auto &name : requiredNames) {
869 if (!documentedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
870 node->doc().location().warning(
871 "Undocumented template parameter '%1' in %2"_L1
872 .arg(name, node->plainFullName()));
877 warnAboutUnknownDocumentedParams(node, documentedNames, allowedNames,
886
887
888
889
890
896 const auto *en =
static_cast<
const ExampleNode *>(node);
899 if (exampleUrl.isEmpty()) {
900 if (!en->noAutoList()) {
905 generateLinkToExample(en, marker, exampleUrl);
910
911
912
913
914
915
917 const QString &baseUrl)
919 QString exampleUrl(baseUrl);
921#ifndef QT_BOOTSTRAPPED
922 link = QUrl(exampleUrl).host();
926 link.prepend(
"Example project");
928 const QLatin1Char separator(
'/');
929 const QLatin1Char placeholder(
'\1');
930 if (!exampleUrl.contains(placeholder)) {
931 if (!exampleUrl.endsWith(separator))
932 exampleUrl += separator;
933 exampleUrl += placeholder;
940 pathRoot = metaTagMap->value(QLatin1String(
"installpath"));
941 if (pathRoot.isEmpty())
943 QStringList path = QStringList() << pathRoot << en->name();
944 path.removeAll(QString());
948 <<
Atom(
Atom::Link, exampleUrl.replace(placeholder, path.join(separator)))
958 const QString prefix(
"/images/used-in-examples");
964 s_outFileNames << prefix.mid(1) +
"/" + resolved_file.get_query();
967 OutputDirectory::ensure(s_outDir, en
->location());
969 outDir.ensureSubdir(prefix.mid(1), en
->location());
971 const QFileInfo fi{resolved_file.get_query()};
972 const QString relativePath = fi.path();
974 const bool hasSubdir = !relativePath.isEmpty() && relativePath !=
"."_L1;
976 hasSubdir ? imagesUsedInExamplesDir.ensureSubdir(relativePath, en
->location())
977 : imagesUsedInExamplesDir;
979 const QString fileName = fi.fileName();
995
996
997
998
999
1000
1010 paths = en->images();
1014 paths = en->files();
1017 std::sort(paths.begin(), paths.end(), Generator::comparePaths);
1022 for (
const auto &path : std::as_const(paths)) {
1023 auto maybe_resolved_file{file_resolver.resolve(path)};
1024 if (!maybe_resolved_file) {
1026 QString details = std::transform_reduce(
1027 file_resolver.get_search_directories().cbegin(),
1028 file_resolver.get_search_directories().cend(),
1029 u"Searched directories:"_s,
1031 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
1034 en->location().warning(u"(Generator)Cannot find file to quote from: %1"_s.arg(path), details);
1039 const auto &file{*maybe_resolved_file};
1041 addImageToCopy(en, file);
1043 generateExampleFilePage(en, file, marker);
1046 text << Atom(Atom::ListItemNumber, openedList.numberString())
1047 << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
1048 << Atom(atomType, file.get_query()) << Atom(Atom::FormattingLeft,
ATOM_FORMATTING_LINK) << file.get_query()
1050 << Atom(Atom::ListItemRight, openedList.styleString());
1053 if (!paths.isEmpty())
1054 generateText(text, en, marker);
1058
1059
1062 if (!node->url().isNull())
1066 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1074
1075
1079 PageNode *pageNode =
static_cast<PageNode *>(node);
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100 auto *cn =
static_cast<CollectionNode *>(node);
1101 if (cn->wasSeen()) {
1102 m_qdb->mergeCollections(cn);
1103 beginSubPage(pageNode, fileName(node));
1106 }
else if (cn->isGenericCollection()) {
1110 QString name = cn->name().toLower();
1111 name.replace(QChar(
' '), QString(
"-"));
1113 cn->tree()->physicalModuleName() +
"-" + name +
"." + fileExtension();
1114 beginSubPage(pageNode, filename);
1119 beginSubPage(pageNode, fileName(node));
1125 beginSubPage(pageNode, fileName(node));
1129 beginSubPage(pageNode, fileName(node));
1130 auto *qcn =
static_cast<QmlTypeNode *>(node);
1134 beginSubPage(pageNode, fileName(node));
1142 auto *aggregate =
static_cast<Aggregate *>(node);
1143 const NodeList &children = aggregate->childNodes();
1144 for (
auto *child : children) {
1145 if (child->isPageNode()) {
1146 generateDocumentation(child);
1147 }
else if (!node->parent() && child->isInAPI() && !child->isRelatedNonmember()
1148 && !child->doc().isAutoGenerated()) {
1150 child->location().warning(u"No documentation generated for %1 '%2' in global scope."_s
1151 .arg(typeString(child), child->name()),
1152 u"Maybe you forgot to use the '\\relates' command?"_s);
1153 child->setStatus(Status::DontDocument);
1154 }
else if (child->isQmlModule() && !child->wasSeen()) {
1156 auto *qmlModule =
static_cast<CollectionNode *>(child);
1157 for (
const auto *member : qmlModule->members()) {
1158 member->location().warning(
1159 u"Undocumented QML module '%1' referred by type '%2' or its members"_s
1160 .arg(qmlModule->name(), member->name()),
1161 u"Maybe you forgot to document '\\qmlmodule %1'?"_s
1162 .arg(qmlModule->name()));
1164 }
else if (child->isQmlType() && !child->hasDoc()) {
1166 auto *qmlType =
static_cast<QmlTypeNode *>(child);
1167 if (
auto qmid = qmlType->logicalModuleName(); !qmid.isEmpty())
1168 qmlType->location().warning(u"No such type '%1' in QML module '%2'"_s
1169 .arg(qmlType->name(), qmid));
1181 const FunctionNode *overrides = cn->findOverriddenFunction(fn);
1187 overrides->parent()->name()
1188 +
"::" + overrides->signature(Node::SignaturePlain);
1191 generateText(text, fn, marker);
1193 fn
->doc().location().warning(
1194 QStringLiteral(
"Illegal \\reimp; no documented virtual function for %1")
1195 .arg(overrides->plainSignature()));
1199 const PropertyNode *sameName = cn->findOverriddenProperty(fn);
1200 if (sameName && sameName
->hasDoc()) {
1202 text <<
Atom::ParaLeft <<
"Reimplements an access function for property: ";
1203 QString fullName = sameName->parent()->name() +
"::" + sameName->name();
1206 generateText(text, fn, marker);
1212 QStringList since = node->since().split(QLatin1Char(
' '));
1215 if (since.size() == 1) {
1217 return productName.isEmpty() ? node->since() : productName +
" " + since[0];
1221 return node->since();
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1247 status = metaMap->value(
"status");
1248 if (!status.isEmpty())
1251 const auto &since = node->deprecatedSince();
1253 status = u"Deprecated"_s;
1254 if (!since.isEmpty())
1255 status +=
" since %1"_L1.arg(since);
1256 }
else if (!since.isEmpty()) {
1257 status =
"Until %1"_L1.arg(since);
1261 status = collection->state();
1264 return status.isEmpty() ?
std::nullopt :
std::optional(status);
1269 if (!node->since().isEmpty()) {
1272 const auto &collective =
static_cast<
const SharedCommentNode *>(node)->collective();
1273 QString typeStr = typeString(collective.first(), collective.size() > 1);
1274 text << Atom::ParaLeft <<
"These " << typeStr <<
" were introduced in "
1275 << formatSince(node) <<
"." << Atom::ParaRight;
1277 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" was introduced in "
1278 << formatSince(node) <<
"." << Atom::ParaRight;
1285 std::vector<
const Node*> nodes;
1287 auto shared_node =
static_cast<
const SharedCommentNode*>(node);
1288 nodes.reserve(shared_node->collective().size());
1289 nodes.insert(nodes.begin(), shared_node->collective().begin(), shared_node->collective().end());
1290 }
else nodes.push_back(node);
1292 std::size_t counter{1};
1293 for (
const Node* node : nodes) {
1294 if (node->isFunction(Genus::CPP)) {
1295 if (
const auto &exception_info =
static_cast<
const FunctionNode*>(node)->getNoexcept(); exception_info && !(*exception_info).isEmpty()) {
1297 text << Atom::NoteLeft
1298 << (nodes.size() > 1 ? QString::fromStdString(
" ("s + std::to_string(counter) +
")"s) : QString::fromStdString(
"This ") + typeString(node))
1299 <<
" is noexcept when "
1300 << Atom(Atom::C, marker->markedUpCode(*exception_info,
nullptr, Location()))
1301 <<
" is " << Atom(Atom::C,
"true") <<
"."
1303 generateText(text, node, marker);
1319 const QString &state =
static_cast<
const CollectionNode*>(node)->state();
1320 if (!state.isEmpty()) {
1321 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" is in "
1328 if (
const auto &version = node->deprecatedSince(); !version.isEmpty()) {
1329 text << Atom::ParaLeft <<
"This " << typeString(node)
1330 <<
" is scheduled for deprecation in version "
1331 << version <<
"." << Atom::ParaRight;
1335 auto description = Config::instance()
1338 description.replace(
'\1'_L1, typeString(node));
1347 text <<
"This " << typeString(node) <<
" is deprecated";
1348 if (
const QString &version = node->deprecatedSince(); !version.isEmpty()) {
1350 if (node
->isQmlNode() && !node->logicalModuleName().isEmpty())
1351 text << node->logicalModuleName() <<
" ";
1355 text <<
". We strongly advise against using it in new code.";
1364 <<
"Part of developer documentation for internal use."
1374
1375
1376
1380 Q_ASSERT(node && !node->name().isEmpty());
1382 text << Atom(Atom::DivLeft,
1383 "class=\"admonition %1\""_L1.arg(prefix == AdmonitionPrefix::Note ? u"note"_s : u"auto"_s));
1398 text <<
"This function can be invoked via the meta-object system and from QML. See "
1404 text <<
"This is a private signal. It can be used in signal connections "
1405 "but cannot be emitted by the user.";
1409 QString handler(node->name());
1410 qsizetype prefixLocation = handler.lastIndexOf(
'.', -2) + 1;
1411 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
1412 handler.insert(prefixLocation, QLatin1String(
"on"));
1413 text <<
"The corresponding handler is "
1422 const auto *fn =
static_cast<
const FunctionNode *>(node);
1423 auto nodes = fn->associatedProperties();
1424 if (nodes.isEmpty())
1429 QMap<PropertyNode::FunctionRole, QList<
const PropertyNode *>> roleGroups;
1430 for (
const auto *n : std::as_const(nodes)) {
1431 const auto *pn =
static_cast<
const PropertyNode *>(n);
1432 if (pn->isInAPI()) {
1433 PropertyNode::FunctionRole role = pn->role(fn);
1434 roleGroups[role].append(pn);
1438 if (roleGroups.isEmpty())
1449 for (
auto role : roleOrder) {
1450 const auto it = roleGroups.constFind(role);
1451 if (it == roleGroups.cend())
1454 const auto &properties = it.value();
1458 case PropertyNode::FunctionRole::Getter:
1459 msg = u"Getter function"_s;
1461 case PropertyNode::FunctionRole::Setter:
1462 msg = u"Setter function"_s;
1464 case PropertyNode::FunctionRole::Resetter:
1465 msg = u"Resetter function"_s;
1467 case PropertyNode::FunctionRole::Notifier:
1468 msg = u"Notifier signal"_s;
1470 case PropertyNode::FunctionRole::Bindable:
1471 msg = u"Bindable function"_s;
1477 if (properties.size() == 1) {
1478 const auto *pn = properties.first();
1479 text << msg << u" for property "_s << Atom(Atom::Link, pn->name())
1483 text << msg << u" for properties "_s;
1484 for (qsizetype i = 0; i < properties.size(); ++i) {
1485 const auto *pn = properties.at(i);
1489 << TextUtils::separator(i, properties.size());
1498 text <<
"This property supports "
1502 text <<
" bindings.";
1507 const auto *func =
static_cast<
const FunctionNode *>(node);
1510 if (func->isPrimaryOverload())
1513 if (func->isSignal() || func->isSlot()) {
1514 QString functionType = func->isSignal() ?
"signal" :
"slot";
1515 const QString &configKey = func->isSignal() ?
"overloadedsignalstarget" :
"overloadedslotstarget";
1516 const QString &defaultTarget = func->isSignal() ?
"connecting-overloaded-signals" :
"connecting-overloaded-slots";
1517 const QString &linkTarget = Config::instance().get(configKey).asString(defaultTarget);
1519 text <<
"This " << functionType <<
" is overloaded. ";
1521 QString snippet = generateOverloadSnippet(func);
1522 if (!snippet.isEmpty()) {
1523 text <<
"To connect to this " << functionType <<
":\n\n"
1527 if (!linkTarget.isEmpty()) {
1528 text <<
"For more examples and approaches, see "
1531 <<
"connecting to overloaded " << functionType <<
"s"
1535 const auto &args = node
->doc().overloadList();
1536 if (args.first().first.isEmpty()) {
1537 text <<
"This is an overloaded function.";
1539 QString target = args.first().first;
1542 if (!target.contains(
"::")) {
1545 target = parent->name() +
"::" + target;
1562
1563
1564
1565
1568 bool result =
false;
1579
1580
1581
1582
1583
1584
1585
1586
1590 bool result =
false;
1592 const NodeList &children =
static_cast<
const Aggregate *>(node)->childNodes();
1593 for (
auto child : children) {
1594 if (!child->isDeprecated()) {
1595 switch (child->threadSafeness()) {
1596 case Node::Reentrant:
1597 reentrant.append(child);
1598 if (ts == Node::ThreadSafe)
1601 case Node::ThreadSafe:
1602 threadsafe.append(child);
1603 if (ts == Node::Reentrant)
1606 case Node::NonReentrant:
1607 nonreentrant.append(child);
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1639 if (atom->count() > 1) {
1640 if (s_trademarks.contains(atom->string(1)))
1642 s_trademarks << atom->string(1);
1655
1656
1657
1660 Text text, rlink, tlink;
1665 bool exceptions =
false;
1676 case Node::NonReentrant:
1679 << typeString(node) <<
" is not " << rlink <<
"." << Atom::ParaRight;
1685 exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
1686 text <<
"All functions in this " << typeString(node) <<
" are ";
1695 text <<
" with the following exceptions:";
1697 text <<
"This " << typeString(node) <<
" is ";
1714 if (!nonreentrant.isEmpty()) {
1716 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1717 signatureList(nonreentrant, node, marker);
1719 if (!threadsafe.isEmpty()) {
1722 text <<
"These functions are also " << tlink <<
":" <<
Atom::ParaRight;
1724 signatureList(threadsafe, node, marker);
1727 if (!reentrant.isEmpty()) {
1729 text <<
"These functions are only " << rlink <<
":" <<
Atom::ParaRight;
1730 signatureList(reentrant, node, marker);
1732 if (!nonreentrant.isEmpty()) {
1735 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1736 signatureList(nonreentrant, node, marker);
1743
1744
1745
1746
1747
1751 if (category == ComparisonCategory::None)
1755 text << Atom::ParaLeft <<
"%1 is "_L1.arg(node->plainFullName())
1757 << QString::fromStdString(comparisonCategoryAsString(category))
1758 << ((category == ComparisonCategory::Equality) ?
"-"_L1 :
"ly "_L1)
1759 << Atom(Atom::String,
"comparable"_L1)
1761 <<
"."_L1 << Atom::ParaRight;
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1785 const auto *map = node
->doc().comparesWithMap();
1787 const bool hasSelfComparison = (selfCategory != ComparisonCategory::None);
1788 const bool hasComparesWithEntries = (map && !map->isEmpty());
1790 if (!hasSelfComparison && !hasComparesWithEntries)
1793 bool hasDescriptions =
false;
1794 if (hasComparesWithEntries) {
1795 for (
const auto &description : *map) {
1796 if (description.firstAtom()->next() != description.lastAtom()) {
1797 hasDescriptions =
true;
1805 text << Atom::ParaLeft
1807 <<
"%1 Comparisons"_L1.arg(node->plainFullName())
1811 text << Atom(Atom::TableLeft,
"generic"_L1);
1813 text << Atom::TableHeaderLeft
1814 << Atom::TableItemLeft <<
"Category"_L1 << Atom::TableItemRight
1815 << Atom::TableItemLeft <<
"Comparable Types"_L1 << Atom::TableItemRight;
1816 if (hasDescriptions)
1817 text << Atom::TableItemLeft <<
"Description"_L1 << Atom::TableItemRight;
1821 if (hasSelfComparison) {
1822 const QString &category = QString::fromStdString(comparisonCategoryAsString(selfCategory));
1828 if (hasDescriptions)
1834 if (hasComparesWithEntries) {
1835 for (
auto [key, description] : map->asKeyValueRange()) {
1836 const QString &category = QString::fromStdString(comparisonCategoryAsString(key));
1838 text << Atom::TableRowLeft;
1840 text << Atom::TableItemLeft << category << Atom::TableItemRight;
1842 text << Atom::TableItemLeft;
1843 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
1844 for (
const auto &name : types)
1845 text << Atom(Atom::AutoLink, name)
1846 << TextUtils::separator(types.indexOf(name), types.size());
1847 text << Atom::TableItemRight;
1849 if (hasDescriptions) {
1850 text << Atom::TableItemLeft;
1851 if (description.firstAtom()->next() != description.lastAtom())
1852 text << Text::subText(description.firstAtom()->next(), description.lastAtom());
1853 text << Atom::TableItemRight;
1856 text << Atom::TableRowRight;
1867
1868
1871 s_currentGenerator =
this;
1883 if (
auto *gen =
dynamic_cast<
Generator *>(producer))
1889 for (
const auto &generator : std::as_const(s_generators)) {
1890 if (generator->format() == format)
1905 while (i < markedCode.size()) {
1906 if (markedCode.at(i) == QLatin1Char(
'\n')) {
1910 for (
int j = 0; j < level; j++)
1911 t += QLatin1Char(
' ');
1915 t += markedCode.at(i++);
1922 Config &config = Config::instance();
1923 s_outputFormats = config.getOutputFormats();
1926 for (
auto &g : s_generators) {
1927 if (s_outputFormats.contains(g->format())) {
1928 s_currentGenerator = g;
1929 OutputProducerRegistry::instance().registerProducer(g);
1930 g->initializeGenerator();
1935 for (
const auto &n : configFormatting) {
1937 const auto &formattingDotNames = config.subVars(formattingDotName);
1938 for (
const auto &f : formattingDotNames) {
1939 const auto &configVar = config.get(formattingDotName + Config::dot + f);
1940 QString def{configVar.asString()};
1941 if (!def.isEmpty()) {
1942 int numParams = Config::numParams(def);
1943 int numOccs = def.count(
"\1");
1944 if (numParams != 1) {
1945 configVar.location().warning(QStringLiteral(
"Formatting '%1' must "
1947 "parameter (found %2)")
1948 .arg(n, numParams));
1949 }
else if (numOccs > 1) {
1950 configVar.location().fatal(QStringLiteral(
"Formatting '%1' must "
1951 "contain exactly one "
1952 "occurrence of '\\1' "
1956 int paramPos = def.indexOf(
"\1");
1957 s_fmtLeftMaps[f].insert(n, def.left(paramPos));
1958 s_fmtRightMaps[f].insert(n, def.mid(paramPos + 1));
1965 s_outDir = config.getOutputDir();
1966 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
1968 s_outputPrefixes.clear();
1970 if (!items.isEmpty()) {
1971 for (
const auto &prefix : items)
1972 s_outputPrefixes[prefix] =
1975 if (!items.contains(u"QML"_s))
1976 s_outputPrefixes[u"QML"_s] = u"qml-"_s;
1978 s_outputSuffixes.clear();
1981 + Config::dot + suffix).asString();
1988
1989
1990
1991void Generator::copyTemplateFiles(
const QString &configVar,
const QString &subDir)
2008 Config &config = Config::instance();
2009 QStringList files = config.getCanonicalPathList(configVar,
Config::Validate);
2010 const auto &loc = config.get(configVar)
.location();
2011 if (!files.isEmpty()) {
2021 OutputDirectory::ensure(s_outDir, loc);
2024 outDir.ensureSubdir(subDir, loc);
2026 for (
const auto &file : files) {
2027 if (!file.isEmpty()) {
2028 const QFileInfo fi(file);
2029 Config::copyFile(loc, fi.absoluteFilePath(), fi.fileName(), templateDir.path());
2036
2037
2038
2039
2042 Config &config = Config::instance();
2043 s_outFileNames.clear();
2044 s_useOutputSubdirs =
true;
2045 if (config.get(format() + Config::dot +
"nosubdirs").asBool())
2048 if (s_outputFormats.isEmpty())
2053 s_outDir = config.getOutputDir(format());
2054 if (s_outDir.isEmpty()) {
2055 Location().fatal(QStringLiteral(
"No output directory specified in "
2056 "configuration file or on the command line"));
2058 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
2063 OutputDirectory::ensure(s_outDir,
Location());
2067 if (!outputDir.toQDir().isEmpty())
2068 Location().error(
"Output directory '%1' exists but is not empty"_L1.arg(s_outDir));
2078 outputDir.ensureSubdir(imagesDir,
Location());
2079 s_imagesOutDir =
std::move(imagesDir);
2082 copyTemplateFiles(format() + Config::dot +
CONFIG_SCRIPTS,
"scripts");
2093
2094
2095
2107
2108
2109
2110
2113 return *outStreamStack.top();
2118 return QFileInfo(
static_cast<QFile *>(
out().device())->fileName()).fileName();
2127 return s_outputPrefixes[u"QML"_s];
2129 return s_outputPrefixes[u"CPP"_s];
2142 return s_outputSuffixes[u"QML"_s];
2144 return s_outputSuffixes[u"CPP"_s];
2154 QStringView *contents, QStringView *par1)
2157 if (i >= n || src[i] != c)
2162 while (i < n && src[i] == ' ')
2172 if (tag != QStringView(src).mid(i, tag.size())) {
2184 while (i < n && src[i].isLetter())
2186 if (src[i] ==
'=') {
2191 while (i < n && src[i] !=
'"')
2193 *par1 = QStringView(src).mid(j, i - j);
2204 if (i + 4 + tag.size() > n)
2208 if (src[i + 1] !=
'/')
2210 if (src[i + 2] !=
'@')
2212 if (tag != QStringView(src).mid(i + 3, tag.size()))
2214 if (src[i + 3 + tag.size()] !=
'>')
2219 *contents = QStringView(src).mid(j, i - j);
2221 i += tag.size() + 4;
2231 QString t = markedCode;
2232 t.replace(tag, QString());
2233 t.replace(quot, QLatin1String(
"\""));
2234 t.replace(gt, QLatin1String(
">"));
2235 t.replace(lt, QLatin1String(
"<"));
2236 t.replace(amp, QLatin1String(
"&"));
2244 while (atom && atom
->type() != type) {
2252
2253
2263 m_sectionNumber.clear();
2269 const auto fn =
static_cast<
const FunctionNode *>(node);
2270 if (fn->overloadNumber() == 0) {
2271 QString alternateName;
2274 if (fn->name().startsWith(
"set") && fn->name().size() >= 4) {
2275 alternateName = fn->name()[3].toLower();
2276 alternateName += fn->name().mid(4);
2277 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2279 if (!alternateFunc) {
2280 alternateName =
"is" + fn->name().mid(3);
2281 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2282 if (!alternateFunc) {
2283 alternateName =
"has" + fn->name().mid(3);
2284 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2287 }
else if (!fn->name().isEmpty()) {
2288 alternateName =
"set";
2289 alternateName += fn->name()[0].toUpper();
2290 alternateName += fn->name().mid(1);
2291 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2294 if (alternateFunc && alternateFunc
->access() != Access::Private) {
2296 for (i = 0; i < alsoList.size(); ++i) {
2297 if (alsoList.at(i).toString().contains(alternateName))
2301 if (i == alsoList.size()) {
2304 alternateName +=
"()";
2310 alsoList.prepend(also);
2330 const auto *start{body.firstAtom()};
2336 text << body.subText(text
.isEmpty() ? start : start->next(), end);
2351 for (
const auto &generator : std::as_const(s_generators)) {
2352 if (s_outputFormats.contains(generator->format())) {
2353 OutputProducerRegistry::instance().unregisterProducer(generator);
2354 generator->terminateGenerator();
2381 s_generators.clear();
2383 s_fmtLeftMaps.clear();
2384 s_fmtRightMaps.clear();
2386 s_imagesOutDir.clear();
2392
2393
2394
2395QString
Generator::trimmedTrailing(
const QString &string,
const QString &prefix,
2396 const QString &suffix)
2398 QString trimmed = string;
2399 while (trimmed.size() > 0 && trimmed[trimmed.size() - 1].isSpace())
2400 trimmed.truncate(trimmed.size() - 1);
2402 trimmed.append(suffix);
2403 trimmed.prepend(prefix);
2410 case NodeType::Namespace:
2411 return plural ?
"namespaces"_L1 :
"namespace"_L1;
2412 case NodeType::Class:
2413 return plural ?
"classes"_L1 :
"class"_L1;
2414 case NodeType::Struct:
2415 return plural ?
"structs"_L1 :
"struct"_L1;
2416 case NodeType::Union:
2417 return plural ?
"unions"_L1 :
"union"_L1;
2418 case NodeType::QmlType:
2419 case NodeType::QmlValueType:
2420 return plural ?
"types"_L1 :
"type"_L1;
2421 case NodeType::Page:
2422 return "documentation"_L1;
2423 case NodeType::Enum:
2424 return plural ?
"enums"_L1 :
"enum"_L1;
2425 case NodeType::Typedef:
2426 case NodeType::TypeAlias:
2427 return plural ?
"typedefs"_L1 :
"typedef"_L1;
2429 const auto fn =
static_cast<
const FunctionNode *>(node);
2430 switch (fn->metaness()) {
2431 case Metaness::QmlSignal:
2432 return plural ?
"signals"_L1 :
"signal"_L1;
2433 case Metaness::QmlSignalHandler:
2434 return plural ?
"signal handlers"_L1 :
"signal handler"_L1;
2435 case Metaness::QmlMethod:
2436 return plural ?
"methods"_L1 :
"method"_L1;
2437 case Metaness::MacroWithParams:
2438 case Metaness::MacroWithoutParams:
2439 return plural ?
"macros"_L1 :
"macro"_L1;
2443 return plural ?
"functions"_L1 :
"function"_L1;
2445 case NodeType::Property:
2446 case NodeType::QmlEnum:
2447 return plural ?
"enumerations"_L1 :
"enumeration"_L1;
2448 case NodeType::QmlProperty:
2449 return plural ?
"properties"_L1 :
"property"_L1;
2450 case NodeType::Module:
2451 case NodeType::QmlModule:
2452 return plural ?
"modules"_L1 :
"module"_L1;
2453 case NodeType::Variable:
2454 return plural ?
"variables"_L1 :
"variable"_L1;
2455 case NodeType::Concept:
2456 return plural ?
"concepts"_L1 :
"concept"_L1;
2458 const auto *shared =
static_cast<
const SharedCommentNode *>(node);
2459 if (shared->isPropertyGroup())
2460 return plural ?
"property groups"_L1 :
"property group"_L1;
2461 const auto &collective = shared->collective();
2462 return collective.first()->nodeTypeString();
2465 return "documentation"_L1;
2471 Location::internalError(QStringLiteral(
"unknown atom type '%1' in %2 generator")
2472 .arg(atom->typeString(), format()));
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2509 if (!cn || (cn->cmakeComponent().isEmpty() && cn->cmakePackage().isEmpty())) {
2513 const QString package =
2514 cn->cmakePackage().isEmpty() ?
"Qt" + QString::number(QT_VERSION_MAJOR) : cn->cmakePackage();
2516 QString findPackageText;
2517 if (cn->cmakeComponent().isEmpty()) {
2518 findPackageText =
"find_package(" + package +
" REQUIRED)";
2520 findPackageText =
"find_package(" + package +
" REQUIRED COMPONENTS " + cn->cmakeComponent() +
")";
2524 if (cn->cmakeTargetItem().isEmpty()) {
2525 if (cn->cmakeComponent().isEmpty()) {
2526 targetText = package +
"::" + package;
2528 targetText = package +
"::" + cn->cmakeComponent();
2531 targetText = cn->cmakeTargetItem();
2534 const QString targetLinkLibrariesText =
"target_link_libraries(mytarget PRIVATE " + targetText +
")";
2535 const QStringList cmakeInfo { findPackageText, targetLinkLibrariesText };
2537 return std::make_pair(findPackageText, targetLinkLibrariesText);
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550void Generator::addNodeLink(
Text &text,
const QString &nodeRef,
const QString &linkText) {
2558
2559
2560
2561
2562
2563
2564
2565
2569 Utilities::stringForNode(node),
2570 linkText.isEmpty() ? node->name() : linkText
2575
2576
2577
2578
2579
2580
2581
2587 QString className = func->parent()->name();
2588 QString functionName = func->name();
2589 QString typeList = func->parameters().generateTypeList();
2590 QString typeAndNameList = func->parameters().generateTypeAndNameList();
2591 QString nameList = func->parameters().generateNameList();
2592 QString objectName = generateObjectName(className);
2598 "// Connect using qOverload:\n"
2599 "connect(%1, qOverload<%2>(&%3::%4),\n"
2600 " receiver, &ReceiverClass::slot);\n\n"
2601 "// Or using a lambda:\n"
2602 "connect(%1, qOverload<%2>(&%3::%4),\n"
2603 " this, [](%5) { /* handle %4 */ });")
2604 .arg(objectName, typeList, className, functionName, typeAndNameList);
2607 "// Connect using qOverload:\n"
2608 "connect(sender, &SenderClass::signal,\n"
2609 " %1, qOverload<%2>(&%3::%4));\n\n"
2610 "// Or using a lambda as wrapper:\n"
2611 "connect(sender, &SenderClass::signal,\n"
2612 " %1, [receiver = %1](%5) { receiver->%4(%6); });")
2613 .arg(objectName, typeList, className, functionName, typeAndNameList, nameList);
2620
2621
2622
2625 QString name = className;
2627 if (name.startsWith(
'Q') && name.length() > 1)
2630 if (!name.isEmpty())
2631 name[0] = name[0].toLower();
#define ATOM_FORMATTING_TELETYPE
#define ATOM_FORMATTING_BOLD
#define ATOM_FORMATTING_TRADEMARK
#define ATOM_FORMATTING_ITALIC
#define ATOM_FORMATTING_LINK
#define ATOM_FORMATTING_PARAMETER
The Atom class is the fundamental unit for representing documents internally.
AtomType type() const
Return the type of this atom.
AtomType
\value AnnotatedList \value AutoLink \value BaseName \value BriefLeft \value BriefRight \value C \val...
const Atom * next() const
Return the next atom in the atom list.
The ClassNode represents a C++ class.
A class for holding the members of a collection of doc pages.
const Location & location() const
bool asBool() const
Returns this config variable as a boolean.
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
QStringMultiMap * metaTagMap() const
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
This node is used to represent any kind of function being documented.
bool isPrivateSignal() const
QString kindString() const
Returns a string representing the kind of function this Function node represents, which depends on th...
const Parameters & parameters() const
const QString & overridesThis() const
bool isDeprecated() const override
\reimp
bool hasOverloads() const
Returns true if this function has overloads.
bool isMarkedReimp() const override
Returns true if the FunctionNode is marked as a reimplemented function.
bool isIgnored() const
In some cases, it is ok for a public function to be not documented.
bool hasAssociatedProperties() const
virtual QString typeString(const Node *node, bool plural=false)
void appendSignature(Text &text, const Node *node)
Append the signature for the function named in node to text, so that is a link to the documentation f...
virtual void generateCollectionNode(CollectionNode *, CodeMarker *)
virtual void generateProxyPage(Aggregate *, CodeMarker *)
virtual void generateCppReferencePage(Aggregate *, CodeMarker *)
bool generateComparisonCategory(const Node *node, CodeMarker *marker=nullptr)
QMap< QString, QString > & formattingRightMap()
FileResolver & file_resolver
virtual bool generateText(const Text &text, const Node *relative)
virtual void initializeFormat()
Reads format-specific variables from config, sets output (sub)directories, creates them on the filesy...
virtual void generateDocumentation(Node *node)
Recursive writing of HTML files from the root node.
const Atom * generateAtomList(const Atom *atom, const Node *relative, CodeMarker *marker, bool generate, int &numGeneratedAtoms)
void generateStatus(const Node *node, CodeMarker *marker)
virtual void generateAlsoList(const Node *node, CodeMarker *marker)
Generates text for a "see also" list for the given node and marker if a list has been defined.
void appendFullName(Text &text, const Node *apparentNode, const Node *relative, const Node *actualNode=nullptr)
virtual void generateFileList(const ExampleNode *en, CodeMarker *marker, bool images)
This function is called when the documentation for an example is being formatted.
void generateThreadSafeness(const Node *node, CodeMarker *marker)
Generates text that explains how threadsafe and/or reentrant node is.
Generator(FileResolver &file_resolver)
Constructs the generator base class.
QString fullDocumentLocation(const Node *node) const
Returns the full document location.
static bool useOutputSubdirs()
void generateNoexceptNote(const Node *node, CodeMarker *marker)
void unknownAtom(const Atom *atom)
QString generateObjectName(const QString &className)
Generates an appropriate object name for code snippets based on the class name.
virtual bool generateText(const Text &text, const Node *relative, CodeMarker *marker)
Generate the documentation for relative.
int appendSortedQmlNames(Text &text, const Node *base, const QStringList &knownTypes, const QList< Node * > &subs)
void generateLinkToExample(const ExampleNode *en, CodeMarker *marker, const QString &exampleUrl)
Generates an external link to the project folder for example node.
virtual void terminateGenerator()
QString generateOverloadSnippet(const FunctionNode *func)
Generates a contextual code snippet for connecting to an overloaded signal or slot.
static bool matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
void addImageToCopy(const ExampleNode *en, const ResolvedFile &resolved_file)
virtual void generateDocs()
Traverses the database recursively to generate all the documentation.
static bool appendTrademark(const Atom *atom)
Returns true if a trademark symbol should be appended to the output as determined by atom.
void generateEnumValuesForQmlReference(const Node *node, CodeMarker *marker)
virtual int skipAtoms(const Atom *atom, Atom::AtomType type) const
bool m_threeColumnEnumValueTable
QString linkForExampleFile(const QString &path, const QString &fileExt=QString()) const
Constructs an href link from an example file name, which is a path to the example file.
virtual void generateQmlTypePage(QmlTypeNode *, CodeMarker *)
void signatureList(const QList< Node * > &nodes, const Node *relative, CodeMarker *marker)
Generate a bullet list of function signatures.
void appendFullName(Text &text, const Node *apparentNode, const QString &fullName, const Node *actualNode)
static bool s_redirectDocumentationToDevNull
virtual void generateBody(const Node *node, CodeMarker *marker)
Generate the body of the documentation from the qdoc comment found with the entity represented by the...
void beginSubPage(const PageNode *node, const QString &fileName)
Creates the file named fileName in the output directory.
virtual void generatePageNode(PageNode *, CodeMarker *)
virtual ~Generator()
Destroys the generator after removing it from the list of output generators.
void generateSince(const Node *node, CodeMarker *marker)
QMap< QString, QString > & formattingLeftMap()
int appendSortedNames(Text &text, const ClassNode *classe, const QList< RelatedClass > &classes)
void endSubPage()
Flush the text stream associated with the subpage, and then pop it off the text stream stack and dele...
virtual void generateAddendum(const Node *node, Addendum type, CodeMarker *marker)
QString indent(int level, const QString &markedCode)
QString fileName(const Node *node, const QString &extension=QString()) const
If the node has a URL, return the URL as the file name.
virtual void generateAddendum(const Node *node, Addendum type, CodeMarker *marker, AdmonitionPrefix prefix)
static void resetUseOutputSubdirs()
bool generateComparisonTable(const Node *node)
Generates a table of comparison categories for node, combining both self-comparison (from \compares) ...
bool parseArg(const QString &src, const QString &tag, int *pos, int n, QStringView *contents, QStringView *par1=nullptr)
virtual void generateGenericCollectionPage(CollectionNode *, CodeMarker *)
virtual QString fileBase(const Node *node) const
virtual void initializeGenerator()
No-op base implementation.
void initializeTextOutput()
Resets the variables used during text output.
void generateRequiredLinks(const Node *node, CodeMarker *marker)
Generates either a link to the project folder for example node, or a list of links files/images if 'u...
static bool isIncluded(const InclusionPolicy &policy, const NodeContext &context)
static bool requiresDocumentation(const InclusionPolicy &policy, const NodeContext &context)
The Location class provides a way to mark a location in a file.
Location()
Constructs an empty location.
Interface implemented by Node subclasses that can refer to a C++ enum.
virtual const NativeEnum * nativeEnum() const =0
Encapsulates information about native (C++) enum values.
const EnumNode * enumNode() const
QString styleString() const
OpenedList(ListStyle style)
Represents an output directory that has been verified to exist.
const QString & path() const noexcept
Singleton registry for discovering output producers by format.
OutputProducer * producerForFormat(const QString &format) const
Returns the producer registered for format, or nullptr if none.
static OutputProducerRegistry & instance()
Returns the singleton registry instance.
A PageNode is a Node that generates a documentation page.
bool isAttribution() const
This class describes one instance of using the Q_PROPERTY macro.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
const CollectionNode * getModuleNode(const Node *relative)
Returns the collection node representing the module that relative node belongs to,...
Status
Specifies the status of the QQmlIncubator.
const Atom * firstAtom() const
#define CONFIG_REDIRECTDOCUMENTATIONTODEVNULL
#define CONFIG_AUTOLINKERRORS
#define CONFIG_EXTRAIMAGES
#define CONFIG_OUTPUTSUFFIXES
#define CONFIG_OUTPUTPREFIXES
#define CONFIG_PRELIMINARY
#define CONFIG_NOLINKERRORS
#define CONFIG_DESCRIPTION
#define CONFIG_EXAMPLESINSTALLPATH
#define CONFIG_PRODUCTNAME
#define CONFIG_QUOTINGINFORMATION
#define CONFIG_STYLESHEETS
#define CONFIG_IMAGESOUTPUTDIR
#define CONFIG_FORMATTING
QMultiMap< QString, QString > QStringMultiMap
Metaness
Specifies the kind of function a FunctionNode represents.
This namespace holds QDoc-internal utility methods.
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isExternalPage() const
Returns true if the node type is ExternalPage.
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.
virtual bool docMustBeGenerated() const
This function is called to perform a test to decide if the node must have documentation generated.
virtual bool isWrapper() const
Returns true if the node is a class node or a QML type node that is marked as being a wrapper class o...
bool isPrivate() const
Returns true if this node's access is Private.
bool isActive() const
Returns true if this node's status is Active.
bool isNamespace() const
Returns true if the node type is Namespace.
ComparisonCategory comparisonCategory() const
bool hasFileNameBase() const
Returns true if the node's file name base has been set.
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
bool isSharedCommentNode() const
Returns true if the node type is SharedComment.
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.
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
virtual bool isMacro() const
returns true if either FunctionNode::isMacroWithParams() or FunctionNode::isMacroWithoutParams() retu...
bool isEnumType() const
Returns true if the node type is Enum.
virtual Status status() const
Returns the node's status value.
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
virtual bool isAttached() const
Returns true if the QML property or QML method node is marked as attached.
Aggregate * parent() const
Returns the node's parent pointer.
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
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.
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
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.
virtual bool isMarkedReimp() const
Returns true if the FunctionNode is marked as a reimplemented function.
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.
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
bool isSharingComment() const
This function returns true if the node is sharing a comment with other nodes.
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
bool isPreliminary() const
Returns true if this node's status is Preliminary.
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 isExample() const
Returns true if the node type is Example.
bool isIndexNode() const
Returns true if this node was created from something in an index file.
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Represents a file that is reachable by QDoc based on its current configuration.