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;
62QStringList
Generator::s_exampleImageFileNames;
65QHash<QString, QString>
Generator::s_outputPrefixes;
66QHash<QString, QString>
Generator::s_outputSuffixes;
74static QRegularExpression
tag(
"</?@[^>]*>");
81
82
83
84
85
91 names.unite(p->templateDecl()->parameterNames());
97
98
99
100
101
102
103
104
108
109
110
112 const QSet<QString> &documentedNames,
113 const QSet<QString> &allowedNames,
116 for (
const auto &name : documentedNames) {
117 if (!allowedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
118 const auto message = (context == ValidationContext::TemplateDoc)
119 ?
"No such template parameter '%1' in %2"_L1
120 :
"No such parameter '%1' in %2"_L1;
121 node->doc().location().warning(message.arg(name, node->plainFullName()),
122 suggestName(name, allowedNames));
128
129
130
131
132
137 s_generators.prepend(
this);
141
142
143
146 s_generators.removeAll(
this);
150 const Node *actualNode)
152 if (actualNode ==
nullptr)
153 actualNode = apparentNode;
155 addNodeLink(text, actualNode, apparentNode->plainFullName(relative));
159 const Node *actualNode)
161 if (actualNode ==
nullptr)
162 actualNode = apparentNode;
164 addNodeLink(text, actualNode, fullName);
168
169
170
171
178
179
180
181
187 for (
const auto &node : nodes) {
188 text << Atom(Atom::ListItemNumber, QString::number(++count));
189 text << Atom(Atom::ListItemLeft, QString(
"bullet"));
190 appendSignature(text, node);
191 text << Atom(Atom::ListItemRight, QString(
"bullet"));
199 QMap<QString, Text> classMap;
200 for (
const auto &relatedClass : rc) {
201 ClassNode *rcn = relatedClass.m_node;
202 if (rcn && rcn->isInAPI()) {
204 appendFullName(className, rcn, cn);
205 classMap[className.toString().toLower()] = className;
210 const QStringList classNames = classMap.keys();
211 for (
const auto &className : classNames) {
212 text << classMap[className];
213 text << TextUtils::comma(index++, classNames.size());
221 QMap<QString, Text> classMap;
223 QStringList typeNames(knownTypes);
224 for (
const auto sub : subs)
225 typeNames << sub->name();
227 for (
const auto sub : subs) {
229 appendFullName(full_name, sub, base);
231 if (typeNames.count(sub->name()) > 1)
232 full_name << Atom(Atom::String,
" (%1)"_L1.arg(sub->logicalModuleName()));
233 classMap[full_name.toString().toLower()] = full_name;
237 const auto &names = classMap.keys();
238 for (
const auto &name : names)
239 text << classMap[name] << TextUtils::comma(index++, names.size());
244
245
246
247
248
249
250
258 if (s_outFileNames.contains(fileName) && !node
->isAttribution() && !fileName.contains(
"-attribution-"_L1))
259 node
->location().warning(
"Already generated %1 for this project"_L1.arg(fileName));
261 QString path = outputDir() + QLatin1Char(
'/') + fileName;
264 auto outFile =
new QFile(outPath);
267 const QString warningText {
"Output file already exists, overwriting %1"_L1.arg(outFile->fileName())};
268 if (qEnvironmentVariableIsSet(
"QDOC_ALL_OVERWRITES_ARE_WARNINGS"))
271 qCDebug(lcQdoc) << qUtf8Printable(warningText);
274 if (!outFile->open(QFile::WriteOnly | QFile::Text)) {
276 QStringLiteral(
"Cannot open output file '%1'").arg(outFile->fileName()));
279 qCDebug(lcQdoc,
"Writing: %s", qPrintable(path));
280 s_outFileNames << fileName;
281 s_trademarks.clear();
286
287
288
289
292 QFile *outFile = openSubPageFile(
static_cast<
const PageNode*>(node), fileName);
293 auto *out =
new QTextStream(outFile);
294 outStreamStack.push(out);
298
299
300
301
304 outStreamStack.top()->flush();
305 delete outStreamStack.top()->device();
306 delete outStreamStack.pop();
315 return node->fileNameBase();
317 QString result = Utilities::computeFileBase(
319 [](
const Node *n) {
return outputPrefix(n); },
320 [](
const Node *n) {
return outputSuffix(n); });
322 const_cast<
Node *>(node)->setFileNameBase(result);
327
328
329
330
331
334 return Utilities::linkForExampleFile(path, s_project, fileExt.isEmpty() ? fileExtension() : fileExt);
338
339
340
343 return Utilities::exampleFileTitle(relative->files(), relative->images(), fileName);
347
348
349
350
351
354 if (!node->url().isEmpty())
361 QFileInfo originalName(node->name());
362 QString suffix = originalName.suffix();
363 if (!suffix.isEmpty() && suffix !=
"html") {
365 QString name = fileBase(node);
366 return name + QLatin1Char(
'.') + suffix;
370 QString name = fileBase(node) + QLatin1Char(
'.');
371 return name + (extension.isNull() ? fileExtension() : extension);
375
376
377
378
379
380
381
382
383QString
Generator::cleanRef(
const QString &ref,
bool xmlCompliant)
394 clean.reserve(ref.size() + 20);
395 const QChar c = ref[0];
396 const uint u = c.unicode();
398 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (!xmlCompliant && u >=
'0' && u <=
'9')) {
400 }
else if (xmlCompliant && u >=
'0' && u <=
'9') {
401 clean += QLatin1Char(
'A') + c;
402 }
else if (u ==
'~') {
404 }
else if (u ==
'_') {
405 clean +=
"underscore.";
407 clean += QLatin1Char(
'A');
410 for (
int i = 1; i < ref.size(); i++) {
411 const QChar c = ref[i];
412 const uint u = c.unicode();
413 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (u >=
'0' && u <=
'9') || u ==
'-'
414 || u ==
'_' || (xmlCompliant && u ==
':') || u ==
'.') {
416 }
else if (c.isSpace()) {
417 clean += QLatin1Char(
'-');
418 }
else if (u ==
'!') {
420 }
else if (u ==
'&') {
422 }
else if (u ==
'<') {
424 }
else if (u ==
'=') {
426 }
else if (u ==
'>') {
428 }
else if (u ==
'#') {
429 clean += QLatin1Char(
'#');
431 clean += QLatin1Char(
'-');
432 clean += QString::number(
static_cast<
int>(u), 16);
440 return s_fmtLeftMaps[format()];
445 return s_fmtRightMaps[format()];
449
450
455 if (!node->url().isEmpty())
463
464
465
466 if (!fileBase(node).isEmpty())
467 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
471 return fileBase(node) + QLatin1Char(
'.') + fileExtension();
473 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
474 }
else if (fileBase(node).isEmpty())
477 Node *parentNode =
nullptr;
481 if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
482 parentName = fullDocumentLocation(node->parent());
486 case NodeType::Class:
487 case NodeType::Struct:
488 case NodeType::Union:
489 case NodeType::Namespace:
490 case NodeType::Proxy:
491 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
494 const auto *fn =
static_cast<
const FunctionNode *>(node);
495 switch (fn->metaness()) {
497 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal";
500 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal-handler";
503 anchorRef = QLatin1Char(
'#') + node->name() +
"-method";
507 anchorRef =
"#dtor." + fn->name().mid(1);
508 else if (
const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty())
509 return fullDocumentLocation(p);
510 else if (fn->overloadNumber() > 0)
511 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name()) + QLatin1Char(
'-')
512 + QString::number(fn->overloadNumber());
514 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name());
520
521
522
523
526 anchorRef = QLatin1Char(
'#') + node->name() +
"-enum";
529 const auto *tdef =
static_cast<
const TypedefNode *>(node);
530 if (tdef->associatedEnum())
531 return fullDocumentLocation(tdef->associatedEnum());
534 anchorRef = QLatin1Char(
'#') + node->name() +
"-typedef";
537 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
545 anchorRef = QLatin1Char(
'#') + node->name() +
"-attached-prop";
547 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
550 anchorRef = QLatin1Char(
'#') + node->name() +
"-var";
558 parentName = fileBase(node);
559 parentName.replace(QLatin1Char(
'/'), QLatin1Char(
'-'))
560 .replace(QLatin1Char(
'.'), QLatin1Char(
'-'));
561 parentName += QLatin1Char(
'.') + fileExtension();
569 parentName.replace(QLatin1Char(
'.') + fileExtension(),
570 "-obsolete." + fileExtension());
573 return parentName.toLower() + anchorRef;
577
578
579
580
581
582
583
586 QList<Text> alsoList = node
->doc().alsoList();
587 supplementAlsoList(node, alsoList);
589 if (!alsoList.isEmpty()) {
596 for (
const auto &also : std::as_const(alsoList)) {
598 const Atom *atom = also.firstAtom();
599 QString link = atom->string();
600 if (!used.contains(link)) {
605 if (m_qdb->findNodeForAtom(atom, node, ref) == node && ref.isEmpty())
606 node->doc().location().warning(
"Redundant link to self in \\sa command for %1"_L1.arg(node->name()));
611 for (
const auto &also : std::as_const(items))
612 text << also << TextUtils::separator(i++, items.size());
620 bool generate,
int &numAtoms)
622 while (atom !=
nullptr) {
624 int numAtoms0 = numAtoms;
625 bool rightFormat = canHandleFormat(atom->string());
640 if (generate && numAtoms0 == numAtoms) {
641 relative
->location().warning(QStringLiteral(
"Output format %1 not handled %2")
642 .arg(format(), outFileName()));
643 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
653 n += generateAtom(atom, relative, marker);
665
666
667
670 const FunctionNode *fn = node->isFunction() ?
static_cast<
const FunctionNode *>(node) :
nullptr;
673
674
675
679 text <<
"Destroys the instance of ";
680 text << fn
->parent()->name() <<
".";
682 text <<
" The destructor is virtual.";
688 text <<
"Default-constructs an instance of "
695 text <<
"Copy-constructs an instance of "
702 text <<
"Move-constructs an instance of "
709 text <<
"Copy-assigns "
712 <<
" to this " << fn
->parent()->name() <<
" instance.";
718 text <<
"Move-assigns "
721 <<
" to this " << fn
->parent()->name() <<
" instance.";
726 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
730 QStringLiteral(
"No documentation for %1 '%2'")
735 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
739 QStringLiteral(
"No documentation for '%1'").arg(node->plainSignature()));
744 generateReimplementsClause(fn, marker);
746 if (
static_cast<
const PropertyNode *>(node)->propertyType() != PropertyNode::PropertyType::StandardProperty)
771 const auto *enume =
static_cast<
const EnumNode *>(node);
773 QSet<QString> definedItems;
774 const QList<EnumItem> &items = enume->items();
775 for (
const auto &item : items)
776 definedItems.insert(item.name());
778 const auto &documentedItemList = enume->doc().enumItemNames();
779 QSet<QString> documentedItems(documentedItemList.cbegin(), documentedItemList.cend());
780 const QSet<QString> allItems = definedItems + documentedItems;
781 if (allItems.size() > definedItems.size()
782 || allItems.size() > documentedItems.size()) {
783 for (
const auto &it : allItems) {
784 if (!definedItems.contains(it)) {
785 node->doc().location().warning(
786 QStringLiteral(
"No such enum item '%1' in %2")
787 .arg(it, node->plainFullName()),
788 QStringLiteral(
"Maybe you meant '%1'?")
789 .arg(suggestName(it, definedItems, documentedItems)));
790 }
else if (!documentedItems.contains(it)) {
791 node->doc().location().warning(
792 QStringLiteral(
"Undocumented enum item '%1' in %2")
793 .arg(it, node->plainFullName()));
806 const QSet<QString> requiredFunctionParams = fn
->parameters().getNames();
807 const QSet<QString> requiredTemplateParams = fn->templateDecl()
808 ? fn->templateDecl()->requiredParameterNamesForFunctions()
810 const QSet<QString> requiredNames = requiredFunctionParams + requiredTemplateParams;
814 const QSet<QString> ownTemplateParams = fn->templateDecl()
815 ? fn->templateDecl()->parameterNames()
817 const QSet<QString> allowedNames = requiredNames + ownTemplateParams
818 + inheritedTemplateParamNames(fn);
820 const QSet<QString> documentedNames = fn
->doc().parameterNames();
823 for (
const auto &name : requiredNames) {
824 if (!documentedNames.contains(name)) {
825 if (fn->isActive() || fn->isPreliminary()) {
828 if (!fn->isMarkedReimp() && !fn->isOverload()
829 && !(fn->isSomeCtor() && fn->hasOverloads())) {
831 const bool isTemplateParam = requiredTemplateParams.contains(name);
832 fn->doc().location().warning(
833 "Undocumented %1 '%2' in %3"_L1
834 .arg(isTemplateParam ?
"template parameter"_L1
836 name, node->plainFullName()));
842 warnAboutUnknownDocumentedParams(fn, documentedNames, allowedNames,
845
846
847
848
851 if (!fn
->doc().body().contains(
"return"))
853 QStringLiteral(
"Undocumented return value "
854 "(hint: use 'return' or 'returns' in the text"));
857 if (
auto *qpn =
static_cast<
const QmlPropertyNode *>(node); !qpn->validateDataType())
858 qpn->doc().location().warning(
"Invalid QML property type: %1"_L1.arg(qpn->dataType()));
864 const QSet<QString> requiredNames = node->templateDecl()->parameterNames();
865 const QSet<QString> allowedNames = requiredNames + inheritedTemplateParamNames(node);
866 const QSet<QString> documentedNames = node
->doc().parameterNames();
869 for (
const auto &name : requiredNames) {
870 if (!documentedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
871 node->doc().location().warning(
872 "Undocumented template parameter '%1' in %2"_L1
873 .arg(name, node->plainFullName()));
878 warnAboutUnknownDocumentedParams(node, documentedNames, allowedNames,
887
888
889
890
891
897 const auto *en =
static_cast<
const ExampleNode *>(node);
900 if (exampleUrl.isEmpty()) {
901 if (!en->noAutoList()) {
906 generateLinkToExample(en, marker, exampleUrl);
911
912
913
914
915
916
918 const QString &baseUrl)
920 QString exampleUrl(baseUrl);
922#ifndef QT_BOOTSTRAPPED
923 link = QUrl(exampleUrl).host();
927 link.prepend(
"Example project");
929 const QLatin1Char separator(
'/');
930 const QLatin1Char placeholder(
'\1');
931 if (!exampleUrl.contains(placeholder)) {
932 if (!exampleUrl.endsWith(separator))
933 exampleUrl += separator;
934 exampleUrl += placeholder;
941 pathRoot = metaTagMap->value(QLatin1String(
"installpath"));
942 if (pathRoot.isEmpty())
944 QStringList path = QStringList() << pathRoot << en->name();
945 path.removeAll(QString());
949 <<
Atom(
Atom::Link, exampleUrl.replace(placeholder, path.join(separator)))
959 const QString prefix(
"/images/used-in-examples");
965 s_exampleImageFileNames << prefix.mid(1) +
"/" + resolved_file.get_query();
968 OutputDirectory::ensure(s_outDir, en
->location());
970 outDir.ensureSubdir(prefix.mid(1), en
->location());
972 const QFileInfo fi{resolved_file.get_query()};
973 const QString relativePath = fi.path();
975 const bool hasSubdir = !relativePath.isEmpty() && relativePath !=
"."_L1;
977 hasSubdir ? imagesUsedInExamplesDir.ensureSubdir(relativePath, en
->location())
978 : imagesUsedInExamplesDir;
980 const QString fileName = fi.fileName();
996
997
998
999
1000
1001
1011 paths = en->images();
1015 paths = en->files();
1018 std::sort(paths.begin(), paths.end(), Generator::comparePaths);
1023 for (
const auto &path : std::as_const(paths)) {
1024 auto maybe_resolved_file{file_resolver.resolve(path)};
1025 if (!maybe_resolved_file) {
1027 QString details = std::transform_reduce(
1028 file_resolver.get_search_directories().cbegin(),
1029 file_resolver.get_search_directories().cend(),
1030 u"Searched directories:"_s,
1032 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
1035 en->location().warning(u"(Generator)Cannot find file to quote from: %1"_s.arg(path), details);
1040 const auto &file{*maybe_resolved_file};
1042 addImageToCopy(en, file);
1044 generateExampleFilePage(en, file, marker);
1047 text << Atom(Atom::ListItemNumber, openedList.numberString())
1048 << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
1049 << Atom(atomType, file.get_query()) << Atom(Atom::FormattingLeft,
ATOM_FORMATTING_LINK) << file.get_query()
1051 << Atom(Atom::ListItemRight, openedList.styleString());
1054 if (!paths.isEmpty())
1055 generateText(text, en, marker);
1059
1060
1063 if (!node->url().isNull())
1067 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1075
1076
1080 PageNode *pageNode =
static_cast<PageNode *>(node);
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101 auto *cn =
static_cast<CollectionNode *>(node);
1102 if (cn->wasSeen()) {
1103 m_qdb->mergeCollections(cn);
1104 beginSubPage(pageNode, fileName(node));
1107 }
else if (cn->isGenericCollection()) {
1111 QString name = cn->name().toLower();
1112 name.replace(QChar(
' '), QString(
"-"));
1114 cn->tree()->physicalModuleName() +
"-" + name +
"." + fileExtension();
1115 beginSubPage(pageNode, filename);
1120 beginSubPage(pageNode, fileName(node));
1126 beginSubPage(pageNode, fileName(node));
1130 beginSubPage(pageNode, fileName(node));
1131 auto *qcn =
static_cast<QmlTypeNode *>(node);
1135 beginSubPage(pageNode, fileName(node));
1143 auto *aggregate =
static_cast<Aggregate *>(node);
1144 const NodeList &children = aggregate->childNodes();
1145 for (
auto *child : children) {
1146 if (child->isPageNode()) {
1147 generateDocumentation(child);
1148 }
else if (!node->parent() && child->isInAPI() && !child->isRelatedNonmember()
1149 && !child->doc().isAutoGenerated()) {
1151 child->location().warning(u"No documentation generated for %1 '%2' in global scope."_s
1152 .arg(typeString(child), child->name()),
1153 u"Maybe you forgot to use the '\\relates' command?"_s);
1154 child->setStatus(Status::DontDocument);
1155 }
else if (child->isQmlModule() && !child->wasSeen()) {
1157 auto *qmlModule =
static_cast<CollectionNode *>(child);
1158 for (
const auto *member : qmlModule->members()) {
1159 member->location().warning(
1160 u"Undocumented QML module '%1' referred by type '%2' or its members"_s
1161 .arg(qmlModule->name(), member->name()),
1162 u"Maybe you forgot to document '\\qmlmodule %1'?"_s
1163 .arg(qmlModule->name()));
1165 }
else if (child->isQmlType() && !child->hasDoc()) {
1167 auto *qmlType =
static_cast<QmlTypeNode *>(child);
1168 if (
auto qmid = qmlType->logicalModuleName(); !qmid.isEmpty())
1169 qmlType->location().warning(u"No such type '%1' in QML module '%2'"_s
1170 .arg(qmlType->name(), qmid));
1182 const FunctionNode *overrides = cn->findOverriddenFunction(fn);
1188 overrides->parent()->name()
1189 +
"::" + overrides->signature(Node::SignaturePlain);
1192 generateText(text, fn, marker);
1194 fn
->doc().location().warning(
1195 QStringLiteral(
"Illegal \\reimp; no documented virtual function for %1")
1196 .arg(overrides->plainSignature()));
1200 const PropertyNode *sameName = cn->findOverriddenProperty(fn);
1201 if (sameName && sameName
->hasDoc()) {
1203 text <<
Atom::ParaLeft <<
"Reimplements an access function for property: ";
1204 QString fullName = sameName->parent()->name() +
"::" + sameName->name();
1207 generateText(text, fn, marker);
1213 QStringList since = node->since().split(QLatin1Char(
' '));
1216 if (since.size() == 1) {
1218 return productName.isEmpty() ? node->since() : productName +
" " + since[0];
1222 return node->since();
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1248 status = metaMap->value(
"status");
1249 if (!status.isEmpty())
1252 const auto &since = node->deprecatedSince();
1254 status = u"Deprecated"_s;
1255 if (!since.isEmpty())
1256 status +=
" since %1"_L1.arg(since);
1257 }
else if (!since.isEmpty()) {
1258 status =
"Until %1"_L1.arg(since);
1262 status = collection->state();
1265 return status.isEmpty() ?
std::nullopt :
std::optional(status);
1270 if (!node->since().isEmpty()) {
1273 const auto &collective =
static_cast<
const SharedCommentNode *>(node)->collective();
1274 QString typeStr = typeString(collective.first(), collective.size() > 1);
1275 text << Atom::ParaLeft <<
"These " << typeStr <<
" were introduced in "
1276 << formatSince(node) <<
"." << Atom::ParaRight;
1278 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" was introduced in "
1279 << formatSince(node) <<
"." << Atom::ParaRight;
1286 std::vector<
const Node*> nodes;
1288 auto shared_node =
static_cast<
const SharedCommentNode*>(node);
1289 nodes.reserve(shared_node->collective().size());
1290 nodes.insert(nodes.begin(), shared_node->collective().begin(), shared_node->collective().end());
1291 }
else nodes.push_back(node);
1293 std::size_t counter{1};
1294 for (
const Node* node : nodes) {
1295 if (node->isFunction(Genus::CPP)) {
1296 if (
const auto &exception_info =
static_cast<
const FunctionNode*>(node)->getNoexcept(); exception_info && !(*exception_info).isEmpty()) {
1298 text << Atom::NoteLeft
1299 << (nodes.size() > 1 ? QString::fromStdString(
" ("s + std::to_string(counter) +
")"s) : QString::fromStdString(
"This ") + typeString(node))
1300 <<
" is noexcept when "
1301 << Atom(Atom::C, marker->markedUpCode(*exception_info,
nullptr, Location()))
1302 <<
" is " << Atom(Atom::C,
"true") <<
"."
1304 generateText(text, node, marker);
1320 const QString &state =
static_cast<
const CollectionNode*>(node)->state();
1321 if (!state.isEmpty()) {
1322 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" is in "
1329 if (
const auto &version = node->deprecatedSince(); !version.isEmpty()) {
1330 text << Atom::ParaLeft <<
"This " << typeString(node)
1331 <<
" is scheduled for deprecation in version "
1332 << version <<
"." << Atom::ParaRight;
1336 auto description = Config::instance()
1339 description.replace(
'\1'_L1, typeString(node));
1348 text <<
"This " << typeString(node) <<
" is deprecated";
1349 if (
const QString &version = node->deprecatedSince(); !version.isEmpty()) {
1351 if (node
->isQmlNode() && !node->logicalModuleName().isEmpty())
1352 text << node->logicalModuleName() <<
" ";
1356 text <<
". We strongly advise against using it in new code.";
1365 <<
"Part of developer documentation for internal use."
1375
1376
1377
1381 Q_ASSERT(node && !node->name().isEmpty());
1383 text << Atom(Atom::DivLeft,
1384 "class=\"admonition %1\""_L1.arg(prefix == AdmonitionPrefix::Note ? u"note"_s : u"auto"_s));
1399 text <<
"This function can be invoked via the meta-object system and from QML. See "
1405 text <<
"This is a private signal. It can be used in signal connections "
1406 "but cannot be emitted by the user.";
1410 QString handler(node->name());
1411 qsizetype prefixLocation = handler.lastIndexOf(
'.', -2) + 1;
1412 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
1413 handler.insert(prefixLocation, QLatin1String(
"on"));
1414 text <<
"The corresponding handler is "
1423 const auto *fn =
static_cast<
const FunctionNode *>(node);
1424 auto nodes = fn->associatedProperties();
1425 if (nodes.isEmpty())
1430 QMap<PropertyNode::FunctionRole, QList<
const PropertyNode *>> roleGroups;
1431 for (
const auto *n : std::as_const(nodes)) {
1432 const auto *pn =
static_cast<
const PropertyNode *>(n);
1433 if (pn->isInAPI()) {
1434 PropertyNode::FunctionRole role = pn->role(fn);
1435 roleGroups[role].append(pn);
1439 if (roleGroups.isEmpty())
1450 for (
auto role : roleOrder) {
1451 const auto it = roleGroups.constFind(role);
1452 if (it == roleGroups.cend())
1455 const auto &properties = it.value();
1459 case PropertyNode::FunctionRole::Getter:
1460 msg = u"Getter function"_s;
1462 case PropertyNode::FunctionRole::Setter:
1463 msg = u"Setter function"_s;
1465 case PropertyNode::FunctionRole::Resetter:
1466 msg = u"Resetter function"_s;
1468 case PropertyNode::FunctionRole::Notifier:
1469 msg = u"Notifier signal"_s;
1471 case PropertyNode::FunctionRole::Bindable:
1472 msg = u"Bindable function"_s;
1478 if (properties.size() == 1) {
1479 const auto *pn = properties.first();
1480 text << msg << u" for property "_s << Atom(Atom::Link, pn->name())
1484 text << msg << u" for properties "_s;
1485 for (qsizetype i = 0; i < properties.size(); ++i) {
1486 const auto *pn = properties.at(i);
1490 << TextUtils::separator(i, properties.size());
1499 text <<
"This property supports "
1503 text <<
" bindings.";
1508 const auto *func =
static_cast<
const FunctionNode *>(node);
1511 if (func->isPrimaryOverload())
1514 if (func->isSignal() || func->isSlot()) {
1515 QString functionType = func->isSignal() ?
"signal" :
"slot";
1516 const QString &configKey = func->isSignal() ?
"overloadedsignalstarget" :
"overloadedslotstarget";
1517 const QString &defaultTarget = func->isSignal() ?
"connecting-overloaded-signals" :
"connecting-overloaded-slots";
1518 const QString &linkTarget = Config::instance().get(configKey).asString(defaultTarget);
1520 text <<
"This " << functionType <<
" is overloaded. ";
1522 QString snippet = generateOverloadSnippet(func);
1523 if (!snippet.isEmpty()) {
1524 text <<
"To connect to this " << functionType <<
":\n\n"
1528 if (!linkTarget.isEmpty()) {
1529 text <<
"For more examples and approaches, see "
1532 <<
"connecting to overloaded " << functionType <<
"s"
1536 const auto &args = node
->doc().overloadList();
1537 if (args.first().first.isEmpty()) {
1538 text <<
"This is an overloaded function.";
1540 QString target = args.first().first;
1543 if (!target.contains(
"::")) {
1546 target = parent->name() +
"::" + target;
1563
1564
1565
1566
1569 bool result =
false;
1580
1581
1582
1583
1584
1585
1586
1587
1591 bool result =
false;
1593 const NodeList &children =
static_cast<
const Aggregate *>(node)->childNodes();
1594 for (
auto child : children) {
1595 if (!child->isDeprecated()) {
1596 switch (child->threadSafeness()) {
1597 case Node::Reentrant:
1598 reentrant.append(child);
1599 if (ts == Node::ThreadSafe)
1602 case Node::ThreadSafe:
1603 threadsafe.append(child);
1604 if (ts == Node::Reentrant)
1607 case Node::NonReentrant:
1608 nonreentrant.append(child);
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1640 if (atom->count() > 1) {
1641 if (s_trademarks.contains(atom->string(1)))
1643 s_trademarks << atom->string(1);
1656
1657
1658
1661 Text text, rlink, tlink;
1666 bool exceptions =
false;
1677 case Node::NonReentrant:
1680 << typeString(node) <<
" is not " << rlink <<
"." << Atom::ParaRight;
1686 exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
1687 text <<
"All functions in this " << typeString(node) <<
" are ";
1696 text <<
" with the following exceptions:";
1698 text <<
"This " << typeString(node) <<
" is ";
1715 if (!nonreentrant.isEmpty()) {
1717 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1718 signatureList(nonreentrant, node, marker);
1720 if (!threadsafe.isEmpty()) {
1723 text <<
"These functions are also " << tlink <<
":" <<
Atom::ParaRight;
1725 signatureList(threadsafe, node, marker);
1728 if (!reentrant.isEmpty()) {
1730 text <<
"These functions are only " << rlink <<
":" <<
Atom::ParaRight;
1731 signatureList(reentrant, node, marker);
1733 if (!nonreentrant.isEmpty()) {
1736 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1737 signatureList(nonreentrant, node, marker);
1744
1745
1746
1747
1748
1752 if (category == ComparisonCategory::None)
1756 text << Atom::ParaLeft <<
"%1 is "_L1.arg(node->plainFullName())
1758 << QString::fromStdString(comparisonCategoryAsString(category))
1759 << ((category == ComparisonCategory::Equality) ?
"-"_L1 :
"ly "_L1)
1760 << Atom(Atom::String,
"comparable"_L1)
1762 <<
"."_L1 << Atom::ParaRight;
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1786 const auto *map = node
->doc().comparesWithMap();
1788 const bool hasSelfComparison = (selfCategory != ComparisonCategory::None);
1789 const bool hasComparesWithEntries = (map && !map->isEmpty());
1791 if (!hasSelfComparison && !hasComparesWithEntries)
1794 bool hasDescriptions =
false;
1795 if (hasComparesWithEntries) {
1796 for (
const auto &description : *map) {
1797 if (description.firstAtom()->next() != description.lastAtom()) {
1798 hasDescriptions =
true;
1806 text << Atom::ParaLeft
1808 <<
"%1 Comparisons"_L1.arg(node->plainFullName())
1812 text << Atom(Atom::TableLeft,
"generic"_L1);
1814 text << Atom::TableHeaderLeft
1815 << Atom::TableItemLeft <<
"Category"_L1 << Atom::TableItemRight
1816 << Atom::TableItemLeft <<
"Comparable Types"_L1 << Atom::TableItemRight;
1817 if (hasDescriptions)
1818 text << Atom::TableItemLeft <<
"Description"_L1 << Atom::TableItemRight;
1822 if (hasSelfComparison) {
1823 const QString &category = QString::fromStdString(comparisonCategoryAsString(selfCategory));
1829 if (hasDescriptions)
1835 if (hasComparesWithEntries) {
1836 for (
auto [key, description] : map->asKeyValueRange()) {
1837 const QString &category = QString::fromStdString(comparisonCategoryAsString(key));
1839 text << Atom::TableRowLeft;
1841 text << Atom::TableItemLeft << category << Atom::TableItemRight;
1843 text << Atom::TableItemLeft;
1844 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
1845 for (
const auto &name : types)
1846 text << Atom(Atom::AutoLink, name)
1847 << TextUtils::separator(types.indexOf(name), types.size());
1848 text << Atom::TableItemRight;
1850 if (hasDescriptions) {
1851 text << Atom::TableItemLeft;
1852 if (description.firstAtom()->next() != description.lastAtom())
1853 text << Text::subText(description.firstAtom()->next(), description.lastAtom());
1854 text << Atom::TableItemRight;
1857 text << Atom::TableRowRight;
1868
1869
1872 s_currentGenerator =
this;
1884 if (
auto *gen =
dynamic_cast<
Generator *>(producer))
1890 for (
const auto &generator : std::as_const(s_generators)) {
1891 if (generator->format() == format)
1906 while (i < markedCode.size()) {
1907 if (markedCode.at(i) == QLatin1Char(
'\n')) {
1911 for (
int j = 0; j < level; j++)
1912 t += QLatin1Char(
' ');
1916 t += markedCode.at(i++);
1923 Config &config = Config::instance();
1924 s_outputFormats = config.getOutputFormats();
1927 for (
auto &g : s_generators) {
1928 if (s_outputFormats.contains(g->format())) {
1929 s_currentGenerator = g;
1930 OutputProducerRegistry::instance().registerProducer(g);
1931 g->initializeGenerator();
1936 for (
const auto &n : configFormatting) {
1938 const auto &formattingDotNames = config.subVars(formattingDotName);
1939 for (
const auto &f : formattingDotNames) {
1940 const auto &configVar = config.get(formattingDotName + Config::dot + f);
1941 QString def{configVar.asString()};
1942 if (!def.isEmpty()) {
1943 int numParams = Config::numParams(def);
1944 int numOccs = def.count(
"\1");
1945 if (numParams != 1) {
1946 configVar.location().warning(QStringLiteral(
"Formatting '%1' must "
1948 "parameter (found %2)")
1949 .arg(n, numParams));
1950 }
else if (numOccs > 1) {
1951 configVar.location().fatal(QStringLiteral(
"Formatting '%1' must "
1952 "contain exactly one "
1953 "occurrence of '\\1' "
1957 int paramPos = def.indexOf(
"\1");
1958 s_fmtLeftMaps[f].insert(n, def.left(paramPos));
1959 s_fmtRightMaps[f].insert(n, def.mid(paramPos + 1));
1966 s_outDir = config.getOutputDir();
1967 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
1969 s_outputPrefixes.clear();
1971 if (!items.isEmpty()) {
1972 for (
const auto &prefix : items)
1973 s_outputPrefixes[prefix] =
1976 if (!items.contains(u"QML"_s))
1977 s_outputPrefixes[u"QML"_s] = u"qml-"_s;
1979 s_outputSuffixes.clear();
1982 + Config::dot + suffix).asString();
1989
1990
1991
1992void Generator::copyTemplateFiles(
const QString &configVar,
const QString &subDir)
2009 Config &config = Config::instance();
2010 QStringList files = config.getCanonicalPathList(configVar,
Config::Validate);
2011 const auto &loc = config.get(configVar)
.location();
2012 if (!files.isEmpty()) {
2022 OutputDirectory::ensure(s_outDir, loc);
2025 outDir.ensureSubdir(subDir, loc);
2027 for (
const auto &file : files) {
2028 if (!file.isEmpty()) {
2029 const QFileInfo fi(file);
2030 Config::copyFile(loc, fi.absoluteFilePath(), fi.fileName(), templateDir.path());
2037
2038
2039
2040
2043 Config &config = Config::instance();
2044 s_outFileNames.clear();
2045 s_exampleImageFileNames.clear();
2046 s_useOutputSubdirs =
true;
2047 if (config.get(format() + Config::dot +
"nosubdirs").asBool())
2050 if (s_outputFormats.isEmpty())
2055 s_outDir = config.getOutputDir(format());
2056 if (s_outDir.isEmpty()) {
2057 Location().fatal(QStringLiteral(
"No output directory specified in "
2058 "configuration file or on the command line"));
2060 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
2065 OutputDirectory::ensure(s_outDir,
Location());
2069 if (!outputDir.toQDir().isEmpty())
2070 Location().error(
"Output directory '%1' exists but is not empty"_L1.arg(s_outDir));
2080 outputDir.ensureSubdir(imagesDir,
Location());
2081 s_imagesOutDir =
std::move(imagesDir);
2084 copyTemplateFiles(format() + Config::dot +
CONFIG_SCRIPTS,
"scripts");
2095
2096
2097
2109
2110
2111
2112
2115 return *outStreamStack.top();
2120 return QFileInfo(
static_cast<QFile *>(
out().device())->fileName()).fileName();
2129 return s_outputPrefixes[u"QML"_s];
2131 return s_outputPrefixes[u"CPP"_s];
2144 return s_outputSuffixes[u"QML"_s];
2146 return s_outputSuffixes[u"CPP"_s];
2156 QStringView *contents, QStringView *par1)
2159 if (i >= n || src[i] != c)
2164 while (i < n && src[i] == ' ')
2174 if (tag != QStringView(src).mid(i, tag.size())) {
2186 while (i < n && src[i].isLetter())
2188 if (src[i] ==
'=') {
2193 while (i < n && src[i] !=
'"')
2195 *par1 = QStringView(src).mid(j, i - j);
2206 if (i + 4 + tag.size() > n)
2210 if (src[i + 1] !=
'/')
2212 if (src[i + 2] !=
'@')
2214 if (tag != QStringView(src).mid(i + 3, tag.size()))
2216 if (src[i + 3 + tag.size()] !=
'>')
2221 *contents = QStringView(src).mid(j, i - j);
2223 i += tag.size() + 4;
2233 QString t = markedCode;
2234 t.replace(tag, QString());
2235 t.replace(quot, QLatin1String(
"\""));
2236 t.replace(gt, QLatin1String(
">"));
2237 t.replace(lt, QLatin1String(
"<"));
2238 t.replace(amp, QLatin1String(
"&"));
2246 while (atom && atom
->type() != type) {
2254
2255
2265 m_sectionNumber.clear();
2271 const auto fn =
static_cast<
const FunctionNode *>(node);
2272 if (fn->overloadNumber() == 0) {
2273 QString alternateName;
2276 if (fn->name().startsWith(
"set") && fn->name().size() >= 4) {
2277 alternateName = fn->name()[3].toLower();
2278 alternateName += fn->name().mid(4);
2279 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2281 if (!alternateFunc) {
2282 alternateName =
"is" + fn->name().mid(3);
2283 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2284 if (!alternateFunc) {
2285 alternateName =
"has" + fn->name().mid(3);
2286 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2289 }
else if (!fn->name().isEmpty()) {
2290 alternateName =
"set";
2291 alternateName += fn->name()[0].toUpper();
2292 alternateName += fn->name().mid(1);
2293 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2296 if (alternateFunc && alternateFunc
->access() != Access::Private) {
2298 for (i = 0; i < alsoList.size(); ++i) {
2299 if (alsoList.at(i).toString().contains(alternateName))
2303 if (i == alsoList.size()) {
2306 alternateName +=
"()";
2312 alsoList.prepend(also);
2332 const auto *start{body.firstAtom()};
2338 text << body.subText(text
.isEmpty() ? start : start->next(), end);
2353 for (
const auto &generator : std::as_const(s_generators)) {
2354 if (s_outputFormats.contains(generator->format())) {
2355 OutputProducerRegistry::instance().unregisterProducer(generator);
2356 generator->terminateGenerator();
2383 s_generators.clear();
2385 s_fmtLeftMaps.clear();
2386 s_fmtRightMaps.clear();
2388 s_imagesOutDir.clear();
2394
2395
2396
2397QString
Generator::trimmedTrailing(
const QString &string,
const QString &prefix,
2398 const QString &suffix)
2400 QString trimmed = string;
2401 while (trimmed.size() > 0 && trimmed[trimmed.size() - 1].isSpace())
2402 trimmed.truncate(trimmed.size() - 1);
2404 trimmed.append(suffix);
2405 trimmed.prepend(prefix);
2412 case NodeType::Namespace:
2413 return plural ?
"namespaces"_L1 :
"namespace"_L1;
2414 case NodeType::Class:
2415 return plural ?
"classes"_L1 :
"class"_L1;
2416 case NodeType::Struct:
2417 return plural ?
"structs"_L1 :
"struct"_L1;
2418 case NodeType::Union:
2419 return plural ?
"unions"_L1 :
"union"_L1;
2420 case NodeType::QmlType:
2421 case NodeType::QmlValueType:
2422 return plural ?
"types"_L1 :
"type"_L1;
2423 case NodeType::Page:
2424 return "documentation"_L1;
2425 case NodeType::Enum:
2426 return plural ?
"enums"_L1 :
"enum"_L1;
2427 case NodeType::Typedef:
2428 case NodeType::TypeAlias:
2429 return plural ?
"typedefs"_L1 :
"typedef"_L1;
2431 const auto fn =
static_cast<
const FunctionNode *>(node);
2432 switch (fn->metaness()) {
2433 case Metaness::QmlSignal:
2434 return plural ?
"signals"_L1 :
"signal"_L1;
2435 case Metaness::QmlSignalHandler:
2436 return plural ?
"signal handlers"_L1 :
"signal handler"_L1;
2437 case Metaness::QmlMethod:
2438 return plural ?
"methods"_L1 :
"method"_L1;
2439 case Metaness::MacroWithParams:
2440 case Metaness::MacroWithoutParams:
2441 return plural ?
"macros"_L1 :
"macro"_L1;
2445 return plural ?
"functions"_L1 :
"function"_L1;
2447 case NodeType::QmlEnum:
2448 return plural ?
"enumerations"_L1 :
"enumeration"_L1;
2449 case NodeType::Property:
2450 case NodeType::QmlProperty:
2451 return plural ?
"properties"_L1 :
"property"_L1;
2452 case NodeType::Module:
2453 case NodeType::QmlModule:
2454 return plural ?
"modules"_L1 :
"module"_L1;
2455 case NodeType::Variable:
2456 return plural ?
"variables"_L1 :
"variable"_L1;
2457 case NodeType::Concept:
2458 return plural ?
"concepts"_L1 :
"concept"_L1;
2460 const auto *shared =
static_cast<
const SharedCommentNode *>(node);
2461 if (shared->isPropertyGroup())
2462 return plural ?
"property groups"_L1 :
"property group"_L1;
2463 const auto &collective = shared->collective();
2464 return collective.first()->nodeTypeString();
2467 return "documentation"_L1;
2473 Location::internalError(QStringLiteral(
"unknown atom type '%1' in %2 generator")
2474 .arg(atom->typeString(), format()));
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
2507
2508
2511 if (!cn || (cn->cmakeComponent().isEmpty() && cn->cmakePackage().isEmpty())) {
2515 const QString package =
2516 cn->cmakePackage().isEmpty() ?
"Qt" + QString::number(QT_VERSION_MAJOR) : cn->cmakePackage();
2518 QString findPackageText;
2519 if (cn->cmakeComponent().isEmpty()) {
2520 findPackageText =
"find_package(" + package +
" REQUIRED)";
2522 findPackageText =
"find_package(" + package +
" REQUIRED COMPONENTS " + cn->cmakeComponent() +
")";
2526 if (cn->cmakeTargetItem().isEmpty()) {
2527 if (cn->cmakeComponent().isEmpty()) {
2528 targetText = package +
"::" + package;
2530 targetText = package +
"::" + cn->cmakeComponent();
2533 targetText = cn->cmakeTargetItem();
2536 const QString targetLinkLibrariesText =
"target_link_libraries(mytarget PRIVATE " + targetText +
")";
2537 const QStringList cmakeInfo { findPackageText, targetLinkLibrariesText };
2539 return std::make_pair(findPackageText, targetLinkLibrariesText);
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552void Generator::addNodeLink(
Text &text,
const QString &nodeRef,
const QString &linkText) {
2560
2561
2562
2563
2564
2565
2566
2567
2571 Utilities::stringForNode(node),
2572 linkText.isEmpty() ? node->name() : linkText
2577
2578
2579
2580
2581
2582
2583
2589 QString className = func->parent()->name();
2590 QString functionName = func->name();
2591 QString typeList = func->parameters().generateTypeList();
2592 QString typeAndNameList = func->parameters().generateTypeAndNameList();
2593 QString nameList = func->parameters().generateNameList();
2594 QString objectName = generateObjectName(className);
2600 "// Connect using qOverload:\n"
2601 "connect(%1, qOverload<%2>(&%3::%4),\n"
2602 " receiver, &ReceiverClass::slot);\n\n"
2603 "// Or using a lambda:\n"
2604 "connect(%1, qOverload<%2>(&%3::%4),\n"
2605 " this, [](%5) { /* handle %4 */ });")
2606 .arg(objectName, typeList, className, functionName, typeAndNameList);
2609 "// Connect using qOverload:\n"
2610 "connect(sender, &SenderClass::signal,\n"
2611 " %1, qOverload<%2>(&%3::%4));\n\n"
2612 "// Or using a lambda as wrapper:\n"
2613 "connect(sender, &SenderClass::signal,\n"
2614 " %1, [receiver = %1](%5) { receiver->%4(%6); });")
2615 .arg(objectName, typeList, className, functionName, typeAndNameList, nameList);
2622
2623
2624
2627 QString name = className;
2629 if (name.startsWith(
'Q') && name.length() > 1)
2632 if (!name.isEmpty())
2633 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.