38#include <QtCore/qdebug.h>
39#include <QtCore/qdir.h>
40#include <QtCore/qregularexpression.h>
42#ifndef QT_BOOTSTRAPPED
43# include "QtCore/qurl.h"
49using namespace std::literals::string_literals;
53using namespace Qt::StringLiterals;
56QMap<QString, QMap<QString, QString>>
Generator::s_fmtLeftMaps;
57QMap<QString, QMap<QString, QString>>
Generator::s_fmtRightMaps;
58QList<Generator *>
Generator::s_generators;
63QStringList
Generator::s_exampleImageFileNames;
66QHash<QString, QString>
Generator::s_outputPrefixes;
67QHash<QString, QString>
Generator::s_outputSuffixes;
75static QRegularExpression
tag(
"</?@[^>]*>");
82
83
84
85
86
92 names.unite(p->templateDecl()->parameterNames());
98
99
100
101
102
103
104
109
110
111
113 const QSet<QString> &documentedNames,
114 const QSet<QString> &allowedNames,
117 for (
const auto &name : documentedNames) {
118 if (!allowedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
119 const auto message = (context == ValidationContext::TemplateDoc)
120 ?
"No such template parameter '%1' in %2"_L1
121 :
"No such parameter '%1' in %2"_L1;
122 node->doc().location().warning(message.arg(name, node->plainFullName()),
123 suggestName(name, allowedNames));
129
130
131
132
133
138 s_generators.prepend(
this);
142
143
144
147 s_generators.removeAll(
this);
151 const Node *actualNode)
153 if (actualNode ==
nullptr)
154 actualNode = apparentNode;
156 addNodeLink(text, actualNode, apparentNode->plainFullName(relative));
160 const Node *actualNode)
162 if (actualNode ==
nullptr)
163 actualNode = apparentNode;
165 addNodeLink(text, actualNode, fullName);
169
170
171
172
179
180
181
182
188 for (
const auto &node : nodes) {
189 text << Atom(Atom::ListItemNumber, QString::number(++count));
190 text << Atom(Atom::ListItemLeft, QString(
"bullet"));
191 appendSignature(text, node);
192 text << Atom(Atom::ListItemRight, QString(
"bullet"));
200 QMap<QString, Text> classMap;
201 for (
const auto &relatedClass : rc) {
202 ClassNode *rcn = relatedClass.m_node;
203 if (rcn && rcn->isInAPI()) {
205 appendFullName(className, rcn, cn);
206 classMap[className.toString().toLower()] = className;
211 const QStringList classNames = classMap.keys();
212 for (
const auto &className : classNames) {
213 text << classMap[className];
214 text << TextUtils::comma(index++, classNames.size());
222 QMap<QString, Text> classMap;
224 QStringList typeNames(knownTypes);
225 for (
const auto sub : subs)
226 typeNames << sub->name();
228 for (
const auto sub : subs) {
230 appendFullName(full_name, sub, base);
232 if (typeNames.count(sub->name()) > 1)
233 full_name << Atom(Atom::String,
" (%1)"_L1.arg(sub->logicalModuleName()));
234 classMap[full_name.toString().toLower()] = full_name;
238 const auto &names = classMap.keys();
239 for (
const auto &name : names)
240 text << classMap[name] << TextUtils::comma(index++, names.size());
245
246
247
248
249
250
251
259 if (s_outFileNames.contains(fileName) && !node
->isAttribution() && !fileName.contains(
"-attribution-"_L1))
260 node
->location().warning(
"Already generated %1 for this project"_L1.arg(fileName));
262 QString path = outputDir() + QLatin1Char(
'/') + fileName;
265 auto outFile =
new QFile(outPath);
268 const QString warningText {
"Output file already exists, overwriting %1"_L1.arg(outFile->fileName())};
269 if (qEnvironmentVariableIsSet(
"QDOC_ALL_OVERWRITES_ARE_WARNINGS"))
272 qCDebug(lcQdoc) << qUtf8Printable(warningText);
275 if (!outFile->open(QFile::WriteOnly | QFile::Text)) {
277 QStringLiteral(
"Cannot open output file '%1'").arg(outFile->fileName()));
280 qCDebug(lcQdoc,
"Writing: %s", qPrintable(path));
281 s_outFileNames << fileName;
282 s_trademarks.clear();
287
288
289
290
293 QFile *outFile = openSubPageFile(
static_cast<
const PageNode*>(node), fileName);
294 auto *out =
new QTextStream(outFile);
295 outStreamStack.push(out);
299
300
301
302
305 outStreamStack.top()->flush();
306 delete outStreamStack.top()->device();
307 delete outStreamStack.pop();
316 return node->fileNameBase();
318 QString result = Utilities::computeFileBase(
320 [](
const Node *n) {
return outputPrefix(n); },
321 [](
const Node *n) {
return outputSuffix(n); });
323 const_cast<
Node *>(node)->setFileNameBase(result);
328
329
330
331
332
335 return Utilities::linkForExampleFile(path, s_project, fileExt.isEmpty() ? fileExtension() : fileExt);
339
340
341
344 return Utilities::exampleFileTitle(relative->files(), relative->images(), fileName);
348
349
350
351
352
355 if (!node->url().isEmpty())
362 QFileInfo originalName(node->name());
363 QString suffix = originalName.suffix();
364 if (!suffix.isEmpty() && suffix !=
"html") {
366 QString name = fileBase(node);
367 return name + QLatin1Char(
'.') + suffix;
371 QString name = fileBase(node) + QLatin1Char(
'.');
372 return name + (extension.isNull() ? fileExtension() : extension);
376
377
378
379
380
381
382
383
384QString
Generator::cleanRef(
const QString &ref,
bool xmlCompliant)
395 clean.reserve(ref.size() + 20);
396 const QChar c = ref[0];
397 const uint u = c.unicode();
399 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (!xmlCompliant && u >=
'0' && u <=
'9')) {
401 }
else if (xmlCompliant && u >=
'0' && u <=
'9') {
402 clean += QLatin1Char(
'A') + c;
403 }
else if (u ==
'~') {
405 }
else if (u ==
'_') {
406 clean +=
"underscore.";
408 clean += QLatin1Char(
'A');
411 for (
int i = 1; i < ref.size(); i++) {
412 const QChar c = ref[i];
413 const uint u = c.unicode();
414 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (u >=
'0' && u <=
'9') || u ==
'-'
415 || u ==
'_' || (xmlCompliant && u ==
':') || u ==
'.') {
417 }
else if (c.isSpace()) {
418 clean += QLatin1Char(
'-');
419 }
else if (u ==
'!') {
421 }
else if (u ==
'&') {
423 }
else if (u ==
'<') {
425 }
else if (u ==
'=') {
427 }
else if (u ==
'>') {
429 }
else if (u ==
'#') {
430 clean += QLatin1Char(
'#');
432 clean += QLatin1Char(
'-');
433 clean += QString::number(
static_cast<
int>(u), 16);
441 return s_fmtLeftMaps[format()];
446 return s_fmtRightMaps[format()];
450
451
456 if (!node->url().isEmpty())
464
465
466
467 if (!fileBase(node).isEmpty())
468 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
472 return fileBase(node) + QLatin1Char(
'.') + fileExtension();
474 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
475 }
else if (fileBase(node).isEmpty())
478 Node *parentNode =
nullptr;
482 if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
483 parentName = fullDocumentLocation(node->parent());
487 case NodeType::Class:
488 case NodeType::Struct:
489 case NodeType::Union:
490 case NodeType::Namespace:
491 case NodeType::Proxy:
492 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
495 const auto *fn =
static_cast<
const FunctionNode *>(node);
496 switch (fn->metaness()) {
498 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal";
501 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal-handler";
504 anchorRef = QLatin1Char(
'#') + node->name() +
"-method";
508 anchorRef =
"#dtor." + fn->name().mid(1);
509 else if (
const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty())
510 return fullDocumentLocation(p);
511 else if (fn->overloadNumber() > 0)
512 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name()) + QLatin1Char(
'-')
513 + QString::number(fn->overloadNumber());
515 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name());
521
522
523
524
527 anchorRef = QLatin1Char(
'#') + node->name() +
"-enum";
530 const auto *tdef =
static_cast<
const TypedefNode *>(node);
531 if (tdef->associatedEnum())
532 return fullDocumentLocation(tdef->associatedEnum());
535 anchorRef = QLatin1Char(
'#') + node->name() +
"-typedef";
538 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
546 anchorRef = QLatin1Char(
'#') + node->name() +
"-attached-prop";
548 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
551 anchorRef = QLatin1Char(
'#') + node->name() +
"-var";
559 parentName = fileBase(node);
560 parentName.replace(QLatin1Char(
'/'), QLatin1Char(
'-'))
561 .replace(QLatin1Char(
'.'), QLatin1Char(
'-'));
562 parentName += QLatin1Char(
'.') + fileExtension();
569 parentName.replace(u'.' + fileExtension(),
"-obsolete."_L1 + fileExtension());
571 return parentName.toLower() + anchorRef;
575
576
577
578
579
580
581
584 QList<Text> alsoList = node
->doc().alsoList();
585 supplementAlsoList(node, alsoList);
587 if (!alsoList.isEmpty()) {
594 for (
const auto &also : std::as_const(alsoList)) {
596 const Atom *atom = also.firstAtom();
597 QString link = atom->string();
598 if (!used.contains(link)) {
603 if (m_qdb->findNodeForAtom(atom, node, ref) == node && ref.isEmpty())
604 node->doc().location().warning(
"Redundant link to self in \\sa command for %1"_L1.arg(node->name()));
609 for (
const auto &also : std::as_const(items))
610 text << also << TextUtils::separator(i++, items.size());
618 bool generate,
int &numAtoms)
620 while (atom !=
nullptr) {
622 int numAtoms0 = numAtoms;
623 bool rightFormat = canHandleFormat(atom->string());
638 if (generate && numAtoms0 == numAtoms) {
639 relative
->location().warning(QStringLiteral(
"Output format %1 not handled %2")
640 .arg(format(), outFileName()));
641 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
651 n += generateAtom(atom, relative, marker);
663
664
665
668 const FunctionNode *fn = node->isFunction() ?
static_cast<
const FunctionNode *>(node) :
nullptr;
671
672
673
677 text <<
"Destroys the instance of ";
678 text << fn
->parent()->name() <<
".";
680 text <<
" The destructor is virtual.";
686 text <<
"Default-constructs an instance of "
693 text <<
"Copy-constructs an instance of "
700 text <<
"Move-constructs an instance of "
707 text <<
"Copy-assigns "
710 <<
" to this " << fn
->parent()->name() <<
" instance.";
716 text <<
"Move-assigns "
719 <<
" to this " << fn
->parent()->name() <<
" instance.";
724 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
728 QStringLiteral(
"No documentation for %1 '%2'")
733 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
737 QStringLiteral(
"No documentation for '%1'").arg(node->plainSignature()));
742 generateReimplementsClause(fn, marker);
744 if (
static_cast<
const PropertyNode *>(node)->propertyType() != PropertyNode::PropertyType::StandardProperty)
769 const auto *enume =
static_cast<
const EnumNode *>(node);
771 QSet<QString> definedItems;
772 const QList<EnumItem> &items = enume->items();
773 for (
const auto &item : items)
774 definedItems.insert(item.name());
776 const auto &documentedItemList = enume->doc().enumItemNames();
777 QSet<QString> documentedItems(documentedItemList.cbegin(), documentedItemList.cend());
778 const QSet<QString> allItems = definedItems + documentedItems;
779 if (allItems.size() > definedItems.size()
780 || allItems.size() > documentedItems.size()) {
781 for (
const auto &it : allItems) {
782 if (!definedItems.contains(it)) {
783 node->doc().location().warning(
784 QStringLiteral(
"No such enum item '%1' in %2")
785 .arg(it, node->plainFullName()),
786 QStringLiteral(
"Maybe you meant '%1'?")
787 .arg(suggestName(it, definedItems, documentedItems)));
788 }
else if (!documentedItems.contains(it)) {
789 node->doc().location().warning(
790 QStringLiteral(
"Undocumented enum item '%1' in %2")
791 .arg(it, node->plainFullName()));
804 const QSet<QString> requiredFunctionParams = fn
->parameters().getNames();
805 const QSet<QString> requiredTemplateParams = fn->templateDecl()
806 ? fn->templateDecl()->requiredParameterNamesForFunctions()
808 const QSet<QString> requiredNames = requiredFunctionParams + requiredTemplateParams;
812 const QSet<QString> ownTemplateParams = fn->templateDecl()
813 ? fn->templateDecl()->parameterNames()
815 const QSet<QString> allowedNames = requiredNames + ownTemplateParams
816 + inheritedTemplateParamNames(fn);
818 const QSet<QString> documentedNames = fn
->doc().parameterNames();
821 for (
const auto &name : requiredNames) {
822 if (!documentedNames.contains(name)) {
823 if (fn->isActive() || fn->isPreliminary()) {
826 if (!fn->isMarkedReimp() && !fn->isOverload()
827 && !(fn->isSomeCtor() && fn->hasOverloads())) {
829 const bool isTemplateParam = requiredTemplateParams.contains(name);
830 fn->doc().location().warning(
831 "Undocumented %1 '%2' in %3"_L1
832 .arg(isTemplateParam ?
"template parameter"_L1
834 name, node->plainFullName()));
840 warnAboutUnknownDocumentedParams(fn, documentedNames, allowedNames,
843
844
845
846
849 if (!fn
->doc().body().contains(
"return"))
851 QStringLiteral(
"Undocumented return value "
852 "(hint: use 'return' or 'returns' in the text"));
855 if (
auto *qpn =
static_cast<
const QmlPropertyNode *>(node); !qpn->validateDataType())
856 qpn->doc().location().warning(
"Invalid QML property type: %1"_L1.arg(qpn->dataType()));
862 const QSet<QString> requiredNames = node->templateDecl()->parameterNames();
863 const QSet<QString> allowedNames = requiredNames + inheritedTemplateParamNames(node);
864 const QSet<QString> documentedNames = node
->doc().parameterNames();
867 for (
const auto &name : requiredNames) {
868 if (!documentedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
869 node->doc().location().warning(
870 "Undocumented template parameter '%1' in %2"_L1
871 .arg(name, node->plainFullName()));
876 warnAboutUnknownDocumentedParams(node, documentedNames, allowedNames,
885
886
887
888
889
895 const auto *en =
static_cast<
const ExampleNode *>(node);
898 if (exampleUrl.isEmpty()) {
899 if (!en->noAutoList()) {
904 generateLinkToExample(en, marker, exampleUrl);
909
910
911
912
913
914
916 const QString &baseUrl)
918 QString exampleUrl(baseUrl);
920#ifndef QT_BOOTSTRAPPED
921 link = QUrl(exampleUrl).host();
925 link.prepend(
"Example project");
927 const QLatin1Char separator(
'/');
928 const QLatin1Char placeholder(
'\1');
929 if (!exampleUrl.contains(placeholder)) {
930 if (!exampleUrl.endsWith(separator))
931 exampleUrl += separator;
932 exampleUrl += placeholder;
939 pathRoot = metaTagMap->value(QLatin1String(
"installpath"));
940 if (pathRoot.isEmpty())
942 QStringList path = QStringList() << pathRoot << en->name();
943 path.removeAll(QString());
947 <<
Atom(
Atom::Link, exampleUrl.replace(placeholder, path.join(separator)))
957 const QString prefix(
"/images/used-in-examples");
963 s_exampleImageFileNames << prefix.mid(1) +
"/" + resolved_file.get_query();
966 OutputDirectory::ensure(s_outDir, en
->location());
968 outDir.ensureSubdir(prefix.mid(1), en
->location());
970 const QFileInfo fi{resolved_file.get_query()};
971 const QString relativePath = fi.path();
973 const bool hasSubdir = !relativePath.isEmpty() && relativePath !=
"."_L1;
975 hasSubdir ? imagesUsedInExamplesDir.ensureSubdir(relativePath, en
->location())
976 : imagesUsedInExamplesDir;
978 const QString fileName = fi.fileName();
994
995
996
997
998
999
1009 paths = en->images();
1013 paths = en->files();
1016 std::sort(paths.begin(), paths.end(), Generator::comparePaths);
1021 for (
const auto &path : std::as_const(paths)) {
1022 auto maybe_resolved_file{file_resolver.resolve(path)};
1023 if (!maybe_resolved_file) {
1025 QString details = std::transform_reduce(
1026 file_resolver.get_search_directories().cbegin(),
1027 file_resolver.get_search_directories().cend(),
1028 u"Searched directories:"_s,
1030 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
1033 en->location().warning(u"(Generator)Cannot find file to quote from: %1"_s.arg(path), details);
1038 const auto &file{*maybe_resolved_file};
1040 addImageToCopy(en, file);
1042 generateExampleFilePage(en, file, marker);
1045 text << Atom(Atom::ListItemNumber, openedList.numberString())
1046 << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
1047 << Atom(atomType, file.get_query()) << Atom(Atom::FormattingLeft,
ATOM_FORMATTING_LINK) << file.get_query()
1049 << Atom(Atom::ListItemRight, openedList.styleString());
1052 if (!paths.isEmpty())
1053 generateText(text, en, marker);
1057
1058
1061 if (!node->url().isNull())
1065 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1073
1074
1078 PageNode *pageNode =
static_cast<PageNode *>(node);
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099 auto *cn =
static_cast<CollectionNode *>(node);
1100 if (cn->wasSeen()) {
1101 m_qdb->mergeCollections(cn);
1102 beginSubPage(pageNode, fileName(node));
1105 }
else if (cn->isGenericCollection()) {
1109 QString name = cn->name().toLower();
1110 name.replace(QChar(
' '), QString(
"-"));
1112 cn->tree()->physicalModuleName() +
"-" + name +
"." + fileExtension();
1113 beginSubPage(pageNode, filename);
1118 beginSubPage(pageNode, fileName(node));
1124 beginSubPage(pageNode, fileName(node));
1128 beginSubPage(pageNode, fileName(node));
1129 auto *qcn =
static_cast<QmlTypeNode *>(node);
1133 beginSubPage(pageNode, fileName(node));
1141 auto *aggregate =
static_cast<Aggregate *>(node);
1142 const NodeList &children = aggregate->childNodes();
1143 for (
auto *child : children) {
1144 if (child->isPageNode()) {
1145 generateDocumentation(child);
1146 }
else if (!node->parent() && child->isInAPI() && !child->isRelatedNonmember()
1147 && !child->doc().isAutoGenerated()) {
1149 child->location().warning(u"No documentation generated for %1 '%2' in global scope."_s
1150 .arg(NodeText::nodeTypeDescription(child), child->name()),
1151 u"Maybe you forgot to use the '\\relates' command?"_s);
1152 child->setStatus(Status::DontDocument);
1153 }
else if (child->isQmlModule() && !child->wasSeen()) {
1155 auto *qmlModule =
static_cast<CollectionNode *>(child);
1156 for (
const auto *member : qmlModule->members()) {
1157 member->location().warning(
1158 u"Undocumented QML module '%1' referred by type '%2' or its members"_s
1159 .arg(qmlModule->name(), member->name()),
1160 u"Maybe you forgot to document '\\qmlmodule %1'?"_s
1161 .arg(qmlModule->name()));
1163 }
else if (child->isQmlType() && !child->hasDoc()) {
1165 auto *qmlType =
static_cast<QmlTypeNode *>(child);
1166 if (
auto qmid = qmlType->logicalModuleName(); !qmid.isEmpty())
1167 qmlType->location().warning(u"No such type '%1' in QML module '%2'"_s
1168 .arg(qmlType->name(), qmid));
1180 const FunctionNode *overrides = cn->findOverriddenFunction(fn);
1186 overrides->parent()->name()
1187 +
"::" + overrides->signature(Node::SignaturePlain);
1190 generateText(text, fn, marker);
1192 fn
->doc().location().warning(
1193 QStringLiteral(
"Illegal \\reimp; no documented virtual function for %1")
1194 .arg(overrides->plainSignature()));
1198 const PropertyNode *sameName = cn->findOverriddenProperty(fn);
1199 if (sameName && sameName
->hasDoc()) {
1201 text <<
Atom::ParaLeft <<
"Reimplements an access function for property: ";
1202 QString fullName = sameName->parent()->name() +
"::" + sameName->name();
1205 generateText(text, fn, marker);
1211 QStringList since = node->since().split(QLatin1Char(
' '));
1214 if (since.size() == 1) {
1216 return productName.isEmpty() ? node->since() : productName +
" " + since[0];
1220 return node->since();
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1246 status = metaMap->value(
"status");
1247 if (!status.isEmpty())
1250 const auto &since = node->deprecatedSince();
1252 status = u"Deprecated"_s;
1253 if (!since.isEmpty())
1254 status +=
" since %1"_L1.arg(since);
1255 }
else if (!since.isEmpty()) {
1256 status =
"Until %1"_L1.arg(since);
1260 status = collection->state();
1263 return status.isEmpty() ?
std::nullopt :
std::optional(status);
1268 if (node->since().isEmpty())
1271 const auto *shared = node->isSharedCommentNode()
1272 ?
static_cast<
const SharedCommentNode *>(node) :
nullptr;
1273 const bool plural = shared && shared->collective().size() > 1;
1274 const Node *described = plural ? shared->collective().first() : node;
1279 text << Atom::ParaLeft
1280 << (plural ?
"These "_L1 :
"This "_L1)
1281 << NodeText::nodeTypeDescription(described, number)
1282 << (plural ?
" were "_L1 :
" was "_L1)
1283 <<
"introduced in " << formatSince(node) <<
"." << Atom::ParaRight;
1290 std::vector<
const Node *> nodes;
1292 const auto &collective =
static_cast<
const SharedCommentNode *>(node)->collective();
1293 nodes.assign(collective.begin(), collective.end());
1295 nodes.push_back(node);
1298 std::size_t position = 0;
1299 for (
const Node *member : nodes) {
1301 if (!member->isFunction(Genus::CPP))
1304 const auto &noexceptExpr =
static_cast<
const FunctionNode *>(member)->getNoexcept();
1305 if (!noexceptExpr || noexceptExpr->isEmpty())
1309 if (nodes.size() > 1)
1310 subject =
" ("_L1 + QString::number(position) +
")"_L1;
1312 subject =
"This "_L1 + NodeText::nodeTypeDescription(member);
1315 text << Atom::NoteLeft << subject
1316 <<
" is noexcept when "
1317 << Atom(Atom::C, marker->markedUpCode(*noexceptExpr,
nullptr, Location()))
1318 <<
" is " << Atom(Atom::C,
"true") <<
"." << Atom::NoteRight;
1319 generateText(text, member, marker);
1329 const auto *shared = node->isSharedCommentNode()
1330 ?
static_cast<
const SharedCommentNode *>(node) :
nullptr;
1331 const bool plural = shared && !shared->isPropertyGroup() && shared->collective().size() > 1;
1332 const QString subject = plural
1333 ?
"These "_L1 + NodeText::nodeTypeDescription(shared->collective().first(), NodeText::GrammaticalNumber::Plural) +
" are"_L1
1334 :
"This "_L1 + NodeText::nodeTypeDescription(node) +
" is"_L1;
1340 const QString &state =
static_cast<
const CollectionNode*>(node)->state();
1341 if (!state.isEmpty()) {
1349 if (
const auto &version = node->deprecatedSince(); !version.isEmpty()) {
1351 <<
" scheduled for deprecation in version "
1356 auto description = Config::instance()
1368 text << subject <<
" deprecated";
1369 if (
const QString &version = node->deprecatedSince(); !version.isEmpty()) {
1371 if (node
->isQmlNode() && !node->logicalModuleName().isEmpty())
1372 text << node->logicalModuleName() <<
" ";
1376 text <<
". We strongly advise against using " << (plural ?
"them" :
"it")
1386 <<
"Part of developer documentation for internal use."
1396
1397
1398
1402 Q_ASSERT(node && !node->name().isEmpty());
1404 text << Atom(Atom::DivLeft,
1405 "class=\"admonition %1\""_L1.arg(prefix == AdmonitionPrefix::Note ? u"note"_s : u"auto"_s));
1420 text <<
"This function can be invoked via the meta-object system and from QML. See "
1426 text <<
"This is a private signal. It can be used in signal connections "
1427 "but cannot be emitted by the user.";
1431 QString handler(node->name());
1432 qsizetype prefixLocation = handler.lastIndexOf(
'.', -2) + 1;
1433 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
1434 handler.insert(prefixLocation, QLatin1String(
"on"));
1435 text <<
"The corresponding handler is "
1444 const auto *fn =
static_cast<
const FunctionNode *>(node);
1445 auto nodes = fn->associatedProperties();
1446 if (nodes.isEmpty())
1451 QMap<PropertyNode::FunctionRole, QList<
const PropertyNode *>> roleGroups;
1452 for (
const auto *n : std::as_const(nodes)) {
1453 const auto *pn =
static_cast<
const PropertyNode *>(n);
1454 if (pn->isInAPI()) {
1455 PropertyNode::FunctionRole role = pn->role(fn);
1456 roleGroups[role].append(pn);
1460 if (roleGroups.isEmpty())
1471 for (
auto role : roleOrder) {
1472 const auto it = roleGroups.constFind(role);
1473 if (it == roleGroups.cend())
1476 const auto &properties = it.value();
1480 case PropertyNode::FunctionRole::Getter:
1481 msg = u"Getter function"_s;
1483 case PropertyNode::FunctionRole::Setter:
1484 msg = u"Setter function"_s;
1486 case PropertyNode::FunctionRole::Resetter:
1487 msg = u"Resetter function"_s;
1489 case PropertyNode::FunctionRole::Notifier:
1490 msg = u"Notifier signal"_s;
1492 case PropertyNode::FunctionRole::Bindable:
1493 msg = u"Bindable function"_s;
1499 if (properties.size() == 1) {
1500 const auto *pn = properties.first();
1501 text << msg << u" for property "_s << Atom(Atom::Link, pn->name())
1505 text << msg << u" for properties "_s;
1506 for (qsizetype i = 0; i < properties.size(); ++i) {
1507 const auto *pn = properties.at(i);
1511 << TextUtils::separator(i, properties.size());
1520 text <<
"This property supports "
1524 text <<
" bindings.";
1529 const auto *func =
static_cast<
const FunctionNode *>(node);
1532 if (func->isPrimaryOverload())
1535 if (func->isSignal() || func->isSlot()) {
1536 QString functionType = func->isSignal() ?
"signal" :
"slot";
1537 const QString &configKey = func->isSignal() ?
"overloadedsignalstarget" :
"overloadedslotstarget";
1538 const QString &defaultTarget = func->isSignal() ?
"connecting-overloaded-signals" :
"connecting-overloaded-slots";
1539 const QString &linkTarget = Config::instance().get(configKey).asString(defaultTarget);
1541 text <<
"This " << functionType <<
" is overloaded. ";
1543 QString snippet = generateOverloadSnippet(func);
1544 if (!snippet.isEmpty()) {
1545 text <<
"To connect to this " << functionType <<
":\n\n"
1549 if (!linkTarget.isEmpty()) {
1550 text <<
"For more examples and approaches, see "
1553 <<
"connecting to overloaded " << functionType <<
"s"
1557 const auto &args = node
->doc().overloadList();
1558 if (args.first().first.isEmpty()) {
1559 text <<
"This is an overloaded function.";
1561 QString target = args.first().first;
1564 if (!target.contains(
"::")) {
1567 target = parent->name() +
"::" + target;
1584
1585
1586
1587
1590 bool result =
false;
1601
1602
1603
1604
1605
1606
1607
1608
1612 bool result =
false;
1614 const NodeList &children =
static_cast<
const Aggregate *>(node)->childNodes();
1615 for (
auto child : children) {
1616 if (!child->isDeprecated()) {
1617 switch (child->threadSafeness()) {
1618 case Node::Reentrant:
1619 reentrant.append(child);
1620 if (ts == Node::ThreadSafe)
1623 case Node::ThreadSafe:
1624 threadsafe.append(child);
1625 if (ts == Node::Reentrant)
1628 case Node::NonReentrant:
1629 nonreentrant.append(child);
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1661 if (atom->count() > 1) {
1662 if (s_trademarks.contains(atom->string(1)))
1664 s_trademarks << atom->string(1);
1677
1678
1679
1682 Text text, rlink, tlink;
1687 bool exceptions =
false;
1707 exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
1717 text <<
" with the following exceptions:";
1736 if (!nonreentrant.isEmpty()) {
1738 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1739 signatureList(nonreentrant, node, marker);
1741 if (!threadsafe.isEmpty()) {
1744 text <<
"These functions are also " << tlink <<
":" <<
Atom::ParaRight;
1746 signatureList(threadsafe, node, marker);
1749 if (!reentrant.isEmpty()) {
1751 text <<
"These functions are only " << rlink <<
":" <<
Atom::ParaRight;
1752 signatureList(reentrant, node, marker);
1754 if (!nonreentrant.isEmpty()) {
1757 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1758 signatureList(nonreentrant, node, marker);
1765
1766
1767
1768
1769
1773 if (category == ComparisonCategory::None)
1777 text << Atom::ParaLeft <<
"%1 is "_L1.arg(node->plainFullName())
1779 << QString::fromStdString(comparisonCategoryAsString(category))
1780 << ((category == ComparisonCategory::Equality) ?
"-"_L1 :
"ly "_L1)
1781 << Atom(Atom::String,
"comparable"_L1)
1783 <<
"."_L1 << Atom::ParaRight;
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1807 const auto *map = node
->doc().comparesWithMap();
1809 const bool hasSelfComparison = (selfCategory != ComparisonCategory::None);
1810 const bool hasComparesWithEntries = (map && !map->isEmpty());
1812 if (!hasSelfComparison && !hasComparesWithEntries)
1815 bool hasDescriptions =
false;
1816 if (hasComparesWithEntries) {
1817 for (
const auto &description : *map) {
1818 if (description.firstAtom()->next() != description.lastAtom()) {
1819 hasDescriptions =
true;
1827 text << Atom::ParaLeft
1829 <<
"%1 Comparisons"_L1.arg(node->plainFullName())
1833 text << Atom(Atom::TableLeft,
"generic"_L1);
1835 text << Atom::TableHeaderLeft
1836 << Atom::TableItemLeft <<
"Category"_L1 << Atom::TableItemRight
1837 << Atom::TableItemLeft <<
"Comparable Types"_L1 << Atom::TableItemRight;
1838 if (hasDescriptions)
1839 text << Atom::TableItemLeft <<
"Description"_L1 << Atom::TableItemRight;
1843 if (hasSelfComparison) {
1844 const QString &category = QString::fromStdString(comparisonCategoryAsString(selfCategory));
1850 if (hasDescriptions)
1856 if (hasComparesWithEntries) {
1857 for (
auto [key, description] : map->asKeyValueRange()) {
1858 const QString &category = QString::fromStdString(comparisonCategoryAsString(key));
1860 text << Atom::TableRowLeft;
1862 text << Atom::TableItemLeft << category << Atom::TableItemRight;
1864 text << Atom::TableItemLeft;
1865 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
1866 for (
const auto &name : types)
1867 text << Atom(Atom::AutoLink, name)
1868 << TextUtils::separator(types.indexOf(name), types.size());
1869 text << Atom::TableItemRight;
1871 if (hasDescriptions) {
1872 text << Atom::TableItemLeft;
1873 if (description.firstAtom()->next() != description.lastAtom())
1874 text << Text::subText(description.firstAtom()->next(), description.lastAtom());
1875 text << Atom::TableItemRight;
1878 text << Atom::TableRowRight;
1889
1890
1893 s_currentGenerator =
this;
1905 if (
auto *gen =
dynamic_cast<
Generator *>(producer))
1911 for (
const auto &generator : std::as_const(s_generators)) {
1912 if (generator->format() == format)
1927 while (i < markedCode.size()) {
1928 if (markedCode.at(i) == QLatin1Char(
'\n')) {
1932 for (
int j = 0; j < level; j++)
1933 t += QLatin1Char(
' ');
1937 t += markedCode.at(i++);
1944 Config &config = Config::instance();
1945 s_outputFormats = config.getOutputFormats();
1948 for (
auto &g : s_generators) {
1949 if (s_outputFormats.contains(g->format())) {
1950 s_currentGenerator = g;
1951 OutputProducerRegistry::instance().registerProducer(g);
1952 g->initializeGenerator();
1957 for (
const auto &n : configFormatting) {
1959 const auto &formattingDotNames = config.subVars(formattingDotName);
1960 for (
const auto &f : formattingDotNames) {
1961 const auto &configVar = config.get(formattingDotName + Config::dot + f);
1962 QString def{configVar.asString()};
1963 if (!def.isEmpty()) {
1964 int numParams = Config::numParams(def);
1965 int numOccs = def.count(
"\1");
1966 if (numParams != 1) {
1967 configVar.location().warning(QStringLiteral(
"Formatting '%1' must "
1969 "parameter (found %2)")
1970 .arg(n, numParams));
1971 }
else if (numOccs > 1) {
1972 configVar.location().fatal(QStringLiteral(
"Formatting '%1' must "
1973 "contain exactly one "
1974 "occurrence of '\\1' "
1978 int paramPos = def.indexOf(
"\1");
1979 s_fmtLeftMaps[f].insert(n, def.left(paramPos));
1980 s_fmtRightMaps[f].insert(n, def.mid(paramPos + 1));
1987 s_outDir = config.getOutputDir();
1988 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
1990 s_outputPrefixes.clear();
1992 if (!items.isEmpty()) {
1993 for (
const auto &prefix : items)
1994 s_outputPrefixes[prefix] =
1997 if (!items.contains(u"QML"_s))
1998 s_outputPrefixes[u"QML"_s] = u"qml-"_s;
2000 s_outputSuffixes.clear();
2003 + Config::dot + suffix).asString();
2010
2011
2012
2013void Generator::copyTemplateFiles(
const QString &configVar,
const QString &subDir)
2030 Config &config = Config::instance();
2031 QStringList files = config.getCanonicalPathList(configVar,
Config::Validate);
2032 const auto &loc = config.get(configVar)
.location();
2033 if (!files.isEmpty()) {
2043 OutputDirectory::ensure(s_outDir, loc);
2046 outDir.ensureSubdir(subDir, loc);
2048 for (
const auto &file : files) {
2049 if (!file.isEmpty()) {
2050 const QFileInfo fi(file);
2051 Config::copyFile(loc, fi.absoluteFilePath(), fi.fileName(), templateDir.path());
2058
2059
2060
2061
2064 Config &config = Config::instance();
2065 s_outFileNames.clear();
2066 s_exampleImageFileNames.clear();
2067 s_useOutputSubdirs =
true;
2068 if (config.get(format() + Config::dot +
"nosubdirs").asBool())
2071 if (s_outputFormats.isEmpty())
2076 s_outDir = config.getOutputDir(format());
2077 if (s_outDir.isEmpty()) {
2078 Location().fatal(QStringLiteral(
"No output directory specified in "
2079 "configuration file or on the command line"));
2081 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
2086 OutputDirectory::ensure(s_outDir,
Location());
2090 if (!outputDir.toQDir().isEmpty())
2091 Location().error(
"Output directory '%1' exists but is not empty"_L1.arg(s_outDir));
2101 outputDir.ensureSubdir(imagesDir,
Location());
2102 s_imagesOutDir =
std::move(imagesDir);
2105 copyTemplateFiles(format() + Config::dot +
CONFIG_SCRIPTS,
"scripts");
2116
2117
2118
2130
2131
2132
2133
2136 return *outStreamStack.top();
2141 return QFileInfo(
static_cast<QFile *>(
out().device())->fileName()).fileName();
2150 return s_outputPrefixes[u"QML"_s];
2152 return s_outputPrefixes[u"CPP"_s];
2165 return s_outputSuffixes[u"QML"_s];
2167 return s_outputSuffixes[u"CPP"_s];
2177 QStringView *contents, QStringView *par1)
2180 if (i >= n || src[i] != c)
2185 while (i < n && src[i] == ' ')
2195 if (tag != QStringView(src).mid(i, tag.size())) {
2207 while (i < n && src[i].isLetter())
2209 if (src[i] ==
'=') {
2214 while (i < n && src[i] !=
'"')
2216 *par1 = QStringView(src).mid(j, i - j);
2227 if (i + 4 + tag.size() > n)
2231 if (src[i + 1] !=
'/')
2233 if (src[i + 2] !=
'@')
2235 if (tag != QStringView(src).mid(i + 3, tag.size()))
2237 if (src[i + 3 + tag.size()] !=
'>')
2242 *contents = QStringView(src).mid(j, i - j);
2244 i += tag.size() + 4;
2254 QString t = markedCode;
2255 t.replace(tag, QString());
2256 t.replace(quot, QLatin1String(
"\""));
2257 t.replace(gt, QLatin1String(
">"));
2258 t.replace(lt, QLatin1String(
"<"));
2259 t.replace(amp, QLatin1String(
"&"));
2267 while (atom && atom
->type() != type) {
2275
2276
2286 m_sectionNumber.clear();
2292 const auto fn =
static_cast<
const FunctionNode *>(node);
2293 if (fn->overloadNumber() == 0) {
2294 QString alternateName;
2297 if (fn->name().startsWith(
"set") && fn->name().size() >= 4) {
2298 alternateName = fn->name()[3].toLower();
2299 alternateName += fn->name().mid(4);
2300 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2302 if (!alternateFunc) {
2303 alternateName =
"is" + fn->name().mid(3);
2304 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2305 if (!alternateFunc) {
2306 alternateName =
"has" + fn->name().mid(3);
2307 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2310 }
else if (!fn->name().isEmpty()) {
2311 alternateName =
"set";
2312 alternateName += fn->name()[0].toUpper();
2313 alternateName += fn->name().mid(1);
2314 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2317 if (alternateFunc && alternateFunc
->access() != Access::Private) {
2319 for (i = 0; i < alsoList.size(); ++i) {
2320 if (alsoList.at(i).toString().contains(alternateName))
2324 if (i == alsoList.size()) {
2327 alternateName +=
"()";
2333 alsoList.prepend(also);
2353 const auto *start{body.firstAtom()};
2359 text << body.subText(text
.isEmpty() ? start : start->next(), end);
2374 for (
const auto &generator : std::as_const(s_generators)) {
2375 if (s_outputFormats.contains(generator->format())) {
2376 OutputProducerRegistry::instance().unregisterProducer(generator);
2377 generator->terminateGenerator();
2404 s_generators.clear();
2406 s_fmtLeftMaps.clear();
2407 s_fmtRightMaps.clear();
2409 s_imagesOutDir.clear();
2415
2416
2417
2418QString
Generator::trimmedTrailing(
const QString &string,
const QString &prefix,
2419 const QString &suffix)
2421 QString trimmed = string;
2422 while (trimmed.size() > 0 && trimmed[trimmed.size() - 1].isSpace())
2423 trimmed.truncate(trimmed.size() - 1);
2425 trimmed.append(suffix);
2426 trimmed.prepend(prefix);
2432 Location::internalError(QStringLiteral(
"unknown atom type '%1' in %2 generator")
2433 .arg(atom->typeString(), format()));
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2470 if (!cn || (cn->cmakeComponent().isEmpty() && cn->cmakePackage().isEmpty())) {
2474 const QString package =
2475 cn->cmakePackage().isEmpty() ?
"Qt" + QString::number(QT_VERSION_MAJOR) : cn->cmakePackage();
2477 QString findPackageText;
2478 if (cn->cmakeComponent().isEmpty()) {
2479 findPackageText =
"find_package(" + package +
" REQUIRED)";
2481 findPackageText =
"find_package(" + package +
" REQUIRED COMPONENTS " + cn->cmakeComponent() +
")";
2485 if (cn->cmakeTargetItem().isEmpty()) {
2486 if (cn->cmakeComponent().isEmpty()) {
2487 targetText = package +
"::" + package;
2489 targetText = package +
"::" + cn->cmakeComponent();
2492 targetText = cn->cmakeTargetItem();
2495 const QString targetLinkLibrariesText =
"target_link_libraries(mytarget PRIVATE " + targetText +
")";
2496 const QStringList cmakeInfo { findPackageText, targetLinkLibrariesText };
2498 return std::make_pair(findPackageText, targetLinkLibrariesText);
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511void Generator::addNodeLink(
Text &text,
const QString &nodeRef,
const QString &linkText) {
2519
2520
2521
2522
2523
2524
2525
2526
2530 Utilities::stringForNode(node),
2531 linkText.isEmpty() ? node->name() : linkText
2536
2537
2538
2539
2540
2541
2542
2548 QString className = func->parent()->name();
2549 QString functionName = func->name();
2550 QString typeList = func->parameters().generateTypeList();
2551 QString typeAndNameList = func->parameters().generateTypeAndNameList();
2552 QString nameList = func->parameters().generateNameList();
2553 QString objectName = generateObjectName(className);
2559 "// Connect using qOverload:\n"
2560 "connect(%1, qOverload<%2>(&%3::%4),\n"
2561 " receiver, &ReceiverClass::slot);\n\n"
2562 "// Or using a lambda:\n"
2563 "connect(%1, qOverload<%2>(&%3::%4),\n"
2564 " this, [](%5) { /* handle %4 */ });")
2565 .arg(objectName, typeList, className, functionName, typeAndNameList);
2568 "// Connect using qOverload:\n"
2569 "connect(sender, &SenderClass::signal,\n"
2570 " %1, qOverload<%2>(&%3::%4));\n\n"
2571 "// Or using a lambda as wrapper:\n"
2572 "connect(sender, &SenderClass::signal,\n"
2573 " %1, [receiver = %1](%5) { receiver->%4(%6); });")
2574 .arg(objectName, typeList, className, functionName, typeAndNameList, nameList);
2581
2582
2583
2586 QString name = className;
2588 if (name.startsWith(
'Q') && name.length() > 1)
2591 if (!name.isEmpty())
2592 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
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.
Provides human-readable descriptions of QDoc nodes.
QString nodeTypeDescription(const Node *node, GrammaticalNumber number=GrammaticalNumber::Singular)
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, that is, the aggregate under which the node is documented.
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.