37#include <QtCore/qdebug.h>
38#include <QtCore/qdir.h>
39#include <QtCore/qregularexpression.h>
41#ifndef QT_BOOTSTRAPPED
42# include "QtCore/qurl.h"
48using namespace std::literals::string_literals;
52using namespace Qt::StringLiterals;
55QMap<QString, QMap<QString, QString>>
Generator::s_fmtLeftMaps;
56QMap<QString, QMap<QString, QString>>
Generator::s_fmtRightMaps;
57QList<Generator *>
Generator::s_generators;
64QHash<QString, QString>
Generator::s_outputPrefixes;
65QHash<QString, QString>
Generator::s_outputSuffixes;
73static QRegularExpression
tag(
"</?@[^>]*>");
80
81
82
83
84
90 names.unite(p->templateDecl()->parameterNames());
96
97
98
99
100
101
102
103
107
108
109
111 const QSet<QString> &documentedNames,
112 const QSet<QString> &allowedNames,
115 for (
const auto &name : documentedNames) {
116 if (!allowedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
117 const auto message = (context == ValidationContext::TemplateDoc)
118 ?
"No such template parameter '%1' in %2"_L1
119 :
"No such parameter '%1' in %2"_L1;
120 node->doc().location().warning(message.arg(name, node->plainFullName()),
121 suggestName(name, allowedNames));
127
128
129
130
131
136 s_generators.prepend(
this);
140
141
142
145 s_generators.removeAll(
this);
149 const Node *actualNode)
151 if (actualNode ==
nullptr)
152 actualNode = apparentNode;
154 addNodeLink(text, actualNode, apparentNode->plainFullName(relative));
158 const Node *actualNode)
160 if (actualNode ==
nullptr)
161 actualNode = apparentNode;
163 addNodeLink(text, actualNode, fullName);
167
168
169
170
177
178
179
180
186 for (
const auto &node : nodes) {
187 text << Atom(Atom::ListItemNumber, QString::number(++count));
188 text << Atom(Atom::ListItemLeft, QString(
"bullet"));
189 appendSignature(text, node);
190 text << Atom(Atom::ListItemRight, QString(
"bullet"));
198 QMap<QString, Text> classMap;
199 for (
const auto &relatedClass : rc) {
200 ClassNode *rcn = relatedClass.m_node;
201 if (rcn && rcn->isInAPI()) {
203 appendFullName(className, rcn, cn);
204 classMap[className.toString().toLower()] = className;
209 const QStringList classNames = classMap.keys();
210 for (
const auto &className : classNames) {
211 text << classMap[className];
212 text << TextUtils::comma(index++, classNames.size());
220 QMap<QString, Text> classMap;
222 QStringList typeNames(knownTypes);
223 for (
const auto sub : subs)
224 typeNames << sub->name();
226 for (
const auto sub : subs) {
228 appendFullName(full_name, sub, base);
230 if (typeNames.count(sub->name()) > 1)
231 full_name << Atom(Atom::String,
" (%1)"_L1.arg(sub->logicalModuleName()));
232 classMap[full_name.toString().toLower()] = full_name;
236 const auto &names = classMap.keys();
237 for (
const auto &name : names)
238 text << classMap[name] << TextUtils::comma(index++, names.size());
243
244
245
246
247
248
249
257 if (s_outFileNames.contains(fileName) && !node
->isAttribution() && !fileName.contains(
"-attribution-"_L1))
258 node
->location().warning(
"Already generated %1 for this project"_L1.arg(fileName));
260 QString path = outputDir() + QLatin1Char(
'/') + fileName;
263 auto outFile =
new QFile(outPath);
266 const QString warningText {
"Output file already exists, overwriting %1"_L1.arg(outFile->fileName())};
267 if (qEnvironmentVariableIsSet(
"QDOC_ALL_OVERWRITES_ARE_WARNINGS"))
270 qCDebug(lcQdoc) << qUtf8Printable(warningText);
273 if (!outFile->open(QFile::WriteOnly | QFile::Text)) {
275 QStringLiteral(
"Cannot open output file '%1'").arg(outFile->fileName()));
278 qCDebug(lcQdoc,
"Writing: %s", qPrintable(path));
279 s_outFileNames << fileName;
280 s_trademarks.clear();
285
286
287
288
291 QFile *outFile = openSubPageFile(
static_cast<
const PageNode*>(node), fileName);
292 auto *out =
new QTextStream(outFile);
293 outStreamStack.push(out);
297
298
299
300
303 outStreamStack.top()->flush();
304 delete outStreamStack.top()->device();
305 delete outStreamStack.pop();
314 return node->fileNameBase();
316 QString result = Utilities::computeFileBase(
318 [](
const Node *n) {
return outputPrefix(n); },
319 [](
const Node *n) {
return outputSuffix(n); });
321 const_cast<
Node *>(node)->setFileNameBase(result);
326
327
328
329
330
333 return Utilities::linkForExampleFile(path, s_project, fileExt.isEmpty() ? fileExtension() : fileExt);
337
338
339
342 return Utilities::exampleFileTitle(relative->files(), relative->images(), fileName);
346
347
348
349
350
353 if (!node->url().isEmpty())
360 QFileInfo originalName(node->name());
361 QString suffix = originalName.suffix();
362 if (!suffix.isEmpty() && suffix !=
"html") {
364 QString name = fileBase(node);
365 return name + QLatin1Char(
'.') + suffix;
369 QString name = fileBase(node) + QLatin1Char(
'.');
370 return name + (extension.isNull() ? fileExtension() : extension);
374
375
376
377
378
379
380
381
382QString
Generator::cleanRef(
const QString &ref,
bool xmlCompliant)
393 clean.reserve(ref.size() + 20);
394 const QChar c = ref[0];
395 const uint u = c.unicode();
397 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (!xmlCompliant && u >=
'0' && u <=
'9')) {
399 }
else if (xmlCompliant && u >=
'0' && u <=
'9') {
400 clean += QLatin1Char(
'A') + c;
401 }
else if (u ==
'~') {
403 }
else if (u ==
'_') {
404 clean +=
"underscore.";
406 clean += QLatin1Char(
'A');
409 for (
int i = 1; i < ref.size(); i++) {
410 const QChar c = ref[i];
411 const uint u = c.unicode();
412 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (u >=
'0' && u <=
'9') || u ==
'-'
413 || u ==
'_' || (xmlCompliant && u ==
':') || u ==
'.') {
415 }
else if (c.isSpace()) {
416 clean += QLatin1Char(
'-');
417 }
else if (u ==
'!') {
419 }
else if (u ==
'&') {
421 }
else if (u ==
'<') {
423 }
else if (u ==
'=') {
425 }
else if (u ==
'>') {
427 }
else if (u ==
'#') {
428 clean += QLatin1Char(
'#');
430 clean += QLatin1Char(
'-');
431 clean += QString::number(
static_cast<
int>(u), 16);
439 return s_fmtLeftMaps[format()];
444 return s_fmtRightMaps[format()];
448
449
454 if (!node->url().isEmpty())
462
463
464
465 if (!fileBase(node).isEmpty())
466 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
470 return fileBase(node) + QLatin1Char(
'.') + fileExtension();
472 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
473 }
else if (fileBase(node).isEmpty())
476 Node *parentNode =
nullptr;
480 if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
481 parentName = fullDocumentLocation(node->parent());
485 case NodeType::Class:
486 case NodeType::Struct:
487 case NodeType::Union:
488 case NodeType::Namespace:
489 case NodeType::Proxy:
490 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
493 const auto *fn =
static_cast<
const FunctionNode *>(node);
494 switch (fn->metaness()) {
496 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal";
499 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal-handler";
502 anchorRef = QLatin1Char(
'#') + node->name() +
"-method";
506 anchorRef =
"#dtor." + fn->name().mid(1);
507 else if (
const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty())
508 return fullDocumentLocation(p);
509 else if (fn->overloadNumber() > 0)
510 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name()) + QLatin1Char(
'-')
511 + QString::number(fn->overloadNumber());
513 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name());
519
520
521
522
525 anchorRef = QLatin1Char(
'#') + node->name() +
"-enum";
528 const auto *tdef =
static_cast<
const TypedefNode *>(node);
529 if (tdef->associatedEnum())
530 return fullDocumentLocation(tdef->associatedEnum());
533 anchorRef = QLatin1Char(
'#') + node->name() +
"-typedef";
536 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
544 anchorRef = QLatin1Char(
'#') + node->name() +
"-attached-prop";
546 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
549 anchorRef = QLatin1Char(
'#') + node->name() +
"-var";
557 parentName = fileBase(node);
558 parentName.replace(QLatin1Char(
'/'), QLatin1Char(
'-'))
559 .replace(QLatin1Char(
'.'), QLatin1Char(
'-'));
560 parentName += QLatin1Char(
'.') + fileExtension();
568 parentName.replace(QLatin1Char(
'.') + fileExtension(),
569 "-obsolete." + fileExtension());
572 return parentName.toLower() + anchorRef;
576
577
578
579
580
581
582
585 QList<Text> alsoList = node
->doc().alsoList();
586 supplementAlsoList(node, alsoList);
588 if (!alsoList.isEmpty()) {
595 for (
const auto &also : std::as_const(alsoList)) {
597 const Atom *atom = also.firstAtom();
598 QString link = atom->string();
599 if (!used.contains(link)) {
604 if (m_qdb->findNodeForAtom(atom, node, ref) == node && ref.isEmpty())
605 node->doc().location().warning(
"Redundant link to self in \\sa command for %1"_L1.arg(node->name()));
610 for (
const auto &also : std::as_const(items))
611 text << also << TextUtils::separator(i++, items.size());
619 bool generate,
int &numAtoms)
621 while (atom !=
nullptr) {
623 int numAtoms0 = numAtoms;
624 bool rightFormat = canHandleFormat(atom->string());
639 if (generate && numAtoms0 == numAtoms) {
640 relative
->location().warning(QStringLiteral(
"Output format %1 not handled %2")
641 .arg(format(), outFileName()));
642 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
652 n += generateAtom(atom, relative, marker);
664
665
666
669 const FunctionNode *fn = node->isFunction() ?
static_cast<
const FunctionNode *>(node) :
nullptr;
672
673
674
678 text <<
"Destroys the instance of ";
679 text << fn
->parent()->name() <<
".";
681 text <<
" The destructor is virtual.";
687 text <<
"Default-constructs an instance of "
694 text <<
"Copy-constructs an instance of "
701 text <<
"Move-constructs an instance of "
708 text <<
"Copy-assigns "
711 <<
" to this " << fn
->parent()->name() <<
" instance.";
717 text <<
"Move-assigns "
720 <<
" to this " << fn
->parent()->name() <<
" instance.";
725 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
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
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(typeString(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 &collective =
static_cast<
const SharedCommentNode *>(node)->collective();
1272 QString typeStr = typeString(collective.first(), collective.size() > 1);
1273 text << Atom::ParaLeft <<
"These " << typeStr <<
" were introduced in "
1274 << formatSince(node) <<
"." << Atom::ParaRight;
1276 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" was introduced in "
1277 << formatSince(node) <<
"." << Atom::ParaRight;
1284 std::vector<
const Node*> nodes;
1286 auto shared_node =
static_cast<
const SharedCommentNode*>(node);
1287 nodes.reserve(shared_node->collective().size());
1288 nodes.insert(nodes.begin(), shared_node->collective().begin(), shared_node->collective().end());
1289 }
else nodes.push_back(node);
1291 std::size_t counter{1};
1292 for (
const Node* node : nodes) {
1293 if (node->isFunction(Genus::CPP)) {
1294 if (
const auto &exception_info =
static_cast<
const FunctionNode*>(node)->getNoexcept(); exception_info && !(*exception_info).isEmpty()) {
1296 text << Atom::NoteLeft
1297 << (nodes.size() > 1 ? QString::fromStdString(
" ("s + std::to_string(counter) +
")"s) : QString::fromStdString(
"This ") + typeString(node))
1298 <<
" is noexcept when "
1299 << Atom(Atom::C, marker->markedUpCode(*exception_info,
nullptr, Location()))
1300 <<
" is " << Atom(Atom::C,
"true") <<
"."
1302 generateText(text, node, marker);
1318 const QString &state =
static_cast<
const CollectionNode*>(node)->state();
1319 if (!state.isEmpty()) {
1320 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" is in "
1327 if (
const auto &version = node->deprecatedSince(); !version.isEmpty()) {
1328 text << Atom::ParaLeft <<
"This " << typeString(node)
1329 <<
" is scheduled for deprecation in version "
1330 << version <<
"." << Atom::ParaRight;
1334 auto description = Config::instance()
1337 description.replace(
'\1'_L1, typeString(node));
1346 text <<
"This " << typeString(node) <<
" is deprecated";
1347 if (
const QString &version = node->deprecatedSince(); !version.isEmpty()) {
1349 if (node
->isQmlNode() && !node->logicalModuleName().isEmpty())
1350 text << node->logicalModuleName() <<
" ";
1354 text <<
". We strongly advise against using it in new code.";
1363 <<
"Part of developer documentation for internal use."
1373
1374
1375
1379 Q_ASSERT(node && !node->name().isEmpty());
1381 text << Atom(Atom::DivLeft,
1382 "class=\"admonition %1\""_L1.arg(prefix == AdmonitionPrefix::Note ? u"note"_s : u"auto"_s));
1397 text <<
"This function can be invoked via the meta-object system and from QML. See "
1403 text <<
"This is a private signal. It can be used in signal connections "
1404 "but cannot be emitted by the user.";
1408 QString handler(node->name());
1409 qsizetype prefixLocation = handler.lastIndexOf(
'.', -2) + 1;
1410 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
1411 handler.insert(prefixLocation, QLatin1String(
"on"));
1412 text <<
"The corresponding handler is "
1421 const auto *fn =
static_cast<
const FunctionNode *>(node);
1422 auto nodes = fn->associatedProperties();
1423 if (nodes.isEmpty())
1428 QMap<PropertyNode::FunctionRole, QList<
const PropertyNode *>> roleGroups;
1429 for (
const auto *n : std::as_const(nodes)) {
1430 const auto *pn =
static_cast<
const PropertyNode *>(n);
1431 if (pn->isInAPI()) {
1432 PropertyNode::FunctionRole role = pn->role(fn);
1433 roleGroups[role].append(pn);
1437 if (roleGroups.isEmpty())
1448 for (
auto role : roleOrder) {
1449 const auto it = roleGroups.constFind(role);
1450 if (it == roleGroups.cend())
1453 const auto &properties = it.value();
1457 case PropertyNode::FunctionRole::Getter:
1458 msg = u"Getter function"_s;
1460 case PropertyNode::FunctionRole::Setter:
1461 msg = u"Setter function"_s;
1463 case PropertyNode::FunctionRole::Resetter:
1464 msg = u"Resetter function"_s;
1466 case PropertyNode::FunctionRole::Notifier:
1467 msg = u"Notifier signal"_s;
1469 case PropertyNode::FunctionRole::Bindable:
1470 msg = u"Bindable function"_s;
1476 if (properties.size() == 1) {
1477 const auto *pn = properties.first();
1478 text << msg << u" for property "_s << Atom(Atom::Link, pn->name())
1482 text << msg << u" for properties "_s;
1483 for (qsizetype i = 0; i < properties.size(); ++i) {
1484 const auto *pn = properties.at(i);
1488 << TextUtils::separator(i, properties.size());
1497 text <<
"This property supports "
1501 text <<
" bindings.";
1506 const auto *func =
static_cast<
const FunctionNode *>(node);
1509 if (func->isPrimaryOverload())
1512 if (func->isSignal() || func->isSlot()) {
1513 QString functionType = func->isSignal() ?
"signal" :
"slot";
1514 const QString &configKey = func->isSignal() ?
"overloadedsignalstarget" :
"overloadedslotstarget";
1515 const QString &defaultTarget = func->isSignal() ?
"connecting-overloaded-signals" :
"connecting-overloaded-slots";
1516 const QString &linkTarget = Config::instance().get(configKey).asString(defaultTarget);
1518 text <<
"This " << functionType <<
" is overloaded. ";
1520 QString snippet = generateOverloadSnippet(func);
1521 if (!snippet.isEmpty()) {
1522 text <<
"To connect to this " << functionType <<
":\n\n"
1526 if (!linkTarget.isEmpty()) {
1527 text <<
"For more examples and approaches, see "
1530 <<
"connecting to overloaded " << functionType <<
"s"
1534 const auto &args = node
->doc().overloadList();
1535 if (args.first().first.isEmpty()) {
1536 text <<
"This is an overloaded function.";
1538 QString target = args.first().first;
1541 if (!target.contains(
"::")) {
1544 target = parent->name() +
"::" + target;
1561
1562
1563
1564
1567 bool result =
false;
1578
1579
1580
1581
1582
1583
1584
1585
1589 bool result =
false;
1591 const NodeList &children =
static_cast<
const Aggregate *>(node)->childNodes();
1592 for (
auto child : children) {
1593 if (!child->isDeprecated()) {
1594 switch (child->threadSafeness()) {
1595 case Node::Reentrant:
1596 reentrant.append(child);
1597 if (ts == Node::ThreadSafe)
1600 case Node::ThreadSafe:
1601 threadsafe.append(child);
1602 if (ts == Node::Reentrant)
1605 case Node::NonReentrant:
1606 nonreentrant.append(child);
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1638 if (atom->count() > 1) {
1639 if (s_trademarks.contains(atom->string(1)))
1641 s_trademarks << atom->string(1);
1654
1655
1656
1659 Text text, rlink, tlink;
1664 bool exceptions =
false;
1675 case Node::NonReentrant:
1678 << typeString(node) <<
" is not " << rlink <<
"." << Atom::ParaRight;
1684 exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
1685 text <<
"All functions in this " << typeString(node) <<
" are ";
1694 text <<
" with the following exceptions:";
1696 text <<
"This " << typeString(node) <<
" is ";
1713 if (!nonreentrant.isEmpty()) {
1715 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1716 signatureList(nonreentrant, node, marker);
1718 if (!threadsafe.isEmpty()) {
1721 text <<
"These functions are also " << tlink <<
":" <<
Atom::ParaRight;
1723 signatureList(threadsafe, node, marker);
1726 if (!reentrant.isEmpty()) {
1728 text <<
"These functions are only " << rlink <<
":" <<
Atom::ParaRight;
1729 signatureList(reentrant, node, marker);
1731 if (!nonreentrant.isEmpty()) {
1734 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1735 signatureList(nonreentrant, node, marker);
1742
1743
1744
1745
1746
1750 if (category == ComparisonCategory::None)
1754 text << Atom::ParaLeft <<
"%1 is "_L1.arg(node->plainFullName())
1756 << QString::fromStdString(comparisonCategoryAsString(category))
1757 << ((category == ComparisonCategory::Equality) ?
"-"_L1 :
"ly "_L1)
1758 << Atom(Atom::String,
"comparable"_L1)
1760 <<
"."_L1 << Atom::ParaRight;
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1784 const auto *map = node
->doc().comparesWithMap();
1786 const bool hasSelfComparison = (selfCategory != ComparisonCategory::None);
1787 const bool hasComparesWithEntries = (map && !map->isEmpty());
1789 if (!hasSelfComparison && !hasComparesWithEntries)
1792 bool hasDescriptions =
false;
1793 if (hasComparesWithEntries) {
1794 for (
const auto &description : *map) {
1795 if (description.firstAtom()->next() != description.lastAtom()) {
1796 hasDescriptions =
true;
1804 text << Atom::ParaLeft
1806 <<
"%1 Comparisons"_L1.arg(node->plainFullName())
1810 text << Atom(Atom::TableLeft,
"generic"_L1);
1812 text << Atom::TableHeaderLeft
1813 << Atom::TableItemLeft <<
"Category"_L1 << Atom::TableItemRight
1814 << Atom::TableItemLeft <<
"Comparable Types"_L1 << Atom::TableItemRight;
1815 if (hasDescriptions)
1816 text << Atom::TableItemLeft <<
"Description"_L1 << Atom::TableItemRight;
1820 if (hasSelfComparison) {
1821 const QString &category = QString::fromStdString(comparisonCategoryAsString(selfCategory));
1827 if (hasDescriptions)
1833 if (hasComparesWithEntries) {
1834 for (
auto [key, description] : map->asKeyValueRange()) {
1835 const QString &category = QString::fromStdString(comparisonCategoryAsString(key));
1837 text << Atom::TableRowLeft;
1839 text << Atom::TableItemLeft << category << Atom::TableItemRight;
1841 text << Atom::TableItemLeft;
1842 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
1843 for (
const auto &name : types)
1844 text << Atom(Atom::AutoLink, name)
1845 << TextUtils::separator(types.indexOf(name), types.size());
1846 text << Atom::TableItemRight;
1848 if (hasDescriptions) {
1849 text << Atom::TableItemLeft;
1850 if (description.firstAtom()->next() != description.lastAtom())
1851 text << Text::subText(description.firstAtom()->next(), description.lastAtom());
1852 text << Atom::TableItemRight;
1855 text << Atom::TableRowRight;
1866
1867
1870 s_currentGenerator =
this;
1882 if (
auto *gen =
dynamic_cast<
Generator *>(producer))
1888 for (
const auto &generator : std::as_const(s_generators)) {
1889 if (generator->format() == format)
1904 while (i < markedCode.size()) {
1905 if (markedCode.at(i) == QLatin1Char(
'\n')) {
1909 for (
int j = 0; j < level; j++)
1910 t += QLatin1Char(
' ');
1914 t += markedCode.at(i++);
1921 Config &config = Config::instance();
1922 s_outputFormats = config.getOutputFormats();
1925 for (
auto &g : s_generators) {
1926 if (s_outputFormats.contains(g->format())) {
1927 s_currentGenerator = g;
1928 OutputProducerRegistry::instance().registerProducer(g);
1929 g->initializeGenerator();
1934 for (
const auto &n : configFormatting) {
1936 const auto &formattingDotNames = config.subVars(formattingDotName);
1937 for (
const auto &f : formattingDotNames) {
1938 const auto &configVar = config.get(formattingDotName + Config::dot + f);
1939 QString def{configVar.asString()};
1940 if (!def.isEmpty()) {
1941 int numParams = Config::numParams(def);
1942 int numOccs = def.count(
"\1");
1943 if (numParams != 1) {
1944 configVar.location().warning(QStringLiteral(
"Formatting '%1' must "
1946 "parameter (found %2)")
1947 .arg(n, numParams));
1948 }
else if (numOccs > 1) {
1949 configVar.location().fatal(QStringLiteral(
"Formatting '%1' must "
1950 "contain exactly one "
1951 "occurrence of '\\1' "
1955 int paramPos = def.indexOf(
"\1");
1956 s_fmtLeftMaps[f].insert(n, def.left(paramPos));
1957 s_fmtRightMaps[f].insert(n, def.mid(paramPos + 1));
1964 s_outDir = config.getOutputDir();
1965 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
1967 s_outputPrefixes.clear();
1969 if (!items.isEmpty()) {
1970 for (
const auto &prefix : items)
1971 s_outputPrefixes[prefix] =
1974 if (!items.contains(u"QML"_s))
1975 s_outputPrefixes[u"QML"_s] = u"qml-"_s;
1977 s_outputSuffixes.clear();
1980 + Config::dot + suffix).asString();
1987
1988
1989
1990void Generator::copyTemplateFiles(
const QString &configVar,
const QString &subDir)
2007 Config &config = Config::instance();
2008 QStringList files = config.getCanonicalPathList(configVar,
Config::Validate);
2009 const auto &loc = config.get(configVar)
.location();
2010 if (!files.isEmpty()) {
2020 OutputDirectory::ensure(s_outDir, loc);
2023 outDir.ensureSubdir(subDir, loc);
2025 for (
const auto &file : files) {
2026 if (!file.isEmpty()) {
2027 const QFileInfo fi(file);
2028 Config::copyFile(loc, fi.absoluteFilePath(), fi.fileName(), templateDir.path());
2035
2036
2037
2038
2041 Config &config = Config::instance();
2042 s_outFileNames.clear();
2043 s_useOutputSubdirs =
true;
2044 if (config.get(format() + Config::dot +
"nosubdirs").asBool())
2047 if (s_outputFormats.isEmpty())
2052 s_outDir = config.getOutputDir(format());
2053 if (s_outDir.isEmpty()) {
2054 Location().fatal(QStringLiteral(
"No output directory specified in "
2055 "configuration file or on the command line"));
2057 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
2062 OutputDirectory::ensure(s_outDir,
Location());
2066 if (!outputDir.toQDir().isEmpty())
2067 Location().error(
"Output directory '%1' exists but is not empty"_L1.arg(s_outDir));
2077 outputDir.ensureSubdir(imagesDir,
Location());
2078 s_imagesOutDir =
std::move(imagesDir);
2081 copyTemplateFiles(format() + Config::dot +
CONFIG_SCRIPTS,
"scripts");
2092
2093
2094
2106
2107
2108
2109
2112 return *outStreamStack.top();
2117 return QFileInfo(
static_cast<QFile *>(
out().device())->fileName()).fileName();
2126 return s_outputPrefixes[u"QML"_s];
2128 return s_outputPrefixes[u"CPP"_s];
2141 return s_outputSuffixes[u"QML"_s];
2143 return s_outputSuffixes[u"CPP"_s];
2153 QStringView *contents, QStringView *par1)
2156 if (i >= n || src[i] != c)
2161 while (i < n && src[i] == ' ')
2171 if (tag != QStringView(src).mid(i, tag.size())) {
2183 while (i < n && src[i].isLetter())
2185 if (src[i] ==
'=') {
2190 while (i < n && src[i] !=
'"')
2192 *par1 = QStringView(src).mid(j, i - j);
2203 if (i + 4 + tag.size() > n)
2207 if (src[i + 1] !=
'/')
2209 if (src[i + 2] !=
'@')
2211 if (tag != QStringView(src).mid(i + 3, tag.size()))
2213 if (src[i + 3 + tag.size()] !=
'>')
2218 *contents = QStringView(src).mid(j, i - j);
2220 i += tag.size() + 4;
2230 QString t = markedCode;
2231 t.replace(tag, QString());
2232 t.replace(quot, QLatin1String(
"\""));
2233 t.replace(gt, QLatin1String(
">"));
2234 t.replace(lt, QLatin1String(
"<"));
2235 t.replace(amp, QLatin1String(
"&"));
2243 while (atom && atom
->type() != type) {
2251
2252
2262 m_sectionNumber.clear();
2268 const auto fn =
static_cast<
const FunctionNode *>(node);
2269 if (fn->overloadNumber() == 0) {
2270 QString alternateName;
2273 if (fn->name().startsWith(
"set") && fn->name().size() >= 4) {
2274 alternateName = fn->name()[3].toLower();
2275 alternateName += fn->name().mid(4);
2276 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2278 if (!alternateFunc) {
2279 alternateName =
"is" + fn->name().mid(3);
2280 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2281 if (!alternateFunc) {
2282 alternateName =
"has" + fn->name().mid(3);
2283 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2286 }
else if (!fn->name().isEmpty()) {
2287 alternateName =
"set";
2288 alternateName += fn->name()[0].toUpper();
2289 alternateName += fn->name().mid(1);
2290 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2293 if (alternateFunc && alternateFunc
->access() != Access::Private) {
2295 for (i = 0; i < alsoList.size(); ++i) {
2296 if (alsoList.at(i).toString().contains(alternateName))
2300 if (i == alsoList.size()) {
2303 alternateName +=
"()";
2309 alsoList.prepend(also);
2329 const auto *start{body.firstAtom()};
2335 text << body.subText(text
.isEmpty() ? start : start->next(), end);
2350 for (
const auto &generator : std::as_const(s_generators)) {
2351 if (s_outputFormats.contains(generator->format())) {
2352 OutputProducerRegistry::instance().unregisterProducer(generator);
2353 generator->terminateGenerator();
2380 s_generators.clear();
2382 s_fmtLeftMaps.clear();
2383 s_fmtRightMaps.clear();
2385 s_imagesOutDir.clear();
2391
2392
2393
2394QString
Generator::trimmedTrailing(
const QString &string,
const QString &prefix,
2395 const QString &suffix)
2397 QString trimmed = string;
2398 while (trimmed.size() > 0 && trimmed[trimmed.size() - 1].isSpace())
2399 trimmed.truncate(trimmed.size() - 1);
2401 trimmed.append(suffix);
2402 trimmed.prepend(prefix);
2409 case NodeType::Namespace:
2410 return plural ?
"namespaces"_L1 :
"namespace"_L1;
2411 case NodeType::Class:
2412 return plural ?
"classes"_L1 :
"class"_L1;
2413 case NodeType::Struct:
2414 return plural ?
"structs"_L1 :
"struct"_L1;
2415 case NodeType::Union:
2416 return plural ?
"unions"_L1 :
"union"_L1;
2417 case NodeType::QmlType:
2418 case NodeType::QmlValueType:
2419 return plural ?
"types"_L1 :
"type"_L1;
2420 case NodeType::Page:
2421 return "documentation"_L1;
2422 case NodeType::Enum:
2423 return plural ?
"enums"_L1 :
"enum"_L1;
2424 case NodeType::Typedef:
2425 case NodeType::TypeAlias:
2426 return plural ?
"typedefs"_L1 :
"typedef"_L1;
2428 const auto fn =
static_cast<
const FunctionNode *>(node);
2429 switch (fn->metaness()) {
2430 case Metaness::QmlSignal:
2431 return plural ?
"signals"_L1 :
"signal"_L1;
2432 case Metaness::QmlSignalHandler:
2433 return plural ?
"signal handlers"_L1 :
"signal handler"_L1;
2434 case Metaness::QmlMethod:
2435 return plural ?
"methods"_L1 :
"method"_L1;
2436 case Metaness::MacroWithParams:
2437 case Metaness::MacroWithoutParams:
2438 return plural ?
"macros"_L1 :
"macro"_L1;
2442 return plural ?
"functions"_L1 :
"function"_L1;
2444 case NodeType::Property:
2445 case NodeType::QmlEnum:
2446 return plural ?
"enumerations"_L1 :
"enumeration"_L1;
2447 case NodeType::QmlProperty:
2448 return plural ?
"properties"_L1 :
"property"_L1;
2449 case NodeType::Module:
2450 case NodeType::QmlModule:
2451 return plural ?
"modules"_L1 :
"module"_L1;
2452 case NodeType::Variable:
2453 return plural ?
"variables"_L1 :
"variable"_L1;
2455 const auto *shared =
static_cast<
const SharedCommentNode *>(node);
2456 if (shared->isPropertyGroup())
2457 return plural ?
"property groups"_L1 :
"property group"_L1;
2458 const auto &collective = shared->collective();
2459 return collective.first()->nodeTypeString();
2462 return "documentation"_L1;
2468 Location::internalError(QStringLiteral(
"unknown atom type '%1' in %2 generator")
2469 .arg(atom->typeString(), format()));
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
2500
2501
2502
2503
2506 if (!cn || (cn->cmakeComponent().isEmpty() && cn->cmakePackage().isEmpty())) {
2510 const QString package =
2511 cn->cmakePackage().isEmpty() ?
"Qt" + QString::number(QT_VERSION_MAJOR) : cn->cmakePackage();
2513 QString findPackageText;
2514 if (cn->cmakeComponent().isEmpty()) {
2515 findPackageText =
"find_package(" + package +
" REQUIRED)";
2517 findPackageText =
"find_package(" + package +
" REQUIRED COMPONENTS " + cn->cmakeComponent() +
")";
2521 if (cn->cmakeTargetItem().isEmpty()) {
2522 if (cn->cmakeComponent().isEmpty()) {
2523 targetText = package +
"::" + package;
2525 targetText = package +
"::" + cn->cmakeComponent();
2528 targetText = cn->cmakeTargetItem();
2531 const QString targetLinkLibrariesText =
"target_link_libraries(mytarget PRIVATE " + targetText +
")";
2532 const QStringList cmakeInfo { findPackageText, targetLinkLibrariesText };
2534 return std::make_pair(findPackageText, targetLinkLibrariesText);
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547void Generator::addNodeLink(
Text &text,
const QString &nodeRef,
const QString &linkText) {
2555
2556
2557
2558
2559
2560
2561
2562
2566 Utilities::stringForNode(node),
2567 linkText.isEmpty() ? node->name() : linkText
2572
2573
2574
2575
2576
2577
2578
2584 QString className = func->parent()->name();
2585 QString functionName = func->name();
2586 QString typeList = func->parameters().generateTypeList();
2587 QString typeAndNameList = func->parameters().generateTypeAndNameList();
2588 QString nameList = func->parameters().generateNameList();
2589 QString objectName = generateObjectName(className);
2595 "// Connect using qOverload:\n"
2596 "connect(%1, qOverload<%2>(&%3::%4),\n"
2597 " receiver, &ReceiverClass::slot);\n\n"
2598 "// Or using a lambda:\n"
2599 "connect(%1, qOverload<%2>(&%3::%4),\n"
2600 " this, [](%5) { /* handle %4 */ });")
2601 .arg(objectName, typeList, className, functionName, typeAndNameList);
2604 "// Connect using qOverload:\n"
2605 "connect(sender, &SenderClass::signal,\n"
2606 " %1, qOverload<%2>(&%3::%4));\n\n"
2607 "// Or using a lambda as wrapper:\n"
2608 "connect(sender, &SenderClass::signal,\n"
2609 " %1, [receiver = %1](%5) { receiver->%4(%6); });")
2610 .arg(objectName, typeList, className, functionName, typeAndNameList, nameList);
2617
2618
2619
2622 QString name = className;
2624 if (name.startsWith(
'Q') && name.length() > 1)
2627 if (!name.isEmpty())
2628 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.
static bool useOutputSubdirs()
void generateNoexceptNote(const Node *node, CodeMarker *marker)
void unknownAtom(const Atom *atom)
QString generateObjectName(const QString &className)
Generates an appropriate object name for code snippets based on the class name.
virtual bool generateText(const Text &text, const Node *relative, CodeMarker *marker)
Generate the documentation for relative.
int appendSortedQmlNames(Text &text, const Node *base, const QStringList &knownTypes, const QList< Node * > &subs)
void generateLinkToExample(const ExampleNode *en, CodeMarker *marker, const QString &exampleUrl)
Generates an external link to the project folder for example node.
virtual void terminateGenerator()
QString generateOverloadSnippet(const FunctionNode *func)
Generates a contextual code snippet for connecting to an overloaded signal or slot.
static bool matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
void addImageToCopy(const ExampleNode *en, const ResolvedFile &resolved_file)
virtual void generateDocs()
Traverses the database recursively to generate all the documentation.
static bool appendTrademark(const Atom *atom)
Returns true if a trademark symbol should be appended to the output as determined by atom.
void generateEnumValuesForQmlReference(const Node *node, CodeMarker *marker)
virtual int skipAtoms(const Atom *atom, Atom::AtomType type) const
bool m_threeColumnEnumValueTable
QString linkForExampleFile(const QString &path, const QString &fileExt=QString()) const
Constructs an href link from an example file name, which is a path to the example file.
virtual void generateQmlTypePage(QmlTypeNode *, CodeMarker *)
void signatureList(const QList< Node * > &nodes, const Node *relative, CodeMarker *marker)
Generate a bullet list of function signatures.
void appendFullName(Text &text, const Node *apparentNode, const QString &fullName, const Node *actualNode)
static bool s_redirectDocumentationToDevNull
virtual void generateBody(const Node *node, CodeMarker *marker)
Generate the body of the documentation from the qdoc comment found with the entity represented by the...
void beginSubPage(const PageNode *node, const QString &fileName)
Creates the file named fileName in the output directory.
virtual void generatePageNode(PageNode *, CodeMarker *)
virtual ~Generator()
Destroys the generator after removing it from the list of output generators.
void generateSince(const Node *node, CodeMarker *marker)
QMap< QString, QString > & formattingLeftMap()
int appendSortedNames(Text &text, const ClassNode *classe, const QList< RelatedClass > &classes)
void endSubPage()
Flush the text stream associated with the subpage, and then pop it off the text stream stack and dele...
virtual void generateAddendum(const Node *node, Addendum type, CodeMarker *marker)
QString indent(int level, const QString &markedCode)
QString fileName(const Node *node, const QString &extension=QString()) const
If the node has a URL, return the URL as the file name.
virtual void generateAddendum(const Node *node, Addendum type, CodeMarker *marker, AdmonitionPrefix prefix)
static void resetUseOutputSubdirs()
bool generateComparisonTable(const Node *node)
Generates a table of comparison categories for node, combining both self-comparison (from \compares) ...
bool parseArg(const QString &src, const QString &tag, int *pos, int n, QStringView *contents, QStringView *par1=nullptr)
virtual void generateGenericCollectionPage(CollectionNode *, CodeMarker *)
virtual QString fileBase(const Node *node) const
virtual void initializeGenerator()
No-op base implementation.
void initializeTextOutput()
Resets the variables used during text output.
void generateRequiredLinks(const Node *node, CodeMarker *marker)
Generates either a link to the project folder for example node, or a list of links files/images if 'u...
static bool isIncluded(const InclusionPolicy &policy, const NodeContext &context)
static bool requiresDocumentation(const InclusionPolicy &policy, const NodeContext &context)
The Location class provides a way to mark a location in a file.
Location()
Constructs an empty location.
Interface implemented by Node subclasses that can refer to a C++ enum.
virtual const NativeEnum * nativeEnum() const =0
Encapsulates information about native (C++) enum values.
const EnumNode * enumNode() const
QString styleString() const
OpenedList(ListStyle style)
Represents an output directory that has been verified to exist.
const QString & path() const noexcept
Singleton registry for discovering output producers by format.
OutputProducer * producerForFormat(const QString &format) const
Returns the producer registered for format, or nullptr if none.
static OutputProducerRegistry & instance()
Returns the singleton registry instance.
A PageNode is a Node that generates a documentation page.
bool isAttribution() const
This class describes one instance of using the Q_PROPERTY macro.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
const CollectionNode * getModuleNode(const Node *relative)
Returns the collection node representing the module that relative node belongs to,...
Status
Specifies the status of the QQmlIncubator.
const Atom * firstAtom() const
#define CONFIG_REDIRECTDOCUMENTATIONTODEVNULL
#define CONFIG_AUTOLINKERRORS
#define CONFIG_EXTRAIMAGES
#define CONFIG_OUTPUTSUFFIXES
#define CONFIG_OUTPUTPREFIXES
#define CONFIG_PRELIMINARY
#define CONFIG_NOLINKERRORS
#define CONFIG_DESCRIPTION
#define CONFIG_EXAMPLESINSTALLPATH
#define CONFIG_PRODUCTNAME
#define CONFIG_QUOTINGINFORMATION
#define CONFIG_STYLESHEETS
#define CONFIG_IMAGESOUTPUTDIR
#define CONFIG_FORMATTING
QMultiMap< QString, QString > QStringMultiMap
Metaness
Specifies the kind of function a FunctionNode represents.
This namespace holds QDoc-internal utility methods.
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isExternalPage() const
Returns true if the node type is ExternalPage.
const Doc & doc() const
Returns a reference to the node's Doc data member.
bool isQmlNode() const
Returns true if this node's Genus value is QML.
virtual bool docMustBeGenerated() const
This function is called to perform a test to decide if the node must have documentation generated.
virtual bool isWrapper() const
Returns true if the node is a class node or a QML type node that is marked as being a wrapper class o...
bool isPrivate() const
Returns true if this node's access is Private.
bool isActive() const
Returns true if this node's status is Active.
bool isNamespace() const
Returns true if the node type is Namespace.
ComparisonCategory comparisonCategory() const
bool hasFileNameBase() const
Returns true if the node's file name base has been set.
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
bool isSharedCommentNode() const
Returns true if the node type is SharedComment.
bool isHeader() const
Returns true if the node type is HeaderFile.
NodeType nodeType() const override
Returns this node's type.
Genus genus() const override
Returns this node's Genus.
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
virtual bool isMacro() const
returns true if either FunctionNode::isMacroWithParams() or FunctionNode::isMacroWithoutParams() retu...
bool isEnumType() const
Returns true if the node type is Enum.
virtual Status status() const
Returns the node's status value.
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
virtual bool isAttached() const
Returns true if the QML property or QML method node is marked as attached.
Aggregate * parent() const
Returns the node's parent pointer.
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
static bool nodeNameLessThan(const Node *first, const Node *second)
Returns true if the node n1 is less than node n2.
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
bool isProxyNode() const
Returns true if the node type is Proxy.
const std::optional< RelaxedTemplateDeclaration > & templateDecl() const
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
ThreadSafeness threadSafeness() const
Returns the thread safeness value for whatever this node represents.
virtual bool isMarkedReimp() const
Returns true if the FunctionNode is marked as a reimplemented function.
bool isProperty() const
Returns true if the node type is Property.
NodeContext createContext() const
bool isModule() const
Returns true if the node type is Module.
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
bool isSharingComment() const
This function returns true if the node is sharing a comment with other nodes.
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
bool isPreliminary() const
Returns true if this node's status is Preliminary.
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
bool isQmlModule() const
Returns true if the node type is QmlModule.
bool isExample() const
Returns true if the node type is Example.
bool isIndexNode() const
Returns true if this node was created from something in an index file.
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Represents a file that is reachable by QDoc based on its current configuration.