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 base{node->name()};
317 if (base.endsWith(
".html"))
318 base.truncate(base.size() - 5);
322 base.append(
"-qmlmodule");
324 base.append(
"-module");
325 base.append(outputSuffix(node));
328 base.prepend(
"%1-"_L1.arg(s_project.toLower()));
329 base.append(
"-example");
333
334
335
336
337
338
339
340
342 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
345 base.prepend(
"%1%2-"_L1.arg(node->logicalModuleName(), outputSuffix(node)));
349 base.append(
"-%1-proxy"_L1.arg(node->tree()->physicalModuleName()));
352 const Node *p = node;
354 const Node *pp = p->parent();
355 base.prepend(p->name());
356 if (pp ==
nullptr || pp->name().isEmpty() || pp->isTextPageNode())
358 base.prepend(
'-'_L1);
361 if (node->isNamespace() && !node->name().isEmpty()) {
362 const auto *ns =
static_cast<
const NamespaceNode *>(node);
363 if (!ns->isDocumentedHere()) {
364 base.append(QLatin1String(
"-sub-"));
365 base.append(ns->tree()->camelCaseModuleName());
368 base.append(outputSuffix(node));
371 base.prepend(outputPrefix(node));
372 QString canonicalName{ Utilities::asAsciiPrintable(base) };
374 n->setFileNameBase(canonicalName);
375 return canonicalName;
379
380
381
382
383
386 return Utilities::linkForExampleFile(path, s_project, fileExt.isEmpty() ? fileExtension() : fileExt);
390
391
392
395 return Utilities::exampleFileTitle(relative->files(), relative->images(), fileName);
399
400
401
402
403
406 if (!node->url().isEmpty())
413 QFileInfo originalName(node->name());
414 QString suffix = originalName.suffix();
415 if (!suffix.isEmpty() && suffix !=
"html") {
417 QString name = fileBase(node);
418 return name + QLatin1Char(
'.') + suffix;
422 QString name = fileBase(node) + QLatin1Char(
'.');
423 return name + (extension.isNull() ? fileExtension() : extension);
427
428
429
430
431
432
433
434
435QString
Generator::cleanRef(
const QString &ref,
bool xmlCompliant)
446 clean.reserve(ref.size() + 20);
447 const QChar c = ref[0];
448 const uint u = c.unicode();
450 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (!xmlCompliant && u >=
'0' && u <=
'9')) {
452 }
else if (xmlCompliant && u >=
'0' && u <=
'9') {
453 clean += QLatin1Char(
'A') + c;
454 }
else if (u ==
'~') {
456 }
else if (u ==
'_') {
457 clean +=
"underscore.";
459 clean += QLatin1Char(
'A');
462 for (
int i = 1; i < ref.size(); i++) {
463 const QChar c = ref[i];
464 const uint u = c.unicode();
465 if ((u >=
'a' && u <=
'z') || (u >=
'A' && u <=
'Z') || (u >=
'0' && u <=
'9') || u ==
'-'
466 || u ==
'_' || (xmlCompliant && u ==
':') || u ==
'.') {
468 }
else if (c.isSpace()) {
469 clean += QLatin1Char(
'-');
470 }
else if (u ==
'!') {
472 }
else if (u ==
'&') {
474 }
else if (u ==
'<') {
476 }
else if (u ==
'=') {
478 }
else if (u ==
'>') {
480 }
else if (u ==
'#') {
481 clean += QLatin1Char(
'#');
483 clean += QLatin1Char(
'-');
484 clean += QString::number(
static_cast<
int>(u), 16);
492 return s_fmtLeftMaps[format()];
497 return s_fmtRightMaps[format()];
501
502
507 if (!node->url().isEmpty())
515
516
517
518 if (!fileBase(node).isEmpty())
519 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
523 return fileBase(node) + QLatin1Char(
'.') + fileExtension();
525 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
526 }
else if (fileBase(node).isEmpty())
529 Node *parentNode =
nullptr;
533 if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
534 parentName = fullDocumentLocation(node->parent());
538 case NodeType::Class:
539 case NodeType::Struct:
540 case NodeType::Union:
541 case NodeType::Namespace:
542 case NodeType::Proxy:
543 parentName = fileBase(node) + QLatin1Char(
'.') + fileExtension();
546 const auto *fn =
static_cast<
const FunctionNode *>(node);
547 switch (fn->metaness()) {
549 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal";
552 anchorRef = QLatin1Char(
'#') + node->name() +
"-signal-handler";
555 anchorRef = QLatin1Char(
'#') + node->name() +
"-method";
559 anchorRef =
"#dtor." + fn->name().mid(1);
560 else if (
const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty())
561 return fullDocumentLocation(p);
562 else if (fn->overloadNumber() > 0)
563 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name()) + QLatin1Char(
'-')
564 + QString::number(fn->overloadNumber());
566 anchorRef = QLatin1Char(
'#') + cleanRef(fn->name());
572
573
574
575
578 anchorRef = QLatin1Char(
'#') + node->name() +
"-enum";
581 const auto *tdef =
static_cast<
const TypedefNode *>(node);
582 if (tdef->associatedEnum())
583 return fullDocumentLocation(tdef->associatedEnum());
586 anchorRef = QLatin1Char(
'#') + node->name() +
"-typedef";
589 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
597 anchorRef = QLatin1Char(
'#') + node->name() +
"-attached-prop";
599 anchorRef = QLatin1Char(
'#') + node->name() +
"-prop";
602 anchorRef = QLatin1Char(
'#') + node->name() +
"-var";
610 parentName = fileBase(node);
611 parentName.replace(QLatin1Char(
'/'), QLatin1Char(
'-'))
612 .replace(QLatin1Char(
'.'), QLatin1Char(
'-'));
613 parentName += QLatin1Char(
'.') + fileExtension();
621 parentName.replace(QLatin1Char(
'.') + fileExtension(),
622 "-obsolete." + fileExtension());
625 return parentName.toLower() + anchorRef;
629
630
631
632
633
634
635
638 QList<Text> alsoList = node
->doc().alsoList();
639 supplementAlsoList(node, alsoList);
641 if (!alsoList.isEmpty()) {
648 for (
const auto &also : std::as_const(alsoList)) {
650 const Atom *atom = also.firstAtom();
651 QString link = atom->string();
652 if (!used.contains(link)) {
657 if (m_qdb->findNodeForAtom(atom, node, ref) == node && ref.isEmpty())
658 node->doc().location().warning(
"Redundant link to self in \\sa command for %1"_L1.arg(node->name()));
663 for (
const auto &also : std::as_const(items))
664 text << also << Utilities::separator(i++, items.size());
672 bool generate,
int &numAtoms)
674 while (atom !=
nullptr) {
676 int numAtoms0 = numAtoms;
677 bool rightFormat = canHandleFormat(atom->string());
692 if (generate && numAtoms0 == numAtoms) {
693 relative
->location().warning(QStringLiteral(
"Output format %1 not handled %2")
694 .arg(format(), outFileName()));
695 Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
705 n += generateAtom(atom, relative, marker);
717
718
719
722 const FunctionNode *fn = node->isFunction() ?
static_cast<
const FunctionNode *>(node) :
nullptr;
725
726
727
731 text <<
"Destroys the instance of ";
732 text << fn
->parent()->name() <<
".";
734 text <<
" The destructor is virtual.";
740 text <<
"Default-constructs an instance of "
747 text <<
"Copy-constructs an instance of "
754 text <<
"Move-constructs an instance of "
761 text <<
"Copy-assigns "
764 <<
" to this " << fn
->parent()->name() <<
" instance.";
770 text <<
"Move-assigns "
773 <<
" to this " << fn
->parent()->name() <<
" instance.";
778 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
781 node
->location().warning(QStringLiteral(
"No documentation for '%1'")
782 .arg(node->plainSignature()));
786 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
790 QStringLiteral(
"No documentation for '%1'").arg(node->plainSignature()));
795 generateReimplementsClause(fn, marker);
797 if (
static_cast<
const PropertyNode *>(node)->propertyType() != PropertyNode::PropertyType::StandardProperty)
822 const auto *enume =
static_cast<
const EnumNode *>(node);
824 QSet<QString> definedItems;
825 const QList<EnumItem> &items = enume->items();
826 for (
const auto &item : items)
827 definedItems.insert(item.name());
829 const auto &documentedItemList = enume->doc().enumItemNames();
830 QSet<QString> documentedItems(documentedItemList.cbegin(), documentedItemList.cend());
831 const QSet<QString> allItems = definedItems + documentedItems;
832 if (allItems.size() > definedItems.size()
833 || allItems.size() > documentedItems.size()) {
834 for (
const auto &it : allItems) {
835 if (!definedItems.contains(it)) {
836 node->doc().location().warning(
837 QStringLiteral(
"No such enum item '%1' in %2")
838 .arg(it, node->plainFullName()),
839 QStringLiteral(
"Maybe you meant '%1'?")
840 .arg(suggestName(it, definedItems, documentedItems)));
841 }
else if (!documentedItems.contains(it)) {
842 node->doc().location().warning(
843 QStringLiteral(
"Undocumented enum item '%1' in %2")
844 .arg(it, node->plainFullName()));
857 const QSet<QString> requiredFunctionParams = fn
->parameters().getNames();
858 const QSet<QString> requiredTemplateParams = fn->templateDecl()
859 ? fn->templateDecl()->requiredParameterNamesForFunctions()
861 const QSet<QString> requiredNames = requiredFunctionParams + requiredTemplateParams;
865 const QSet<QString> ownTemplateParams = fn->templateDecl()
866 ? fn->templateDecl()->parameterNames()
868 const QSet<QString> allowedNames = requiredNames + ownTemplateParams
869 + inheritedTemplateParamNames(fn);
871 const QSet<QString> documentedNames = fn
->doc().parameterNames();
874 for (
const auto &name : requiredNames) {
875 if (!documentedNames.contains(name)) {
876 if (fn->isActive() || fn->isPreliminary()) {
879 if (!fn->isMarkedReimp() && !fn->isOverload()
880 && !(fn->isSomeCtor() && fn->hasOverloads())) {
882 const bool isTemplateParam = requiredTemplateParams.contains(name);
883 fn->doc().location().warning(
884 "Undocumented %1 '%2' in %3"_L1
885 .arg(isTemplateParam ?
"template parameter"_L1
887 name, node->plainFullName()));
893 warnAboutUnknownDocumentedParams(fn, documentedNames, allowedNames,
896
897
898
899
902 if (!fn
->doc().body().contains(
"return"))
904 QStringLiteral(
"Undocumented return value "
905 "(hint: use 'return' or 'returns' in the text"));
908 if (
auto *qpn =
static_cast<
const QmlPropertyNode *>(node); !qpn->validateDataType())
909 qpn->doc().location().warning(
"Invalid QML property type: %1"_L1.arg(qpn->dataType()));
915 const QSet<QString> requiredNames = node->templateDecl()->parameterNames();
916 const QSet<QString> allowedNames = requiredNames + inheritedTemplateParamNames(node);
917 const QSet<QString> documentedNames = node
->doc().parameterNames();
920 for (
const auto &name : requiredNames) {
921 if (!documentedNames.contains(name) && CodeParser::isWorthWarningAbout(node->doc())) {
922 node->doc().location().warning(
923 "Undocumented template parameter '%1' in %2"_L1
924 .arg(name, node->plainFullName()));
929 warnAboutUnknownDocumentedParams(node, documentedNames, allowedNames,
938
939
940
941
942
948 const auto *en =
static_cast<
const ExampleNode *>(node);
951 if (exampleUrl.isEmpty()) {
952 if (!en->noAutoList()) {
957 generateLinkToExample(en, marker, exampleUrl);
962
963
964
965
966
967
969 const QString &baseUrl)
971 QString exampleUrl(baseUrl);
973#ifndef QT_BOOTSTRAPPED
974 link = QUrl(exampleUrl).host();
978 link.prepend(
"Example project");
980 const QLatin1Char separator(
'/');
981 const QLatin1Char placeholder(
'\1');
982 if (!exampleUrl.contains(placeholder)) {
983 if (!exampleUrl.endsWith(separator))
984 exampleUrl += separator;
985 exampleUrl += placeholder;
992 pathRoot = metaTagMap->value(QLatin1String(
"installpath"));
993 if (pathRoot.isEmpty())
995 QStringList path = QStringList() << pathRoot << en->name();
996 path.removeAll(QString());
1000 <<
Atom(
Atom::Link, exampleUrl.replace(placeholder, path.join(separator)))
1010 const QString prefix(
"/images/used-in-examples");
1016 s_outFileNames << prefix.mid(1) +
"/" + resolved_file.get_query();
1019 OutputDirectory::ensure(s_outDir, en
->location());
1021 outDir.ensureSubdir(prefix.mid(1), en
->location());
1023 const QFileInfo fi{resolved_file.get_query()};
1024 const QString relativePath = fi.path();
1026 const bool hasSubdir = !relativePath.isEmpty() && relativePath !=
"."_L1;
1028 hasSubdir ? imagesUsedInExamplesDir.ensureSubdir(relativePath, en
->location())
1029 : imagesUsedInExamplesDir;
1031 const QString fileName = fi.fileName();
1047
1048
1049
1050
1051
1052
1062 paths = en->images();
1066 paths = en->files();
1069 std::sort(paths.begin(), paths.end(), Generator::comparePaths);
1074 for (
const auto &path : std::as_const(paths)) {
1075 auto maybe_resolved_file{file_resolver.resolve(path)};
1076 if (!maybe_resolved_file) {
1078 QString details = std::transform_reduce(
1079 file_resolver.get_search_directories().cbegin(),
1080 file_resolver.get_search_directories().cend(),
1081 u"Searched directories:"_s,
1083 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
1086 en->location().warning(u"(Generator)Cannot find file to quote from: %1"_s.arg(path), details);
1091 const auto &file{*maybe_resolved_file};
1093 addImageToCopy(en, file);
1095 generateExampleFilePage(en, file, marker);
1098 text << Atom(Atom::ListItemNumber, openedList.numberString())
1099 << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
1100 << Atom(atomType, file.get_query()) << Atom(Atom::FormattingLeft,
ATOM_FORMATTING_LINK) << file.get_query()
1102 << Atom(Atom::ListItemRight, openedList.styleString());
1105 if (!paths.isEmpty())
1106 generateText(text, en, marker);
1110
1111
1114 if (!node->url().isNull())
1118 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1126
1127
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151 auto *cn =
static_cast<CollectionNode *>(node);
1152 if (cn->wasSeen()) {
1153 m_qdb->mergeCollections(cn);
1154 beginSubPage(node, fileName(node));
1157 }
else if (cn->isGenericCollection()) {
1161 QString name = cn->name().toLower();
1162 name.replace(QChar(
' '), QString(
"-"));
1164 cn->tree()->physicalModuleName() +
"-" + name +
"." + fileExtension();
1165 beginSubPage(node, filename);
1170 beginSubPage(node, fileName(node));
1176 beginSubPage(node, fileName(node));
1180 beginSubPage(node, fileName(node));
1181 auto *qcn =
static_cast<QmlTypeNode *>(node);
1185 beginSubPage(node, fileName(node));
1193 auto *aggregate =
static_cast<Aggregate *>(node);
1194 const NodeList &children = aggregate->childNodes();
1195 for (
auto *child : children) {
1196 if (child->isPageNode()) {
1197 generateDocumentation(child);
1198 }
else if (!node->parent() && child->isInAPI() && !child->isRelatedNonmember()
1199 && !child->doc().isAutoGenerated()) {
1201 child->location().warning(u"No documentation generated for %1 '%2' in global scope."_s
1202 .arg(typeString(child), child->name()),
1203 u"Maybe you forgot to use the '\\relates' command?"_s);
1204 child->setStatus(Status::DontDocument);
1205 }
else if (child->isQmlModule() && !child->wasSeen()) {
1207 auto *qmlModule =
static_cast<CollectionNode *>(child);
1208 for (
const auto *member : qmlModule->members()) {
1209 member->location().warning(
1210 u"Undocumented QML module '%1' referred by type '%2' or its members"_s
1211 .arg(qmlModule->name(), member->name()),
1212 u"Maybe you forgot to document '\\qmlmodule %1'?"_s
1213 .arg(qmlModule->name()));
1215 }
else if (child->isQmlType() && !child->hasDoc()) {
1217 auto *qmlType =
static_cast<QmlTypeNode *>(child);
1218 if (
auto qmid = qmlType->logicalModuleName(); !qmid.isEmpty())
1219 qmlType->location().warning(u"No such type '%1' in QML module '%2'"_s
1220 .arg(qmlType->name(), qmid));
1232 const FunctionNode *overrides = cn->findOverriddenFunction(fn);
1238 overrides->parent()->name()
1239 +
"::" + overrides->signature(Node::SignaturePlain);
1242 generateText(text, fn, marker);
1244 fn
->doc().location().warning(
1245 QStringLiteral(
"Illegal \\reimp; no documented virtual function for %1")
1246 .arg(overrides->plainSignature()));
1250 const PropertyNode *sameName = cn->findOverriddenProperty(fn);
1251 if (sameName && sameName
->hasDoc()) {
1253 text <<
Atom::ParaLeft <<
"Reimplements an access function for property: ";
1254 QString fullName = sameName->parent()->name() +
"::" + sameName->name();
1257 generateText(text, fn, marker);
1263 QStringList since = node->since().split(QLatin1Char(
' '));
1266 if (since.size() == 1) {
1268 return productName.isEmpty() ? node->since() : productName +
" " + since[0];
1272 return node->since();
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1298 status = metaMap->value(
"status");
1299 if (!status.isEmpty())
1302 const auto &since = node->deprecatedSince();
1304 status = u"Deprecated"_s;
1305 if (!since.isEmpty())
1306 status +=
" since %1"_L1.arg(since);
1307 }
else if (!since.isEmpty()) {
1308 status =
"Until %1"_L1.arg(since);
1312 status = collection->state();
1315 return status.isEmpty() ?
std::nullopt :
std::optional(status);
1320 if (!node->since().isEmpty()) {
1323 const auto &collective =
static_cast<
const SharedCommentNode *>(node)->collective();
1324 QString typeStr = typeString(collective.first(), collective.size() > 1);
1325 text << Atom::ParaLeft <<
"These " << typeStr <<
" were introduced in "
1326 << formatSince(node) <<
"." << Atom::ParaRight;
1328 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" was introduced in "
1329 << formatSince(node) <<
"." << Atom::ParaRight;
1336 std::vector<
const Node*> nodes;
1338 auto shared_node =
static_cast<
const SharedCommentNode*>(node);
1339 nodes.reserve(shared_node->collective().size());
1340 nodes.insert(nodes.begin(), shared_node->collective().begin(), shared_node->collective().end());
1341 }
else nodes.push_back(node);
1343 std::size_t counter{1};
1344 for (
const Node* node : nodes) {
1345 if (node->isFunction(Genus::CPP)) {
1346 if (
const auto &exception_info =
static_cast<
const FunctionNode*>(node)->getNoexcept(); exception_info && !(*exception_info).isEmpty()) {
1348 text << Atom::NoteLeft
1349 << (nodes.size() > 1 ? QString::fromStdString(
" ("s + std::to_string(counter) +
")"s) : QString::fromStdString(
"This ") + typeString(node))
1350 <<
" is noexcept when "
1351 << Atom(Atom::C, marker->markedUpCode(*exception_info,
nullptr, Location()))
1352 <<
" is " << Atom(Atom::C,
"true") <<
"."
1354 generateText(text, node, marker);
1370 const QString &state =
static_cast<
const CollectionNode*>(node)->state();
1371 if (!state.isEmpty()) {
1372 text << Atom::ParaLeft <<
"This " << typeString(node) <<
" is in "
1379 if (
const auto &version = node->deprecatedSince(); !version.isEmpty()) {
1380 text << Atom::ParaLeft <<
"This " << typeString(node)
1381 <<
" is scheduled for deprecation in version "
1382 << version <<
"." << Atom::ParaRight;
1386 auto description = Config::instance()
1389 description.replace(
'\1'_L1, typeString(node));
1398 text <<
"This " << typeString(node) <<
" is deprecated";
1399 if (
const QString &version = node->deprecatedSince(); !version.isEmpty()) {
1401 if (node
->isQmlNode() && !node->logicalModuleName().isEmpty())
1402 text << node->logicalModuleName() <<
" ";
1406 text <<
". We strongly advise against using it in new code.";
1414 <<
"Part of developer documentation for internal use."
1424
1425
1426
1430 Q_ASSERT(node && !node->name().isEmpty());
1432 text << Atom(Atom::DivLeft,
1433 "class=\"admonition %1\""_L1.arg(prefix == AdmonitionPrefix::Note ? u"note"_s : u"auto"_s));
1448 text <<
"This function can be invoked via the meta-object system and from QML. See "
1454 text <<
"This is a private signal. It can be used in signal connections "
1455 "but cannot be emitted by the user.";
1459 QString handler(node->name());
1460 qsizetype prefixLocation = handler.lastIndexOf(
'.', -2) + 1;
1461 handler[prefixLocation] = handler[prefixLocation].toTitleCase();
1462 handler.insert(prefixLocation, QLatin1String(
"on"));
1463 text <<
"The corresponding handler is "
1472 const auto *fn =
static_cast<
const FunctionNode *>(node);
1473 auto nodes = fn->associatedProperties();
1474 if (nodes.isEmpty())
1479 QMap<PropertyNode::FunctionRole, QList<
const PropertyNode *>> roleGroups;
1480 for (
const auto *n : std::as_const(nodes)) {
1481 const auto *pn =
static_cast<
const PropertyNode *>(n);
1482 if (pn->isInAPI()) {
1483 PropertyNode::FunctionRole role = pn->role(fn);
1484 roleGroups[role].append(pn);
1488 if (roleGroups.isEmpty())
1499 for (
auto role : roleOrder) {
1500 const auto it = roleGroups.constFind(role);
1501 if (it == roleGroups.cend())
1504 const auto &properties = it.value();
1508 case PropertyNode::FunctionRole::Getter:
1509 msg = u"Getter function"_s;
1511 case PropertyNode::FunctionRole::Setter:
1512 msg = u"Setter function"_s;
1514 case PropertyNode::FunctionRole::Resetter:
1515 msg = u"Resetter function"_s;
1517 case PropertyNode::FunctionRole::Notifier:
1518 msg = u"Notifier signal"_s;
1520 case PropertyNode::FunctionRole::Bindable:
1521 msg = u"Bindable function"_s;
1527 if (properties.size() == 1) {
1528 const auto *pn = properties.first();
1529 text << msg << u" for property "_s << Atom(Atom::Link, pn->name())
1533 text << msg << u" for properties "_s;
1534 for (qsizetype i = 0; i < properties.size(); ++i) {
1535 const auto *pn = properties.at(i);
1539 << Utilities::separator(i, properties.size());
1548 text <<
"This property supports "
1552 text <<
" bindings.";
1557 const auto *func =
static_cast<
const FunctionNode *>(node);
1560 if (func->isPrimaryOverload())
1563 if (func->isSignal() || func->isSlot()) {
1564 QString functionType = func->isSignal() ?
"signal" :
"slot";
1565 const QString &configKey = func->isSignal() ?
"overloadedsignalstarget" :
"overloadedslotstarget";
1566 const QString &defaultTarget = func->isSignal() ?
"connecting-overloaded-signals" :
"connecting-overloaded-slots";
1567 const QString &linkTarget = Config::instance().get(configKey).asString(defaultTarget);
1569 text <<
"This " << functionType <<
" is overloaded. ";
1571 QString snippet = generateOverloadSnippet(func);
1572 if (!snippet.isEmpty()) {
1573 text <<
"To connect to this " << functionType <<
":\n\n"
1577 if (!linkTarget.isEmpty()) {
1578 text <<
"For more examples and approaches, see "
1581 <<
"connecting to overloaded " << functionType <<
"s"
1585 const auto &args = node
->doc().overloadList();
1586 if (args.first().first.isEmpty()) {
1587 text <<
"This is an overloaded function.";
1589 QString target = args.first().first;
1592 if (!target.contains(
"::")) {
1595 target = parent->name() +
"::" + target;
1612
1613
1614
1615
1618 bool result =
false;
1629
1630
1631
1632
1633
1634
1635
1636
1640 bool result =
false;
1642 const NodeList &children =
static_cast<
const Aggregate *>(node)->childNodes();
1643 for (
auto child : children) {
1644 if (!child->isDeprecated()) {
1645 switch (child->threadSafeness()) {
1646 case Node::Reentrant:
1647 reentrant.append(child);
1648 if (ts == Node::ThreadSafe)
1651 case Node::ThreadSafe:
1652 threadsafe.append(child);
1653 if (ts == Node::Reentrant)
1656 case Node::NonReentrant:
1657 nonreentrant.append(child);
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1689 if (atom->count() > 1) {
1690 if (s_trademarks.contains(atom->string(1)))
1692 s_trademarks << atom->string(1);
1705
1706
1707
1710 Text text, rlink, tlink;
1715 bool exceptions =
false;
1726 case Node::NonReentrant:
1729 << typeString(node) <<
" is not " << rlink <<
"." << Atom::ParaRight;
1735 exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
1736 text <<
"All functions in this " << typeString(node) <<
" are ";
1745 text <<
" with the following exceptions:";
1747 text <<
"This " << typeString(node) <<
" is ";
1764 if (!nonreentrant.isEmpty()) {
1766 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1767 signatureList(nonreentrant, node, marker);
1769 if (!threadsafe.isEmpty()) {
1772 text <<
"These functions are also " << tlink <<
":" <<
Atom::ParaRight;
1774 signatureList(threadsafe, node, marker);
1777 if (!reentrant.isEmpty()) {
1779 text <<
"These functions are only " << rlink <<
":" <<
Atom::ParaRight;
1780 signatureList(reentrant, node, marker);
1782 if (!nonreentrant.isEmpty()) {
1785 text <<
"These functions are not " << rlink <<
":" <<
Atom::ParaRight;
1786 signatureList(nonreentrant, node, marker);
1793
1794
1795
1796
1797
1801 if (category == ComparisonCategory::None)
1805 text << Atom::ParaLeft <<
"%1 is "_L1.arg(node->plainFullName())
1807 << QString::fromStdString(comparisonCategoryAsString(category))
1808 << ((category == ComparisonCategory::Equality) ?
"-"_L1 :
"ly "_L1)
1809 << Atom(Atom::String,
"comparable"_L1)
1811 <<
"."_L1 << Atom::ParaRight;
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1835 const auto *map = node
->doc().comparesWithMap();
1837 const bool hasSelfComparison = (selfCategory != ComparisonCategory::None);
1838 const bool hasComparesWithEntries = (map && !map->isEmpty());
1840 if (!hasSelfComparison && !hasComparesWithEntries)
1843 bool hasDescriptions =
false;
1844 if (hasComparesWithEntries) {
1845 for (
const auto &description : *map) {
1846 if (description.firstAtom()->next() != description.lastAtom()) {
1847 hasDescriptions =
true;
1855 text << Atom::ParaLeft
1857 <<
"%1 Comparisons"_L1.arg(node->plainFullName())
1861 text << Atom(Atom::TableLeft,
"generic"_L1);
1863 text << Atom::TableHeaderLeft
1864 << Atom::TableItemLeft <<
"Category"_L1 << Atom::TableItemRight
1865 << Atom::TableItemLeft <<
"Comparable Types"_L1 << Atom::TableItemRight;
1866 if (hasDescriptions)
1867 text << Atom::TableItemLeft <<
"Description"_L1 << Atom::TableItemRight;
1871 if (hasSelfComparison) {
1872 const QString &category = QString::fromStdString(comparisonCategoryAsString(selfCategory));
1878 if (hasDescriptions)
1884 if (hasComparesWithEntries) {
1885 for (
auto [key, description] : map->asKeyValueRange()) {
1886 const QString &category = QString::fromStdString(comparisonCategoryAsString(key));
1888 text << Atom::TableRowLeft;
1890 text << Atom::TableItemLeft << category << Atom::TableItemRight;
1892 text << Atom::TableItemLeft;
1893 const QStringList types{description.firstAtom()->string().split(
';'_L1)};
1894 for (
const auto &name : types)
1895 text << Atom(Atom::AutoLink, name)
1896 << Utilities::separator(types.indexOf(name), types.size());
1897 text << Atom::TableItemRight;
1899 if (hasDescriptions) {
1900 text << Atom::TableItemLeft;
1901 if (description.firstAtom()->next() != description.lastAtom())
1902 text << Text::subText(description.firstAtom()->next(), description.lastAtom());
1903 text << Atom::TableItemRight;
1906 text << Atom::TableRowRight;
1917
1918
1921 s_currentGenerator =
this;
1927 for (
const auto &generator : std::as_const(s_generators)) {
1928 if (generator->format() == format)
1943 while (i < markedCode.size()) {
1944 if (markedCode.at(i) == QLatin1Char(
'\n')) {
1948 for (
int j = 0; j < level; j++)
1949 t += QLatin1Char(
' ');
1953 t += markedCode.at(i++);
1960 Config &config = Config::instance();
1961 s_outputFormats = config.getOutputFormats();
1964 for (
auto &g : s_generators) {
1965 if (s_outputFormats.contains(g->format())) {
1966 s_currentGenerator = g;
1967 OutputProducerRegistry::instance().registerProducer(g);
1968 g->initializeGenerator();
1973 for (
const auto &n : configFormatting) {
1975 const auto &formattingDotNames = config.subVars(formattingDotName);
1976 for (
const auto &f : formattingDotNames) {
1977 const auto &configVar = config.get(formattingDotName + Config::dot + f);
1978 QString def{configVar.asString()};
1979 if (!def.isEmpty()) {
1980 int numParams = Config::numParams(def);
1981 int numOccs = def.count(
"\1");
1982 if (numParams != 1) {
1983 configVar.location().warning(QStringLiteral(
"Formatting '%1' must "
1985 "parameter (found %2)")
1986 .arg(n, numParams));
1987 }
else if (numOccs > 1) {
1988 configVar.location().fatal(QStringLiteral(
"Formatting '%1' must "
1989 "contain exactly one "
1990 "occurrence of '\\1' "
1994 int paramPos = def.indexOf(
"\1");
1995 s_fmtLeftMaps[f].insert(n, def.left(paramPos));
1996 s_fmtRightMaps[f].insert(n, def.mid(paramPos + 1));
2003 s_outDir = config.getOutputDir();
2004 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
2006 s_outputPrefixes.clear();
2008 if (!items.isEmpty()) {
2009 for (
const auto &prefix : items)
2010 s_outputPrefixes[prefix] =
2013 if (!items.contains(u"QML"_s))
2014 s_outputPrefixes[u"QML"_s] = u"qml-"_s;
2016 s_outputSuffixes.clear();
2019 + Config::dot + suffix).asString();
2026
2027
2028
2029void Generator::copyTemplateFiles(
const QString &configVar,
const QString &subDir)
2046 Config &config = Config::instance();
2047 QStringList files = config.getCanonicalPathList(configVar,
Config::Validate);
2048 const auto &loc = config.get(configVar)
.location();
2049 if (!files.isEmpty()) {
2059 OutputDirectory::ensure(s_outDir, loc);
2062 outDir.ensureSubdir(subDir, loc);
2064 for (
const auto &file : files) {
2065 if (!file.isEmpty()) {
2066 const QFileInfo fi(file);
2067 Config::copyFile(loc, fi.absoluteFilePath(), fi.fileName(), templateDir.path());
2074
2075
2076
2077
2080 Config &config = Config::instance();
2081 s_outFileNames.clear();
2082 s_useOutputSubdirs =
true;
2083 if (config.get(format() + Config::dot +
"nosubdirs").asBool())
2086 if (s_outputFormats.isEmpty())
2091 s_outDir = config.getOutputDir(format());
2092 if (s_outDir.isEmpty()) {
2093 Location().fatal(QStringLiteral(
"No output directory specified in "
2094 "configuration file or on the command line"));
2096 s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf(
'/') + 1);
2101 OutputDirectory::ensure(s_outDir,
Location());
2105 if (!outputDir.toQDir().isEmpty())
2106 Location().error(
"Output directory '%1' exists but is not empty"_L1.arg(s_outDir));
2116 outputDir.ensureSubdir(imagesDir,
Location());
2117 s_imagesOutDir =
std::move(imagesDir);
2120 copyTemplateFiles(format() + Config::dot +
CONFIG_SCRIPTS,
"scripts");
2131
2132
2133
2145
2146
2147
2148
2151 return *outStreamStack.top();
2156 return QFileInfo(
static_cast<QFile *>(
out().device())->fileName()).fileName();
2165 return s_outputPrefixes[u"QML"_s];
2167 return s_outputPrefixes[u"CPP"_s];
2180 return s_outputSuffixes[u"QML"_s];
2182 return s_outputSuffixes[u"CPP"_s];
2192 QStringView *contents, QStringView *par1)
2195 if (i >= n || src[i] != c)
2200 while (i < n && src[i] == ' ')
2210 if (tag != QStringView(src).mid(i, tag.size())) {
2222 while (i < n && src[i].isLetter())
2224 if (src[i] ==
'=') {
2229 while (i < n && src[i] !=
'"')
2231 *par1 = QStringView(src).mid(j, i - j);
2242 if (i + 4 + tag.size() > n)
2246 if (src[i + 1] !=
'/')
2248 if (src[i + 2] !=
'@')
2250 if (tag != QStringView(src).mid(i + 3, tag.size()))
2252 if (src[i + 3 + tag.size()] !=
'>')
2257 *contents = QStringView(src).mid(j, i - j);
2259 i += tag.size() + 4;
2269 QString t = markedCode;
2270 t.replace(tag, QString());
2271 t.replace(quot, QLatin1String(
"\""));
2272 t.replace(gt, QLatin1String(
">"));
2273 t.replace(lt, QLatin1String(
"<"));
2274 t.replace(amp, QLatin1String(
"&"));
2282 while (atom && atom
->type() != type) {
2290
2291
2301 m_sectionNumber.clear();
2307 const auto fn =
static_cast<
const FunctionNode *>(node);
2308 if (fn->overloadNumber() == 0) {
2309 QString alternateName;
2312 if (fn->name().startsWith(
"set") && fn->name().size() >= 4) {
2313 alternateName = fn->name()[3].toLower();
2314 alternateName += fn->name().mid(4);
2315 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2317 if (!alternateFunc) {
2318 alternateName =
"is" + fn->name().mid(3);
2319 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2320 if (!alternateFunc) {
2321 alternateName =
"has" + fn->name().mid(3);
2322 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2325 }
else if (!fn->name().isEmpty()) {
2326 alternateName =
"set";
2327 alternateName += fn->name()[0].toUpper();
2328 alternateName += fn->name().mid(1);
2329 alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
2334 for (i = 0; i < alsoList.size(); ++i) {
2335 if (alsoList.at(i).toString().contains(alternateName))
2339 if (i == alsoList.size()) {
2342 alternateName +=
"()";
2348 alsoList.prepend(also);
2368 const auto *start{body.firstAtom()};
2374 text << body.subText(text
.isEmpty() ? start : start->next(), end);
2389 for (
const auto &generator : std::as_const(s_generators)) {
2390 if (s_outputFormats.contains(generator->format())) {
2391 OutputProducerRegistry::instance().unregisterProducer(generator);
2392 generator->terminateGenerator();
2419 s_generators.clear();
2421 s_fmtLeftMaps.clear();
2422 s_fmtRightMaps.clear();
2424 s_imagesOutDir.clear();
2430
2431
2432
2433QString
Generator::trimmedTrailing(
const QString &string,
const QString &prefix,
2434 const QString &suffix)
2436 QString trimmed = string;
2437 while (trimmed.size() > 0 && trimmed[trimmed.size() - 1].isSpace())
2438 trimmed.truncate(trimmed.size() - 1);
2440 trimmed.append(suffix);
2441 trimmed.prepend(prefix);
2448 case NodeType::Namespace:
2449 return plural ?
"namespaces"_L1 :
"namespace"_L1;
2450 case NodeType::Class:
2451 return plural ?
"classes"_L1 :
"class"_L1;
2452 case NodeType::Struct:
2453 return plural ?
"structs"_L1 :
"struct"_L1;
2454 case NodeType::Union:
2455 return plural ?
"unions"_L1 :
"union"_L1;
2456 case NodeType::QmlType:
2457 case NodeType::QmlValueType:
2458 return plural ?
"types"_L1 :
"type"_L1;
2459 case NodeType::Page:
2460 return "documentation"_L1;
2461 case NodeType::Enum:
2462 return plural ?
"enums"_L1 :
"enum"_L1;
2463 case NodeType::Typedef:
2464 case NodeType::TypeAlias:
2465 return plural ?
"typedefs"_L1 :
"typedef"_L1;
2467 const auto fn =
static_cast<
const FunctionNode *>(node);
2468 switch (fn->metaness()) {
2469 case FunctionNode::QmlSignal:
2470 return plural ?
"signals"_L1 :
"signal"_L1;
2471 case FunctionNode::QmlSignalHandler:
2472 return plural ?
"signal handlers"_L1 :
"signal handler"_L1;
2473 case FunctionNode::QmlMethod:
2474 return plural ?
"methods"_L1 :
"method"_L1;
2475 case FunctionNode::MacroWithParams:
2476 case FunctionNode::MacroWithoutParams:
2477 return plural ?
"macros"_L1 :
"macro"_L1;
2481 return plural ?
"functions"_L1 :
"function"_L1;
2483 case NodeType::Property:
2484 case NodeType::QmlProperty:
2485 return plural ?
"properties"_L1 :
"property"_L1;
2486 case NodeType::Module:
2487 case NodeType::QmlModule:
2488 return plural ?
"modules"_L1 :
"module"_L1;
2489 case NodeType::Variable:
2490 return plural ?
"variables"_L1 :
"variable"_L1;
2492 const auto *shared =
static_cast<
const SharedCommentNode *>(node);
2493 if (shared->isPropertyGroup())
2494 return plural ?
"property groups"_L1 :
"property group"_L1;
2495 const auto &collective = shared->collective();
2496 return collective.first()->nodeTypeString();
2499 return "documentation"_L1;
2505 Location::internalError(QStringLiteral(
"unknown atom type '%1' in %2 generator")
2506 .arg(atom->typeString(), format()));
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2543 if (!cn || (cn->cmakeComponent().isEmpty() && cn->cmakePackage().isEmpty())) {
2547 const QString package =
2548 cn->cmakePackage().isEmpty() ?
"Qt" + QString::number(QT_VERSION_MAJOR) : cn->cmakePackage();
2550 QString findPackageText;
2551 if (cn->cmakeComponent().isEmpty()) {
2552 findPackageText =
"find_package(" + package +
" REQUIRED)";
2554 findPackageText =
"find_package(" + package +
" REQUIRED COMPONENTS " + cn->cmakeComponent() +
")";
2558 if (cn->cmakeTargetItem().isEmpty()) {
2559 if (cn->cmakeComponent().isEmpty()) {
2560 targetText = package +
"::" + package;
2562 targetText = package +
"::" + cn->cmakeComponent();
2565 targetText = cn->cmakeTargetItem();
2568 const QString targetLinkLibrariesText =
"target_link_libraries(mytarget PRIVATE " + targetText +
")";
2569 const QStringList cmakeInfo { findPackageText, targetLinkLibrariesText };
2571 return std::make_pair(findPackageText, targetLinkLibrariesText);
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584void Generator::addNodeLink(
Text &text,
const QString &nodeRef,
const QString &linkText) {
2592
2593
2594
2595
2596
2597
2598
2599
2603 Utilities::stringForNode(node),
2604 linkText.isEmpty() ? node->name() : linkText
2609
2610
2611
2612
2613
2614
2615
2621 QString className = func->parent()->name();
2622 QString functionName = func->name();
2623 QString typeList = func->parameters().generateTypeList();
2624 QString typeAndNameList = func->parameters().generateTypeAndNameList();
2625 QString nameList = func->parameters().generateNameList();
2626 QString objectName = generateObjectName(className);
2632 "// Connect using qOverload:\n"
2633 "connect(%1, qOverload<%2>(&%3::%4),\n"
2634 " receiver, &ReceiverClass::slot);\n\n"
2635 "// Or using a lambda:\n"
2636 "connect(%1, qOverload<%2>(&%3::%4),\n"
2637 " this, [](%5) { /* handle %4 */ });")
2638 .arg(objectName, typeList, className, functionName, typeAndNameList);
2641 "// Connect using qOverload:\n"
2642 "connect(sender, &SenderClass::signal,\n"
2643 " %1, qOverload<%2>(&%3::%4));\n\n"
2644 "// Or using a lambda as wrapper:\n"
2645 "connect(sender, &SenderClass::signal,\n"
2646 " %1, [receiver = %1](%5) { receiver->%4(%6); });")
2647 .arg(objectName, typeList, className, functionName, typeAndNameList, nameList);
2654
2655
2656
2659 QString name = className;
2661 if (name.startsWith(
'Q') && name.length() > 1)
2664 if (!name.isEmpty())
2665 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
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.
bool isQmlBasicType() const
Returns true if the node type is QmlBasicType.
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.
virtual CollectionNode * logicalModule() const
If this is a QmlTypeNode, a pointer to its QML module is returned, which is a pointer to a Collection...
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.