36#include <QtCore/qdebug.h>
37#include <QtCore/qdir.h>
38#include <QtCore/qregularexpression.h>
40#ifndef QT_BOOTSTRAPPED
41# include "QtCore/qurl.h"
47using namespace std::literals::string_literals;
51using namespace Qt::StringLiterals;
54QMap<QString, QMap<QString, QString>>
Generator::s_fmtLeftMaps;
55QMap<QString, QMap<QString, QString>>
Generator::s_fmtRightMaps;
56QList<Generator *>
Generator::s_generators;
63QHash<QString, QString>
Generator::s_outputPrefixes;
64QHash<QString, QString>
Generator::s_outputSuffixes;
72static QRegularExpression
tag(
"</?@[^>]*>");
79
80
81
82
83
89 names.unite(p->templateDecl()->parameterNames());
95
96
97
98
99
100
101
102
106
107
108
110 const QSet<QString> &documentedNames,
111 const QSet<QString> &allowedNames,
114 for (
const auto &name : documentedNames) {
115 if (!allowedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
116 const auto message = (context == ValidationContext::TemplateDoc)
117 ?
"No such template parameter '%1' in %2"_L1
118 :
"No such parameter '%1' in %2"_L1;
119 node->doc().location().warning(message.arg(name, node->plainFullName()),
120 suggestName(name, allowedNames));
126
127
128
129
130
135 s_generators.prepend(
this);
139
140
141
144 s_generators.removeAll(
this);
148 const Node *actualNode)
150 if (actualNode ==
nullptr)
151 actualNode = apparentNode;
153 addNodeLink(text, actualNode, apparentNode->plainFullName(relative));
157 const Node *actualNode)
159 if (actualNode ==
nullptr)
160 actualNode = apparentNode;
162 addNodeLink(text, actualNode, fullName);
166
167
168
169
176
177
178
179
185 for (
const auto &node : nodes) {
186 text << Atom(Atom::ListItemNumber, QString::number(++count));
187 text << Atom(Atom::ListItemLeft, QString(
"bullet"));
188 appendSignature(text, node);
189 text << Atom(Atom::ListItemRight, QString(
"bullet"));
197 QMap<QString, Text> classMap;
198 for (
const auto &relatedClass : rc) {
199 ClassNode *rcn = relatedClass.m_node;
200 if (rcn && rcn->isInAPI()) {
202 appendFullName(className, rcn, cn);
203 classMap[className.toString().toLower()] = className;
208 const QStringList classNames = classMap.keys();
209 for (
const auto &className : classNames) {
210 text << classMap[className];
211 text << Utilities::comma(index++, classNames.size());
219 QMap<QString, Text> classMap;
221 QStringList typeNames(knownTypes);
222 for (
const auto sub : subs)
223 typeNames << sub->name();
225 for (
const auto sub : subs) {
227 appendFullName(full_name, sub, base);
229 if (typeNames.count(sub->name()) > 1)
230 full_name << Atom(Atom::String,
" (%1)"_L1.arg(sub->logicalModuleName()));
231 classMap[full_name.toString().toLower()] = full_name;
235 const auto &names = classMap.keys();
236 for (
const auto &name : names)
237 text << classMap[name] << Utilities::comma(index++, names.size());
242
243
244
245
246
247
248
256 if (s_outFileNames.contains(fileName) && !node
->isAttribution() && !fileName.contains(
"-attribution-"_L1))
257 node
->location().warning(
"Already generated %1 for this project"_L1.arg(fileName));
259 QString path = outputDir() + QLatin1Char(
'/') + fileName;
262 auto outFile =
new QFile(outPath);
265 const QString warningText {
"Output file already exists, overwriting %1"_L1.arg(outFile->fileName())};
266 if (qEnvironmentVariableIsSet(
"QDOC_ALL_OVERWRITES_ARE_WARNINGS"))
269 qCDebug(lcQdoc) << qUtf8Printable(warningText);
272 if (!outFile->open(QFile::WriteOnly | QFile::Text)) {
274 QStringLiteral(
"Cannot open output file '%1'").arg(outFile->fileName()));
277 qCDebug(lcQdoc,
"Writing: %s", qPrintable(path));
278 s_outFileNames << fileName;
279 s_trademarks.clear();
284
285
286
287
291 QFile *outFile = openSubPageFile(
static_cast<
const PageNode*>(node), fileName);
292 auto *out =
new QTextStream(outFile);
293 outStreamStack.push(out);
297
298
299
300
303 outStreamStack.top()->flush();
304 delete outStreamStack.top()->device();
305 delete outStreamStack.pop();
314 return node->fileNameBase();
316 QString result = Utilities::computeFileBase(
318 [](
const Node *n) {
return outputPrefix(n); },
319 [](
const Node *n) {
return outputSuffix(n); });
321 const_cast<
Node *>(node)->setFileNameBase(result);
326
327
328
329
330
333 return Utilities::linkForExampleFile(path, s_project, fileExt.isEmpty() ? fileExtension() : fileExt);
337
338
339
342 return Utilities::exampleFileTitle(relative->files(), relative->images(), fileName);
346
347
348
349
350
353 if (!node->url().isEmpty())
360 QFileInfo originalName(node->name());
361 QString suffix = originalName.suffix();
362 if (!suffix.isEmpty() && suffix !=
"html") {
364 QString name = fileBase(node);
365 return name + QLatin1Char(
'.') + suffix;
369 QString name = fileBase(node) + QLatin1Char(
'.');
370 return name + (extension.isNull() ? fileExtension() : extension);
374
375
376
377
378
379
380
381
382QString
Generator::cleanRef(
const QString &ref,
bool xmlCompliant)
393 clean.reserve(ref.size() + 20);
394 const QChar c = ref[0];
395 const uint u = c.unicode();
397 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (!xmlCompliant && u >=
'0' && u <=
'9')) {
399 }
else if (xmlCompliant && u >=
'0' && u <=
'9') {
400 clean += QLatin1Char(
'A') + c;
401 }
else if (u ==
'~') {
403 }
else if (u ==
'_') {
404 clean +=
"underscore.";
406 clean += QLatin1Char(
'A');
409 for (
int i = 1; i < ref.size(); i++) {
410 const QChar c = ref[i];
411 const uint u = c.unicode();
412 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (u >=
'0' && u <=
'9') || u ==
'-'
413 || u ==
'_' || (xmlCompliant && u ==
':') || u ==
'.') {
415 }
else if (c.isSpace()) {
416 clean += QLatin1Char(
'-');
417 }
else if (u ==
'!') {
419 }
else if (u ==
'&') {
421 }
else if (u ==
'<') {
423 }
else if (u ==
'=') {
425 }
else if (u ==
'>') {
427 }
else if (u ==
'#') {
428 clean += QLatin1Char(
'#');
430 clean += QLatin1Char(
'-');
431 clean += QString::number(
static_cast<
int>(u), 16);
439 return s_fmtLeftMaps[format()];
444 return s_fmtRightMaps[format()];
448
449
454 if (!node->url().isEmpty())
462
463
464
465 if (!fileBase(node).isEmpty())
466 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
470 return fileBase(node) + QLatin1Char(
'.') + fileExtension();
472 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
473 }
else if (fileBase(node).isEmpty())
476 Node *parentNode =
nullptr;
480 if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
481 parentName = fullDocumentLocation(node->parent());
485 case NodeType::Class:
486 case NodeType::Struct:
487 case NodeType::Union:
488 case NodeType::Namespace:
489 case NodeType::Proxy:
490 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
493 const auto *fn =
static_cast<
const FunctionNode *>(node);
494 switch (fn->metaness()) {
496 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal";
499 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal-handler";
502 anchorRef = QLatin1Char(
'#') + node->name() +
"-method";
506 anchorRef =
"#dtor." + fn->name().mid(1);
507 else if (
const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty())
508 return fullDocumentLocation(p);
509 else if (fn->overloadNumber() > 0)
510 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name()) + QLatin1Char(
'-')
511 + QString::number(fn->overloadNumber());
513 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name());
519
520
521
522
525 anchorRef = QLatin1Char(
'#') + node->name() +
"-enum";
528 const auto *tdef =
static_cast<
const TypedefNode *>(node);
529 if (tdef->associatedEnum())
530 return fullDocumentLocation(tdef->associatedEnum());
533 anchorRef = QLatin1Char(
'#') + node->name() +
"-typedef";
536 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
544 anchorRef = QLatin1Char(
'#') + node->name() +
"-attached-prop";
546 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
549 anchorRef = QLatin1Char(
'#') + node->name() +
"-var";
557 parentName = fileBase(node);
558 parentName.replace(QLatin1Char(
'/'), QLatin1Char(
'-'))
559 .replace(QLatin1Char(
'.'), QLatin1Char(
'-'));
560 parentName += QLatin1Char(
'.') + fileExtension();
568 parentName.replace(QLatin1Char(
'.') + fileExtension(),
569 "-obsolete." + fileExtension());
572 return parentName.toLower() + anchorRef;
576
577
578
579
580
581
582
585 QList<Text> alsoList = node
->doc().alsoList();
586 supplementAlsoList(node, alsoList);
588 if (!alsoList.isEmpty()) {
595 for (
const auto &also : std::as_const(alsoList)) {
597 const Atom *atom = also.firstAtom();
598 QString link = atom->string();
599 if (!used.contains(link)) {
604 if (m_qdb->findNodeForAtom(atom, node, ref) == node && ref.isEmpty())
605 node->doc().location().warning(
"Redundant link to self in \\sa command for %1"_L1.arg(node->name()));
610 for (
const auto &also : std::as_const(items))
611 text << also << Utilities::separator(i++, items.size());
619 bool generate,
int &numAtoms)
621 while (atom !=
nullptr) {
623 int numAtoms0 = numAtoms;
624 bool rightFormat = canHandleFormat(atom->string());
639 if (generate && numAtoms0 == numAtoms) {
640 relative
->location().warning(QStringLiteral(
"Output format %1 not handled %2")
641 .arg(format(), outFileName()));
642 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
652 n += generateAtom(atom, relative, marker);
664
665
666
669 const FunctionNode *fn = node->isFunction() ?
static_cast<
const FunctionNode *>(node) :
nullptr;
672
673
674
678 text <<
"Destroys the instance of ";
679 text << fn
->parent()->name() <<
".";
681 text <<
" The destructor is virtual.";
687 text <<
"Default-constructs an instance of "
694 text <<
"Copy-constructs an instance of "
701 text <<
"Move-constructs an instance of "
708 text <<
"Copy-assigns "
711 <<
" to this " << fn
->parent()->name() <<
" instance.";
717 text <<
"Move-assigns "
720 <<
" to this " << fn
->parent()->name() <<
" instance.";
725 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
728 node
->location().warning(QStringLiteral(
"No documentation for '%1'")
729 .arg(node->plainSignature()));
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_outFileNames << 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
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098 auto *cn =
static_cast<CollectionNode *>(node);
1099 if (cn->wasSeen()) {
1100 m_qdb->mergeCollections(cn);
1101 beginSubPage(node, fileName(node));
1104 }
else if (cn->isGenericCollection()) {
1108 QString name = cn->name().toLower();
1109 name.replace(QChar(
' '), QString(
"-"));
1111 cn->tree()->physicalModuleName() +
"-" + name +
"." + fileExtension();
1112 beginSubPage(node, filename);
1117 beginSubPage(node, fileName(node));
1123 beginSubPage(node, fileName(node));
1127 beginSubPage(node, fileName(node));
1128 auto *qcn =
static_cast<QmlTypeNode *>(node);
1132 beginSubPage(node, fileName(node));
1140 auto *aggregate =
static_cast<Aggregate *>(node);
1141 const NodeList &children = aggregate->childNodes();
1142 for (
auto *child : children) {
1143 if (child->isPageNode()) {
1144 generateDocumentation(child);
1145 }
else if (!node->parent() && child->isInAPI() && !child->isRelatedNonmember()
1146 && !child->doc().isAutoGenerated()) {
1148 child->location().warning(u"No documentation generated for %1 '%2' in global scope."_s
1149 .arg(typeString(child), child->name()),
1150 u"Maybe you forgot to use the '\\relates' command?"_s);
1151 child->setStatus(Status::DontDocument);
1152 }
else if (child->isQmlModule() && !child->wasSeen()) {
1154 auto *qmlModule =
static_cast<CollectionNode *>(child);
1155 for (
const auto *member : qmlModule->members()) {
1156 member->location().warning(
1157 u"Undocumented QML module '%1' referred by type '%2' or its members"_s
1158 .arg(qmlModule->name(), member->name()),
1159 u"Maybe you forgot to document '\\qmlmodule %1'?"_s
1160 .arg(qmlModule->name()));
1162 }
else if (child->isQmlType() && !child->hasDoc()) {
1164 auto *qmlType =
static_cast<QmlTypeNode *>(child);
1165 if (
auto qmid = qmlType->logicalModuleName(); !qmid.isEmpty())
1166 qmlType->location().warning(u"No such type '%1' in QML module '%2'"_s
1167 .arg(qmlType->name(), qmid));
1179 const FunctionNode *overrides = cn->findOverriddenFunction(fn);
1185 overrides->parent()->name()
1186 +
"::" + overrides->signature(Node::SignaturePlain);
1189 generateText(text, fn, marker);
1191 fn
->doc().location().warning(
1192 QStringLiteral(
"Illegal \\reimp; no documented virtual function for %1")
1193 .arg(overrides->plainSignature()));
1197 const PropertyNode *sameName = cn->findOverriddenProperty(fn);
1198 if (sameName && sameName
->hasDoc()) {
1200 text <<
Atom::ParaLeft <<
"Reimplements an access function for property: ";
1201 QString fullName = sameName->parent()->name() +
"::" + sameName->name();
1204 generateText(text, fn, marker);
1210 QStringList since = node->since().split(QLatin1Char(
' '));
1213 if (since.size() == 1) {
1215 return productName.isEmpty() ? node->since() : productName +
" " + since[0];
1219 return node->since();
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1245 status = metaMap->value(
"status");
1246 if (!status.isEmpty())
1249 const auto &since = node->deprecatedSince();
1251 status = u"Deprecated"_s;
1252 if (!since.isEmpty())
1253 status +=
" since %1"_L1.arg(since);
1254 }
else if (!since.isEmpty()) {
1255 status =
"Until %1"_L1.arg(since);
1259 status = collection->state();
1262 return status.isEmpty() ?
std::nullopt :
std::optional(status);
1267 if (!node->since().isEmpty()) {
1270 const auto &collective =
static_cast<
const SharedCommentNode *>(node)->collective();
1271 QString typeStr = typeString(collective.first(), collective.size() > 1);
1272 text << Atom::ParaLeft <<
"These " << typeStr <<
" were introduced in "
1273 << formatSince(node) <<
"." << Atom::ParaRight;
1275 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" was introduced in "
1276 << formatSince(node) <<
"." << Atom::ParaRight;
1283 std::vector<
const Node*> nodes;
1285 auto shared_node =
static_cast<
const SharedCommentNode*>(node);
1286 nodes.reserve(shared_node->collective().size());
1287 nodes.insert(nodes.begin(), shared_node->collective().begin(), shared_node->collective().end());
1288 }
else nodes.push_back(node);
1290 std::size_t counter{1};
1291 for (
const Node* node : nodes) {
1292 if (node->isFunction(Genus::CPP)) {
1293 if (
const auto &exception_info =
static_cast<
const FunctionNode*>(node)->getNoexcept(); exception_info && !(*exception_info).isEmpty()) {
1295 text << Atom::NoteLeft
1296 << (nodes.size() > 1 ? QString::fromStdString(
" ("s + std::to_string(counter) +
")"s) : QString::fromStdString(
"This ") + typeString(node))
1297 <<
" is noexcept when "
1298 << Atom(Atom::C, marker->markedUpCode(*exception_info,
nullptr, Location()))
1299 <<
" is " << Atom(Atom::C,
"true") <<
"."
1301 generateText(text, node, marker);
1317 const QString &state =
static_cast<
const CollectionNode*>(node)->state();
1318 if (!state.isEmpty()) {
1319 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" is in "
1326 if (
const auto &version = node->deprecatedSince(); !version.isEmpty()) {
1327 text << Atom::ParaLeft <<
"This " << typeString(node)
1328 <<
" is scheduled for deprecation in version "
1329 << version <<
"." << Atom::ParaRight;
1333 auto description = Config::instance()
1336 description.replace(
'\1'_L1, typeString(node));
1345 text <<
"This " << typeString(node) <<
" is deprecated";
1346 if (
const QString &version = node->deprecatedSince(); !version.isEmpty()) {
1348 if (node
->isQmlNode() && !node->logicalModuleName().isEmpty())
1349 text << node->logicalModuleName() <<
" ";
1353 text <<
". We strongly advise against using it in new code.";
1361 <<
"Part of developer documentation for internal use."
1371
1372
1373
1377 Q_ASSERT(node && !node->name().isEmpty());
1379 text << Atom(Atom::DivLeft,
1380 "class=\"admonition %1\""_L1.arg(prefix == AdmonitionPrefix::Note ? u"note"_s : u"auto"_s));
1395 text <<
"This function can be invoked via the meta-object system and from QML. See "
1401 text <<
"This is a private signal. It can be used in signal connections "
1402 "but cannot be emitted by the user.";
1406 QString handler(node->name());
1407 qsizetype prefixLocation = handler.lastIndexOf(
'.', -2) + 1;
1408 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
1409 handler.insert(prefixLocation, QLatin1String(
"on"));
1410 text <<
"The corresponding handler is "
1419 const auto *fn =
static_cast<
const FunctionNode *>(node);
1420 auto nodes = fn->associatedProperties();
1421 if (nodes.isEmpty())
1426 QMap<PropertyNode::FunctionRole, QList<
const PropertyNode *>> roleGroups;
1427 for (
const auto *n : std::as_const(nodes)) {
1428 const auto *pn =
static_cast<
const PropertyNode *>(n);
1429 if (pn->isInAPI()) {
1430 PropertyNode::FunctionRole role = pn->role(fn);
1431 roleGroups[role].append(pn);
1435 if (roleGroups.isEmpty())
1446 for (
auto role : roleOrder) {
1447 const auto it = roleGroups.constFind(role);
1448 if (it == roleGroups.cend())
1451 const auto &properties = it.value();
1455 case PropertyNode::FunctionRole::Getter:
1456 msg = u"Getter function"_s;
1458 case PropertyNode::FunctionRole::Setter:
1459 msg = u"Setter function"_s;
1461 case PropertyNode::FunctionRole::Resetter:
1462 msg = u"Resetter function"_s;
1464 case PropertyNode::FunctionRole::Notifier:
1465 msg = u"Notifier signal"_s;
1467 case PropertyNode::FunctionRole::Bindable:
1468 msg = u"Bindable function"_s;
1474 if (properties.size() == 1) {
1475 const auto *pn = properties.first();
1476 text << msg << u" for property "_s << Atom(Atom::Link, pn->name())
1480 text << msg << u" for properties "_s;
1481 for (qsizetype i = 0; i < properties.size(); ++i) {
1482 const auto *pn = properties.at(i);
1486 << Utilities::separator(i, properties.size());
1495 text <<
"This property supports "
1499 text <<
" bindings.";
1504 const auto *func =
static_cast<
const FunctionNode *>(node);
1507 if (func->isPrimaryOverload())
1510 if (func->isSignal() || func->isSlot()) {
1511 QString functionType = func->isSignal() ?
"signal" :
"slot";
1512 const QString &configKey = func->isSignal() ?
"overloadedsignalstarget" :
"overloadedslotstarget";
1513 const QString &defaultTarget = func->isSignal() ?
"connecting-overloaded-signals" :
"connecting-overloaded-slots";
1514 const QString &linkTarget = Config::instance().get(configKey).asString(defaultTarget);
1516 text <<
"This " << functionType <<
" is overloaded. ";
1518 QString snippet = generateOverloadSnippet(func);
1519 if (!snippet.isEmpty()) {
1520 text <<
"To connect to this " << functionType <<
":\n\n"
1524 if (!linkTarget.isEmpty()) {
1525 text <<
"For more examples and approaches, see "
1528 <<
"connecting to overloaded " << functionType <<
"s"
1532 const auto &args = node
->doc().overloadList();
1533 if (args.first().first.isEmpty()) {
1534 text <<
"This is an overloaded function.";
1536 QString target = args.first().first;
1539 if (!target.contains(
"::")) {
1542 target = parent->name() +
"::" + target;
1559
1560
1561
1562
1565 bool result =
false;
1576
1577
1578
1579
1580
1581
1582
1583
1587 bool result =
false;
1589 const NodeList &children =
static_cast<
const Aggregate *>(node)->childNodes();
1590 for (
auto child : children) {
1591 if (!child->isDeprecated()) {
1592 switch (child->threadSafeness()) {
1593 case Node::Reentrant:
1594 reentrant.append(child);
1595 if (ts == Node::ThreadSafe)
1598 case Node::ThreadSafe:
1599 threadsafe.append(child);
1600 if (ts == Node::Reentrant)
1603 case Node::NonReentrant:
1604 nonreentrant.append(child);
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1636 if (atom->count() > 1) {
1637 if (s_trademarks.contains(atom->string(1)))
1639 s_trademarks << atom->string(1);
1652
1653
1654
1657 Text text, rlink, tlink;
1662 bool exceptions =
false;
1673 case Node::NonReentrant:
1676 << typeString(node) <<
" is not " << rlink <<
"." << Atom::ParaRight;
1682 exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
1683 text <<
"All functions in this " << typeString(node) <<
" are ";
1692 text <<
" with the following exceptions:";
1694 text <<
"This " << typeString(node) <<
" is ";
1711 if (!nonreentrant.isEmpty()) {
1713 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1714 signatureList(nonreentrant, node, marker);
1716 if (!threadsafe.isEmpty()) {
1719 text <<
"These functions are also " << tlink <<
":" <<
Atom::ParaRight;
1721 signatureList(threadsafe, node, marker);
1724 if (!reentrant.isEmpty()) {
1726 text <<
"These functions are only " << rlink <<
":" <<
Atom::ParaRight;
1727 signatureList(reentrant, node, marker);
1729 if (!nonreentrant.isEmpty()) {
1732 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1733 signatureList(nonreentrant, node, marker);
1740
1741
1742
1743
1744
1748 if (category == ComparisonCategory::None)
1752 text << Atom::ParaLeft <<
"%1 is "_L1.arg(node->plainFullName())
1754 << QString::fromStdString(comparisonCategoryAsString(category))
1755 << ((category == ComparisonCategory::Equality) ?
"-"_L1 :
"ly "_L1)
1756 << Atom(Atom::String,
"comparable"_L1)
1758 <<
"."_L1 << Atom::ParaRight;
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1782 const auto *map = node
->doc().comparesWithMap();
1784 const bool hasSelfComparison = (selfCategory != ComparisonCategory::None);
1785 const bool hasComparesWithEntries = (map && !map->isEmpty());
1787 if (!hasSelfComparison && !hasComparesWithEntries)
1790 bool hasDescriptions =
false;
1791 if (hasComparesWithEntries) {
1792 for (
const auto &description : *map) {
1793 if (description.firstAtom()->next() != description.lastAtom()) {
1794 hasDescriptions =
true;
1802 text << Atom::ParaLeft
1804 <<
"%1 Comparisons"_L1.arg(node->plainFullName())
1808 text << Atom(Atom::TableLeft,
"generic"_L1);
1810 text << Atom::TableHeaderLeft
1811 << Atom::TableItemLeft <<
"Category"_L1 << Atom::TableItemRight
1812 << Atom::TableItemLeft <<
"Comparable Types"_L1 << Atom::TableItemRight;
1813 if (hasDescriptions)
1814 text << Atom::TableItemLeft <<
"Description"_L1 << Atom::TableItemRight;
1818 if (hasSelfComparison) {
1819 const QString &category = QString::fromStdString(comparisonCategoryAsString(selfCategory));
1825 if (hasDescriptions)
1831 if (hasComparesWithEntries) {
1832 for (
auto [key, description] : map->asKeyValueRange()) {
1833 const QString &category = QString::fromStdString(comparisonCategoryAsString(key));
1835 text << Atom::TableRowLeft;
1837 text << Atom::TableItemLeft << category << Atom::TableItemRight;
1839 text << Atom::TableItemLeft;
1840 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
1841 for (
const auto &name : types)
1842 text << Atom(Atom::AutoLink, name)
1843 << Utilities::separator(types.indexOf(name), types.size());
1844 text << Atom::TableItemRight;
1846 if (hasDescriptions) {
1847 text << Atom::TableItemLeft;
1848 if (description.firstAtom()->next() != description.lastAtom())
1849 text << Text::subText(description.firstAtom()->next(), description.lastAtom());
1850 text << Atom::TableItemRight;
1853 text << Atom::TableRowRight;
1864
1865
1868 s_currentGenerator =
this;
1880 if (
auto *gen =
dynamic_cast<
Generator *>(producer))
1886 for (
const auto &generator : std::as_const(s_generators)) {
1887 if (generator->format() == format)
1902 while (i < markedCode.size()) {
1903 if (markedCode.at(i) == QLatin1Char(
'\n')) {
1907 for (
int j = 0; j < level; j++)
1908 t += QLatin1Char(
' ');
1912 t += markedCode.at(i++);
1919 Config &config = Config::instance();
1920 s_outputFormats = config.getOutputFormats();
1923 for (
auto &g : s_generators) {
1924 if (s_outputFormats.contains(g->format())) {
1925 s_currentGenerator = g;
1926 OutputProducerRegistry::instance().registerProducer(g);
1927 g->initializeGenerator();
1932 for (
const auto &n : configFormatting) {
1934 const auto &formattingDotNames = config.subVars(formattingDotName);
1935 for (
const auto &f : formattingDotNames) {
1936 const auto &configVar = config.get(formattingDotName + Config::dot + f);
1937 QString def{configVar.asString()};
1938 if (!def.isEmpty()) {
1939 int numParams = Config::numParams(def);
1940 int numOccs = def.count(
"\1");
1941 if (numParams != 1) {
1942 configVar.location().warning(QStringLiteral(
"Formatting '%1' must "
1944 "parameter (found %2)")
1945 .arg(n, numParams));
1946 }
else if (numOccs > 1) {
1947 configVar.location().fatal(QStringLiteral(
"Formatting '%1' must "
1948 "contain exactly one "
1949 "occurrence of '\\1' "
1953 int paramPos = def.indexOf(
"\1");
1954 s_fmtLeftMaps[f].insert(n, def.left(paramPos));
1955 s_fmtRightMaps[f].insert(n, def.mid(paramPos + 1));
1962 s_outDir = config.getOutputDir();
1963 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
1965 s_outputPrefixes.clear();
1967 if (!items.isEmpty()) {
1968 for (
const auto &prefix : items)
1969 s_outputPrefixes[prefix] =
1972 if (!items.contains(u"QML"_s))
1973 s_outputPrefixes[u"QML"_s] = u"qml-"_s;
1975 s_outputSuffixes.clear();
1978 + Config::dot + suffix).asString();
1985
1986
1987
1988void Generator::copyTemplateFiles(
const QString &configVar,
const QString &subDir)
2005 Config &config = Config::instance();
2006 QStringList files = config.getCanonicalPathList(configVar,
Config::Validate);
2007 const auto &loc = config.get(configVar)
.location();
2008 if (!files.isEmpty()) {
2018 OutputDirectory::ensure(s_outDir, loc);
2021 outDir.ensureSubdir(subDir, loc);
2023 for (
const auto &file : files) {
2024 if (!file.isEmpty()) {
2025 const QFileInfo fi(file);
2026 Config::copyFile(loc, fi.absoluteFilePath(), fi.fileName(), templateDir.path());
2033
2034
2035
2036
2039 Config &config = Config::instance();
2040 s_outFileNames.clear();
2041 s_useOutputSubdirs =
true;
2042 if (config.get(format() + Config::dot +
"nosubdirs").asBool())
2045 if (s_outputFormats.isEmpty())
2050 s_outDir = config.getOutputDir(format());
2051 if (s_outDir.isEmpty()) {
2052 Location().fatal(QStringLiteral(
"No output directory specified in "
2053 "configuration file or on the command line"));
2055 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
2060 OutputDirectory::ensure(s_outDir,
Location());
2064 if (!outputDir.toQDir().isEmpty())
2065 Location().error(
"Output directory '%1' exists but is not empty"_L1.arg(s_outDir));
2075 outputDir.ensureSubdir(imagesDir,
Location());
2076 s_imagesOutDir =
std::move(imagesDir);
2079 copyTemplateFiles(format() + Config::dot +
CONFIG_SCRIPTS,
"scripts");
2090
2091
2092
2104
2105
2106
2107
2110 return *outStreamStack.top();
2115 return QFileInfo(
static_cast<QFile *>(
out().device())->fileName()).fileName();
2124 return s_outputPrefixes[u"QML"_s];
2126 return s_outputPrefixes[u"CPP"_s];
2139 return s_outputSuffixes[u"QML"_s];
2141 return s_outputSuffixes[u"CPP"_s];
2151 QStringView *contents, QStringView *par1)
2154 if (i >= n || src[i] != c)
2159 while (i < n && src[i] == ' ')
2169 if (tag != QStringView(src).mid(i, tag.size())) {
2181 while (i < n && src[i].isLetter())
2183 if (src[i] ==
'=') {
2188 while (i < n && src[i] !=
'"')
2190 *par1 = QStringView(src).mid(j, i - j);
2201 if (i + 4 + tag.size() > n)
2205 if (src[i + 1] !=
'/')
2207 if (src[i + 2] !=
'@')
2209 if (tag != QStringView(src).mid(i + 3, tag.size()))
2211 if (src[i + 3 + tag.size()] !=
'>')
2216 *contents = QStringView(src).mid(j, i - j);
2218 i += tag.size() + 4;
2228 QString t = markedCode;
2229 t.replace(tag, QString());
2230 t.replace(quot, QLatin1String(
"\""));
2231 t.replace(gt, QLatin1String(
">"));
2232 t.replace(lt, QLatin1String(
"<"));
2233 t.replace(amp, QLatin1String(
"&"));
2241 while (atom && atom
->type() != type) {
2249
2250
2260 m_sectionNumber.clear();
2266 const auto fn =
static_cast<
const FunctionNode *>(node);
2267 if (fn->overloadNumber() == 0) {
2268 QString alternateName;
2271 if (fn->name().startsWith(
"set") && fn->name().size() >= 4) {
2272 alternateName = fn->name()[3].toLower();
2273 alternateName += fn->name().mid(4);
2274 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2276 if (!alternateFunc) {
2277 alternateName =
"is" + fn->name().mid(3);
2278 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2279 if (!alternateFunc) {
2280 alternateName =
"has" + fn->name().mid(3);
2281 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2284 }
else if (!fn->name().isEmpty()) {
2285 alternateName =
"set";
2286 alternateName += fn->name()[0].toUpper();
2287 alternateName += fn->name().mid(1);
2288 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2293 for (i = 0; i < alsoList.size(); ++i) {
2294 if (alsoList.at(i).toString().contains(alternateName))
2298 if (i == alsoList.size()) {
2301 alternateName +=
"()";
2307 alsoList.prepend(also);
2327 const auto *start{body.firstAtom()};
2333 text << body.subText(text
.isEmpty() ? start : start->next(), end);
2348 for (
const auto &generator : std::as_const(s_generators)) {
2349 if (s_outputFormats.contains(generator->format())) {
2350 OutputProducerRegistry::instance().unregisterProducer(generator);
2351 generator->terminateGenerator();
2378 s_generators.clear();
2380 s_fmtLeftMaps.clear();
2381 s_fmtRightMaps.clear();
2383 s_imagesOutDir.clear();
2389
2390
2391
2392QString
Generator::trimmedTrailing(
const QString &string,
const QString &prefix,
2393 const QString &suffix)
2395 QString trimmed = string;
2396 while (trimmed.size() > 0 && trimmed[trimmed.size() - 1].isSpace())
2397 trimmed.truncate(trimmed.size() - 1);
2399 trimmed.append(suffix);
2400 trimmed.prepend(prefix);
2407 case NodeType::Namespace:
2408 return plural ?
"namespaces"_L1 :
"namespace"_L1;
2409 case NodeType::Class:
2410 return plural ?
"classes"_L1 :
"class"_L1;
2411 case NodeType::Struct:
2412 return plural ?
"structs"_L1 :
"struct"_L1;
2413 case NodeType::Union:
2414 return plural ?
"unions"_L1 :
"union"_L1;
2415 case NodeType::QmlType:
2416 case NodeType::QmlValueType:
2417 return plural ?
"types"_L1 :
"type"_L1;
2418 case NodeType::Page:
2419 return "documentation"_L1;
2420 case NodeType::Enum:
2421 return plural ?
"enums"_L1 :
"enum"_L1;
2422 case NodeType::Typedef:
2423 case NodeType::TypeAlias:
2424 return plural ?
"typedefs"_L1 :
"typedef"_L1;
2426 const auto fn =
static_cast<
const FunctionNode *>(node);
2427 switch (fn->metaness()) {
2428 case FunctionNode::QmlSignal:
2429 return plural ?
"signals"_L1 :
"signal"_L1;
2430 case FunctionNode::QmlSignalHandler:
2431 return plural ?
"signal handlers"_L1 :
"signal handler"_L1;
2432 case FunctionNode::QmlMethod:
2433 return plural ?
"methods"_L1 :
"method"_L1;
2434 case FunctionNode::MacroWithParams:
2435 case FunctionNode::MacroWithoutParams:
2436 return plural ?
"macros"_L1 :
"macro"_L1;
2440 return plural ?
"functions"_L1 :
"function"_L1;
2442 case NodeType::Property:
2443 case NodeType::QmlProperty:
2444 return plural ?
"properties"_L1 :
"property"_L1;
2445 case NodeType::Module:
2446 case NodeType::QmlModule:
2447 return plural ?
"modules"_L1 :
"module"_L1;
2448 case NodeType::Variable:
2449 return plural ?
"variables"_L1 :
"variable"_L1;
2451 const auto *shared =
static_cast<
const SharedCommentNode *>(node);
2452 if (shared->isPropertyGroup())
2453 return plural ?
"property groups"_L1 :
"property group"_L1;
2454 const auto &collective = shared->collective();
2455 return collective.first()->nodeTypeString();
2458 return "documentation"_L1;
2464 Location::internalError(QStringLiteral(
"unknown atom type '%1' in %2 generator")
2465 .arg(atom->typeString(), format()));
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2502 if (!cn || (cn->cmakeComponent().isEmpty() && cn->cmakePackage().isEmpty())) {
2506 const QString package =
2507 cn->cmakePackage().isEmpty() ?
"Qt" + QString::number(QT_VERSION_MAJOR) : cn->cmakePackage();
2509 QString findPackageText;
2510 if (cn->cmakeComponent().isEmpty()) {
2511 findPackageText =
"find_package(" + package +
" REQUIRED)";
2513 findPackageText =
"find_package(" + package +
" REQUIRED COMPONENTS " + cn->cmakeComponent() +
")";
2517 if (cn->cmakeTargetItem().isEmpty()) {
2518 if (cn->cmakeComponent().isEmpty()) {
2519 targetText = package +
"::" + package;
2521 targetText = package +
"::" + cn->cmakeComponent();
2524 targetText = cn->cmakeTargetItem();
2527 const QString targetLinkLibrariesText =
"target_link_libraries(mytarget PRIVATE " + targetText +
")";
2528 const QStringList cmakeInfo { findPackageText, targetLinkLibrariesText };
2530 return std::make_pair(findPackageText, targetLinkLibrariesText);
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543void Generator::addNodeLink(
Text &text,
const QString &nodeRef,
const QString &linkText) {
2551
2552
2553
2554
2555
2556
2557
2558
2562 Utilities::stringForNode(node),
2563 linkText.isEmpty() ? node->name() : linkText
2568
2569
2570
2571
2572
2573
2574
2580 QString className = func->parent()->name();
2581 QString functionName = func->name();
2582 QString typeList = func->parameters().generateTypeList();
2583 QString typeAndNameList = func->parameters().generateTypeAndNameList();
2584 QString nameList = func->parameters().generateNameList();
2585 QString objectName = generateObjectName(className);
2591 "// Connect using qOverload:\n"
2592 "connect(%1, qOverload<%2>(&%3::%4),\n"
2593 " receiver, &ReceiverClass::slot);\n\n"
2594 "// Or using a lambda:\n"
2595 "connect(%1, qOverload<%2>(&%3::%4),\n"
2596 " this, [](%5) { /* handle %4 */ });")
2597 .arg(objectName, typeList, className, functionName, typeAndNameList);
2600 "// Connect using qOverload:\n"
2601 "connect(sender, &SenderClass::signal,\n"
2602 " %1, qOverload<%2>(&%3::%4));\n\n"
2603 "// Or using a lambda as wrapper:\n"
2604 "connect(sender, &SenderClass::signal,\n"
2605 " %1, [receiver = %1](%5) { receiver->%4(%6); });")
2606 .arg(objectName, typeList, className, functionName, typeAndNameList, nameList);
2613
2614
2615
2618 QString name = className;
2620 if (name.startsWith(
'Q') && name.length() > 1)
2623 if (!name.isEmpty())
2624 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
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.
void beginSubPage(const Node *node, const QString &fileName)
Creates the file named fileName in the output directory.
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...
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
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.