summaryrefslogtreecommitdiffstats
path: root/src/qdoc/generator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/generator.cpp')
-rw-r--r--src/qdoc/generator.cpp881
1 files changed, 450 insertions, 431 deletions
diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp
index 58d882671..c7831d1fd 100644
--- a/src/qdoc/generator.cpp
+++ b/src/qdoc/generator.cpp
@@ -42,6 +42,9 @@
#include "separator.h"
#include "tokenizer.h"
#include "qdocdatabase.h"
+#ifndef QT_BOOTSTRAPPED
+# include "QtCore/qurl.h"
+#endif
QT_BEGIN_NAMESPACE
@@ -72,7 +75,8 @@ Generator::QDocPass Generator::qdocPass_ = Generator::Neither;
bool Generator::qdocSingleExec_ = false;
bool Generator::qdocWriteQaPages_ = false;
bool Generator::useOutputSubdirs_ = true;
-QmlTypeNode* Generator::qmlTypeContext_ = 0;
+bool Generator::useTimestamps_ = false;
+QmlTypeNode* Generator::qmlTypeContext_ = nullptr;
static QRegExp tag("</?@[^>]*>");
static QLatin1String amp("&amp;");
@@ -136,7 +140,7 @@ void Generator::appendFullName(Text& text,
const Node *relative,
const Node *actualNode)
{
- if (actualNode == 0)
+ if (actualNode == nullptr)
actualNode = apparentNode;
text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
@@ -149,7 +153,7 @@ void Generator::appendFullName(Text& text,
const QString& fullName,
const Node *actualNode)
{
- if (actualNode == 0)
+ if (actualNode == nullptr)
actualNode = apparentNode;
text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
@@ -212,9 +216,7 @@ int Generator::appendSortedNames(Text& text, const ClassNode* cn, const QList<Re
r = rc.constBegin();
while (r != rc.constEnd()) {
ClassNode* rcn = (*r).node_;
- if (rcn && rcn->access() == Node::Public &&
- rcn->status() != Node::Internal &&
- !rcn->doc().isEmpty()) {
+ if (rcn && rcn->isInAPI()) {
Text className;
appendFullName(className, rcn, cn);
classMap[className.toString().toLower()] = className;
@@ -273,9 +275,12 @@ void Generator::writeOutFileNames()
/*!
Creates the file named \a fileName in the output directory.
Attaches a QTextStream to the created file, which is written
- to all over the place using out().
+ to all over the place using out(). This function does not
+ store the \a fileName in the \a node as the output file name.
+
+ \sa beginSubPage()
*/
-void Generator::beginSubPage(const Node* node, const QString& fileName)
+void Generator::beginFilePage(const Node* node, const QString& fileName)
{
QString path = outputDir() + QLatin1Char('/');
if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty() &&
@@ -297,6 +302,22 @@ void Generator::beginSubPage(const Node* node, const QString& fileName)
out->setCodec(outputCodec);
#endif
outStreamStack.push(out);
+}
+
+ /*!
+ Creates the file named \a fileName in the output directory.
+ Attaches a QTextStream to the created file, which is written
+ to all over the place using out(). This function calls another
+ function, \c beginFilePage(), which is really just most of what
+ this function used to contain. We needed a different version
+ that doesn't store the \a fileName in the \a node as the output
+ file name.
+
+ \sa beginFilePage()
+ */
+void Generator::beginSubPage(const Node* node, const QString& fileName)
+{
+ beginFilePage(node, fileName);
const_cast<Node*>(node)->setOutputFileName(fileName);
}
@@ -312,26 +333,65 @@ void Generator::endSubPage()
delete outStreamStack.pop();
}
+/*
+ the code below is effectively equivalent to:
+ input.replace(QRegExp("[^A-Za-z0-9]+"), " ");
+ input = input.trimmed();
+ input.replace(QLatin1Char(' '), QLatin1Char('-'));
+ input = input.toLower();
+ as this function accounted for ~8% of total running time
+ we optimize a bit...
+*/
+static void transmogrify(QString &input, QString &output)
+{
+ // +5 prevents realloc in fileName() below
+ output.reserve(input.size() + 5);
+ bool begun = false;
+ for (int i = 0; i != input.size(); ++i) {
+ QChar c = input.at(i);
+ uint u = c.unicode();
+ if (u >= 'A' && u <= 'Z')
+ u += 'a' - 'A';
+ if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
+ output += QLatin1Char(u);
+ begun = true;
+ }
+ else if (begun) {
+ output += QLatin1Char('-');
+ begun = false;
+ }
+ }
+ while (output.endsWith(QLatin1Char('-')))
+ output.chop(1);
+}
+
QString Generator::fileBase(const Node *node) const
{
- if (node->relates())
- node = node->relates();
- else if (!node->isAggregate() && !node->isCollectionNode())
+ if (!node->isPageNode() && !node->isCollectionNode())
node = node->parent();
- if (node->type() == Node::QmlPropertyGroup) {
- node = node->parent();
- }
if (node->hasFileNameBase())
return node->fileNameBase();
QString base;
- if (node->isDocumentNode()) {
+ if (node->isCollectionNode()) {
+ base = node->name() + outputSuffix(node);
+ if (base.endsWith(".html"))
+ base.truncate(base.length() - 5);
+
+ if (node->isQmlModule())
+ base.append("-qmlmodule");
+ else if (node->isJsModule())
+ base.append("-jsmodule");
+ else if (node->isModule())
+ base.append("-module");
+ // Why not add "-group" for group pages?
+ } else if (node->isTextPageNode()) {
base = node->name();
- if (base.endsWith(".html") && !node->isExampleFile())
+ if (base.endsWith(".html"))
base.truncate(base.length() - 5);
- if (node->isExample() || node->isExampleFile()) {
+ if (node->isExample()) {
QString modPrefix(node->physicalModuleName());
if (modPrefix.isEmpty()) {
modPrefix = project_;
@@ -341,9 +401,8 @@ QString Generator::fileBase(const Node *node) const
if (node->isExample()) {
base.append(QLatin1String("-example"));
}
- }
- else if (node->isQmlType() || node->isQmlBasicType() ||
- node->isJsType() || node->isJsBasicType()) {
+ } else if (node->isQmlType() || node->isQmlBasicType() ||
+ node->isJsType() || node->isJsBasicType()) {
base = node->name();
/*
To avoid file name conflicts in the html directory,
@@ -357,29 +416,15 @@ QString Generator::fileBase(const Node *node) const
+ QLatin1Char('-'));
}
base.prepend(outputPrefix(node));
- }
- else if (node->isCollectionNode()) {
- base = node->name() + outputSuffix(node);
- if (base.endsWith(".html"))
- base.truncate(base.length() - 5);
-
- if (node->isQmlModule()) {
- base.append("-qmlmodule");
- }
- else if (node->isJsModule()) {
- base.append("-jsmodule");
- }
- else if (node->isModule()) {
- base.append("-module");
- }
- // Why not add "-group" for group pages?
- }
- else {
+ } else if (node->isProxyNode()) {
+ base = node->name();
+ base.append("-proxy");
+ } else {
const Node *p = node;
forever {
const Node *pp = p->parent();
base.prepend(p->name());
- if (!pp || pp->name().isEmpty() || pp->isDocumentNode())
+ if (pp == nullptr || pp->name().isEmpty() || pp->isTextPageNode())
break;
base.prepend(QLatin1Char('-'));
p = pp;
@@ -393,40 +438,33 @@ QString Generator::fileBase(const Node *node) const
}
}
- // the code below is effectively equivalent to:
- // base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
- // base = base.trimmed();
- // base.replace(QLatin1Char(' '), QLatin1Char('-'));
- // base = base.toLower();
- // as this function accounted for ~8% of total running time
- // we optimize a bit...
-
QString res;
- // +5 prevents realloc in fileName() below
- res.reserve(base.size() + 5);
- bool begun = false;
- for (int i = 0; i != base.size(); ++i) {
- QChar c = base.at(i);
- uint u = c.unicode();
- if (u >= 'A' && u <= 'Z')
- u += 'a' - 'A';
- if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
- res += QLatin1Char(u);
- begun = true;
- }
- else if (begun) {
- res += QLatin1Char('-');
- begun = false;
- }
- }
- while (res.endsWith(QLatin1Char('-')))
- res.chop(1);
+ transmogrify(base, res);
Node* n = const_cast<Node*>(node);
n->setFileNameBase(res);
return res;
}
/*!
+ Constructs an href link from an example file name, which
+ is a path to the example file.
+ */
+QString Generator::linkForExampleFile(const QString &path, const Node *parent)
+{
+ QString link = path;
+ QString modPrefix(parent->physicalModuleName());
+ if (modPrefix.isEmpty())
+ modPrefix = project_;
+ link.prepend(modPrefix.toLower() + QLatin1Char('-'));
+
+ QString res;
+ transmogrify(link, res);
+ res.append(QLatin1Char('.'));
+ res.append(fileExtension());
+ return res;
+}
+
+/*!
If the \a node has a URL, return the URL as the file name.
Otherwise, construct the file name from the fileBase() and
either the provided \a extension or fileExtension(), and
@@ -509,7 +547,7 @@ QMap<QString, QString>& Generator::formattingRightMap()
*/
QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
{
- if (!node)
+ if (node == nullptr)
return QString();
if (!node->url().isEmpty())
return node->url();
@@ -529,10 +567,10 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
fdl.append(QLatin1Char('/'));
}
if (node->isNamespace()) {
-
- // The root namespace has no name - check for this before creating
- // an attribute containing the location of any documentation.
-
+ /*
+ The root namespace has no name - check for this before creating
+ an attribute containing the location of any documentation.
+ */
if (!fileBase(node).isEmpty())
parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
else
@@ -553,47 +591,56 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
QLatin1Char('.') + currentGenerator()->fileExtension();
}
}
- else if (node->isDocumentNode() || node->isCollectionNode()) {
+ else if (node->isTextPageNode() || node->isCollectionNode()) {
parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
}
else if (fileBase(node).isEmpty())
return QString();
- Node *parentNode = 0;
+ Node *parentNode = nullptr;
- if ((parentNode = node->relates())) {
- parentName = fullDocumentLocation(node->relates());
- }
- else if ((parentNode = node->parent())) {
- if (parentNode->isQmlPropertyGroup() || parentNode->isJsPropertyGroup()) {
- parentNode = parentNode->parent();
- parentName = fullDocumentLocation(parentNode);
- }
- else {
+ if ((parentNode = node->parent())) {
+ // use the parent's name unless the parent is the root namespace
+ if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
parentName = fullDocumentLocation(node->parent());
- }
}
- switch (node->type()) {
+ switch (node->nodeType()) {
case Node::Class:
+ case Node::Struct:
+ case Node::Union:
case Node::Namespace:
+ case Node::Proxy:
parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
break;
case Node::Function:
{
const FunctionNode *fn = static_cast<const FunctionNode *>(node);
-
- if (fn->isDtor())
- anchorRef = "#dtor." + fn->name().mid(1);
-
- else if (fn->hasOneAssociatedProperty() && fn->doc().isEmpty())
- return fullDocumentLocation(fn->firstAssociatedProperty());
-
- else if (fn->overloadNumber() > 0)
- anchorRef = QLatin1Char('#') + cleanRef(fn->name())
+ switch (fn->metaness()) {
+ case FunctionNode::JsSignal:
+ case FunctionNode::QmlSignal:
+ anchorRef = QLatin1Char('#') + node->name() + "-signal";
+ break;
+ case FunctionNode::JsSignalHandler:
+ case FunctionNode::QmlSignalHandler:
+ anchorRef = QLatin1Char('#') + node->name() + "-signal-handler";
+ break;
+ case FunctionNode::JsMethod:
+ case FunctionNode::QmlMethod:
+ anchorRef = QLatin1Char('#') + node->name() + "-method";
+ break;
+ default:
+ if (fn->isDtor())
+ anchorRef = "#dtor." + fn->name().mid(1);
+ else if (fn->hasOneAssociatedProperty() && fn->doc().isEmpty())
+ return fullDocumentLocation(fn->firstAssociatedProperty());
+ else if (fn->overloadNumber() > 0)
+ anchorRef = QLatin1Char('#') + cleanRef(fn->name())
+ QLatin1Char('-') + QString::number(fn->overloadNumber());
- else
- anchorRef = QLatin1Char('#') + cleanRef(fn->name());
+ else
+ anchorRef = QLatin1Char('#') + cleanRef(fn->name());
+ break;
+ }
break;
}
/*
@@ -616,28 +663,22 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
case Node::Property:
anchorRef = QLatin1Char('#') + node->name() + "-prop";
break;
+ case Node::JsProperty:
case Node::QmlProperty:
if (node->isAttached())
anchorRef = QLatin1Char('#') + node->name() + "-attached-prop";
else
anchorRef = QLatin1Char('#') + node->name() + "-prop";
break;
- case Node::QmlSignal:
- anchorRef = QLatin1Char('#') + node->name() + "-signal";
- break;
- case Node::QmlSignalHandler:
- anchorRef = QLatin1Char('#') + node->name() + "-signal-handler";
- break;
- case Node::QmlMethod:
- anchorRef = QLatin1Char('#') + node->name() + "-method";
- break;
case Node::Variable:
anchorRef = QLatin1Char('#') + node->name() + "-var";
break;
+ case Node::JsType:
case Node::QmlType:
- case Node::Document:
+ case Node::Page:
case Node::Group:
case Node::Module:
+ case Node::JsModule:
case Node::QmlModule:
{
parentName = fileBase(node);
@@ -649,8 +690,8 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
break;
}
- if (!node->isClass() && !node->isNamespace()) {
- if (node->status() == Node::Obsolete)
+ if (!node->isClassNode() && !node->isNamespace()) {
+ if (node->isObsolete())
parentName.replace(QLatin1Char('.') + currentGenerator()->fileExtension(),
"-obsolete." + currentGenerator()->fileExtension());
}
@@ -678,20 +719,13 @@ void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
}
}
-int Generator::generateAtom(const Atom * /* atom */,
- const Node * /* relative */,
- CodeMarker * /* marker */)
-{
- return 0;
-}
-
const Atom *Generator::generateAtomList(const Atom *atom,
const Node *relative,
CodeMarker *marker,
bool generate,
int &numAtoms)
{
- while (atom) {
+ while (atom != nullptr) {
if (atom->type() == Atom::FormatIf) {
int numAtoms0 = numAtoms;
bool rightFormat = canHandleFormat(atom->string());
@@ -700,8 +734,8 @@ const Atom *Generator::generateAtomList(const Atom *atom,
marker,
generate && rightFormat,
numAtoms);
- if (!atom)
- return 0;
+ if (atom == nullptr)
+ return nullptr;
if (atom->type() == Atom::FormatElse) {
++numAtoms;
@@ -710,8 +744,8 @@ const Atom *Generator::generateAtomList(const Atom *atom,
marker,
generate && !rightFormat,
numAtoms);
- if (!atom)
- return 0;
+ if (atom == nullptr)
+ return nullptr;
}
if (atom->type() == Atom::FormatEndif) {
@@ -742,7 +776,7 @@ const Atom *Generator::generateAtomList(const Atom *atom,
atom = atom->next();
}
}
- return 0;
+ return nullptr;
}
/*!
@@ -751,20 +785,12 @@ const Atom *Generator::generateAtomList(const Atom *atom,
*/
void Generator::generateBody(const Node *node, CodeMarker *marker)
{
- bool quiet = false;
-
- if (node->type() == Node::Document) {
- const DocumentNode *dn = static_cast<const DocumentNode *>(node);
- if ((dn->docSubtype() == Node::File) || (dn->docSubtype() == Node::Image)) {
- quiet = true;
- }
- }
if (!node->hasDoc() && !node->hasSharedDoc()) {
/*
Test for special function, like a destructor or copy constructor,
that has no documentation.
*/
- if (node->type() == Node::Function) {
+ if (node->isFunction()) {
const FunctionNode* func = static_cast<const FunctionNode*>(node);
if (func->isDtor()) {
Text text;
@@ -812,32 +838,29 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
generateText(text, node, marker);
out() << "</p>";
}
- else if (!node->isWrapper() && !quiet && !node->isReimplemented()) {
+ else if (!node->isWrapper() && !node->isMarkedReimp()) {
if (!func->isIgnored()) // undocumented functions added by Q_OBJECT
node->location().warning(tr("No documentation for '%1'").arg(node->plainSignature()));
}
- }
- else if (!node->isWrapper() && !quiet && !node->isReimplemented()) {
- /*
- Don't require documentation of things defined in Q_GADGET
- */
+ } else if (!node->isWrapper() && !node->isMarkedReimp()) {
+ // Don't require documentation of things defined in Q_GADGET
if (node->name() != QLatin1String("QtGadgetHelper"))
node->location().warning(tr("No documentation for '%1'").arg(node->plainSignature()));
}
}
else if (!node->isSharingComment()) {
- if (node->type() == Node::Function) {
- const FunctionNode *func = static_cast<const FunctionNode *>(node);
- if (!func->reimplementedFrom().isEmpty())
- generateReimplementedFrom(func, marker);
+ if (node->isFunction()) {
+ const FunctionNode *fn = static_cast<const FunctionNode *>(node);
+ if (!fn->overridesThis().isEmpty())
+ generateReimplementsClause(fn, marker);
}
if (!generateText(node->doc().body(), node, marker)) {
- if (node->isReimplemented())
+ if (node->isMarkedReimp())
return;
}
- if (node->type() == Node::Enum) {
+ if (node->isEnumType()) {
const EnumNode *enume = (const EnumNode *) node;
QSet<QString> definedItems;
@@ -861,8 +884,6 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
node->doc().location().warning(tr("No such enum item '%1' in %2")
.arg(*a).arg(node->plainFullName()), details);
- if (*a == "Void")
- qDebug() << "VOID:" << node->name() << definedItems;
}
else if (!documentedItems.contains(*a)) {
node->doc().location().warning(tr("Undocumented enum item '%1' in %2")
@@ -871,187 +892,179 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
++a;
}
}
- }
- else if (node->type() == Node::Function) {
- const FunctionNode *func = static_cast<const FunctionNode *>(node);
- QSet<QString> definedParams;
- QVector<Parameter>::ConstIterator p = func->parameters().constBegin();
- while (p != func->parameters().constEnd()) {
- if (!(*p).name().isEmpty())
- definedParams.insert((*p).name());
- ++p;
- }
-
- QSet<QString> documentedParams = func->doc().parameterNames();
- QSet<QString> allParams = definedParams + documentedParams;
- if (allParams.count() > definedParams.count()
- || allParams.count() > documentedParams.count()) {
- QSet<QString>::ConstIterator a = allParams.constBegin();
- while (a != allParams.constEnd()) {
- if (!definedParams.contains(*a)) {
+ } else if (node->isFunction()) {
+ const FunctionNode *fn = static_cast<const FunctionNode *>(node);
+ QSet<QString> declaredNames;
+ fn->parameters().getNames(declaredNames);
+ QSet<QString> documentedNames = fn->doc().parameterNames();
+ if (declaredNames != documentedNames) {
+ QSet<QString>::const_iterator i = declaredNames.constBegin();
+ while (i != declaredNames.constEnd()) {
+ if (!documentedNames.contains(*i)) {
+ if (fn->isActive() || fn->isPreliminary()) {
+ if (!fn->isMarkedReimp() && !fn->isOverload()) {
+ fn->doc().location().warning(
+ tr("Undocumented parameter '%1' in %2")
+ .arg(*i).arg(node->plainFullName()));
+ }
+ }
+ }
+ ++i;
+ }
+ i = documentedNames.constBegin();
+ while (i != documentedNames.constEnd()) {
+ if (!declaredNames.contains(*i)) {
+ QString best = nearestName(*i, declaredNames);
QString details;
- QString best = nearestName(*a, definedParams);
if (!best.isEmpty())
details = tr("Maybe you meant '%1'?").arg(best);
-
- node->doc().location().warning(
- tr("No such parameter '%1' in %2").arg(*a).arg(node->plainFullName()),
- details);
- }
- else if (!(*a).isEmpty() && !documentedParams.contains(*a)) {
- bool needWarning = (func->status() > Node::Obsolete);
- if (func->overloadNumber() > 0) {
- FunctionNode *primaryFunc = func->parent()->findFunctionNode(func->name(), QString());
- if (primaryFunc) {
- foreach (const Parameter &param,
- primaryFunc->parameters()) {
- if (param.name() == *a) {
- needWarning = false;
- break;
- }
- }
- }
- }
- if (needWarning && !func->isReimplemented() && !func->isOverload())
- node->doc().location().warning(
- tr("Undocumented parameter '%1' in %2")
- .arg(*a).arg(node->plainFullName()));
+ fn->doc().location().warning(tr("No such parameter '%1' in %2")
+ .arg(*i).arg(fn->plainFullName()),
+ details);
}
- ++a;
+ ++i;
}
}
/*
- Something like this return value check should
- be implemented at some point.
+ This return value check should be implemented
+ for all functions with a return type.
+ mws 13/12/2018
*/
- if (func->status() > Node::Obsolete && func->returnType() == "bool"
- && func->reimplementedFrom() == 0 && !func->isOverload()) {
- QString body = func->doc().body().toString();
- if (!body.contains("return", Qt::CaseInsensitive))
- node->doc().location().warning(tr("Undocumented return value"));
+ if (!fn->isObsolete() && fn->returnsBool() &&
+ !fn->isMarkedReimp() && !fn->isOverload()) {
+ if (!fn->doc().body().contains("return"))
+ node->doc().location().warning(tr("Undocumented return value "
+ "(hint: use 'return' or 'returns' in the text"));
}
}
}
- if (node->isDocumentNode()) {
- const DocumentNode *dn = static_cast<const DocumentNode *>(node);
- if (dn->isExample() && !dn->noAutoList()) {
- generateExampleFiles(dn, marker);
- }
- else if (dn->docSubtype() == Node::File) {
- Text text;
- Quoter quoter;
- Doc::quoteFromFile(dn->doc().location(), quoter, dn->name());
- QString code = quoter.quoteTo(dn->location(), QString(), QString());
- CodeMarker *codeMarker = CodeMarker::markerForFileName(dn->name());
- text << Atom(codeMarker->atomType(), code);
- generateText(text, dn, codeMarker);
+ // For examples, generate either a link to the project directory
+ // (if url.examples is defined), or a list of files/images.
+ if (node->isExample()) {
+ const ExampleNode* en = static_cast<const ExampleNode*>(node);
+ QString exampleUrl = config()->getString(CONFIG_URL + Config::dot + CONFIG_EXAMPLES);
+ if (!exampleUrl.isEmpty()) {
+ generateLinkToExample(en, marker, exampleUrl);
+ } else if (!en->noAutoList()) {
+ generateFileList(en, marker, false);
+ generateFileList(en, marker, true);
}
}
}
-void Generator::generateCppReferencePage(Node* /* node */, CodeMarker* /* marker */)
+/*!
+ Generates a link to the project folder for example node \a en.
+ \a baseUrl is the base URL - path information is available in
+ the example node's name() and 'examplesinstallpath' configuration
+ variable.
+*/
+void Generator::generateLinkToExample(const ExampleNode *en,
+ CodeMarker *marker,
+ const QString &baseUrl)
{
-}
+ Text text;
+ QString exampleUrl(baseUrl);
-void Generator::generateExampleFiles(const DocumentNode *dn, CodeMarker *marker)
-{
- if (dn->childNodes().isEmpty())
- return;
- generateFileList(dn, marker, Node::File);
- generateFileList(dn, marker, Node::Image);
-}
+ if (!exampleUrl.contains("\1")) {
+ if (!exampleUrl.endsWith("/"))
+ exampleUrl += "/";
+ exampleUrl += "\1";
+ }
-void Generator::generateDocumentNode(DocumentNode* /* dn */, CodeMarker* /* marker */)
-{
-}
+ // Name of the example node is the path, relative to install path
+ QStringList path = QStringList()
+ << config()->getString(CONFIG_EXAMPLESINSTALLPATH)
+ << en->name();
+ path.removeAll({});
-void Generator::generateCollectionNode(CollectionNode* , CodeMarker* )
-{
+ QString link;
+#ifndef QT_BOOTSTRAPPED
+ link = QUrl(baseUrl).host();
+#endif
+ if (!link.isEmpty())
+ link.prepend(" @ ");
+ link.prepend("Example project");
+
+ text << Atom::ParaLeft
+ << Atom(Atom::Link, exampleUrl.replace("\1", path.join("/")))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << Atom(Atom::String, link)
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom::ParaRight;
+
+ generateText(text, 0, marker);
}
/*!
- This function is called when the documentation for an
- example is being formatted. It outputs the list of source
- files comprising the example, and the list of images used
- by the example. The images are copied into a subtree of
+ This function is called when the documentation for an example is
+ being formatted. It outputs a list of files for the example, which
+ can be the example's source files or the list of images used by the
+ example. The images are copied into a subtree of
\c{...doc/html/images/used-in-examples/...}
- */
-void Generator::generateFileList(const DocumentNode* dn,
- CodeMarker* marker,
- Node::DocSubtype subtype,
- const QString& regExp)
+*/
+void Generator::generateFileList(const ExampleNode* en, CodeMarker* marker, bool images)
{
- int count = 0;
Text text;
OpenedList openedList(OpenedList::Bullet);
QString tag;
+ QStringList paths;
+ Atom::AtomType atomType = Atom::ExampleFileLink;
- NodeList children(dn->childNodes());
- std::sort(children.begin(), children.end(), Generator::compareNodes);
- if (!regExp.isEmpty()) {
- QRegExp re(regExp);
- QMutableListIterator<Node*> i(children);
- while (i.hasNext()) {
- if (!re.exactMatch(i.next()->name()))
- i.remove();
- }
- }
- if (children.size() > 1) {
- switch (subtype) {
- default:
- case Node::File:
- tag = "Files:";
- break;
- case Node::Image:
- tag = "Images:";
- break;
- }
- text << Atom::ParaLeft << tag << Atom::ParaRight;
+ if (images) {
+ paths = en->images();
+ tag = "Images:";
+ atomType = Atom::ExampleImageLink;
+ } else { //files
+ paths = en->files();
+ tag = "Files:";
}
+ std::sort(paths.begin(), paths.end(), Generator::comparePaths);
+ text << Atom::ParaLeft << tag << Atom::ParaRight;
text << Atom(Atom::ListLeft, openedList.styleString());
- foreach (const Node* child, children) {
- if (child->docSubtype() == subtype) {
- ++count;
- QString file = child->name();
- if (subtype == Node::Image) {
- if (!file.isEmpty()) {
- QDir dirInfo;
- QString userFriendlyFilePath;
- const QString prefix("/images/used-in-examples/");
- QString srcPath = Config::findFile(dn->location(),
- QStringList(),
- exampleDirs,
- file,
- exampleImgExts,
- &userFriendlyFilePath);
- outFileNames_ << prefix.mid(1) + userFriendlyFilePath;
- userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
- QString imgOutDir = outDir_ + prefix + userFriendlyFilePath;
- if (!dirInfo.mkpath(imgOutDir))
- dn->location().fatal(tr("Cannot create output directory '%1'").arg(imgOutDir));
- Config::copyFile(dn->location(), srcPath, file, imgOutDir);
- }
-
+ QString path;
+ foreach (QString file, paths) {
+ if (images) {
+ if (!file.isEmpty()) {
+ QDir dirInfo;
+ QString userFriendlyFilePath;
+ const QString prefix("/images/used-in-examples/");
+ QString srcPath = Config::findFile(en->location(),
+ QStringList(),
+ exampleDirs,
+ file,
+ exampleImgExts,
+ &userFriendlyFilePath);
+ outFileNames_ << prefix.mid(1) + userFriendlyFilePath;
+ userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
+ QString imgOutDir = outDir_ + prefix + userFriendlyFilePath;
+ if (!dirInfo.mkpath(imgOutDir))
+ en->location().fatal(tr("Cannot create output directory '%1'").arg(imgOutDir));
+ Config::copyFile(en->location(), srcPath, file, imgOutDir);
}
- openedList.next();
- text << Atom(Atom::ListItemNumber, openedList.numberString())
- << Atom(Atom::ListItemLeft, openedList.styleString())
- << Atom::ParaLeft
- << Atom(Atom::Link, file)
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << file
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
- << Atom::ParaRight
- << Atom(Atom::ListItemRight, openedList.styleString());
}
+ else {
+ generateExampleFilePage(en, file, marker);
+ }
+
+ openedList.next();
+ text << Atom(Atom::ListItemNumber, openedList.numberString())
+ << Atom(Atom::ListItemLeft, openedList.styleString())
+ << Atom::ParaLeft
+ << Atom(atomType, file)
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+ << file
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+ << Atom::ParaRight
+ << Atom(Atom::ListItemRight, openedList.styleString());
+ path = file;
}
text << Atom(Atom::ListRight, openedList.styleString());
- if (count > 0)
- generateText(text, dn, marker);
+ if (!paths.isEmpty())
+ generateText(text, en, marker);
}
void Generator::generateInheritedBy(const ClassNode *classe, CodeMarker *marker)
@@ -1113,19 +1126,7 @@ void Generator::generateDocumentation(Node* node)
return;
if (node->isInternal() && !showInternal_)
return;
-
- if (node->isDocumentNode()) {
- DocumentNode* docNode = static_cast<DocumentNode*>(node);
- if (docNode->docSubtype() == Node::ExternalPage)
- return;
- if (docNode->docSubtype() == Node::Image)
- return;
- if (docNode->docSubtype() == Node::Page) {
- if (docNode->count() > 0)
- qDebug("PAGE %s HAS CHILDREN", qPrintable(docNode->title()));
- }
- }
- else if (node->isQmlPropertyGroup() || node->isJsPropertyGroup())
+ if (node->isExternalPage())
return;
/*
@@ -1133,68 +1134,81 @@ void Generator::generateDocumentation(Node* node)
*/
CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
- if (node->parent() != 0) {
- if (node->isClass() || (node->isNamespace() && node->docMustBeGenerated())) {
- beginSubPage(node, fileName(node));
- generateCppReferencePage(static_cast<Aggregate*>(node), marker);
- endSubPage();
- }
- else if (node->isQmlType() || node->isJsType()) {
- beginSubPage(node, fileName(node));
- QmlTypeNode* qcn = static_cast<QmlTypeNode*>(node);
- generateQmlTypePage(qcn, marker);
- endSubPage();
- }
- else if (node->isDocumentNode()) {
- beginSubPage(node, fileName(node));
- generateDocumentNode(static_cast<DocumentNode*>(node), marker);
- endSubPage();
- }
- else if (node->isQmlBasicType() || node->isJsBasicType()) {
+ if (node->parent() != nullptr) {
+ if (node->isCollectionNode()) {
+ /*
+ A collection node collects: groups, C++ modules,
+ QML modules or JavaScript modules. Testing for a
+ CollectionNode must be done before testing for a
+ TextPageNode because a CollectionNode is a PageNode
+ at this point.
+
+ Don't output an HTML page for the collection
+ node unless the \group, \module, \qmlmodule or
+ \jsmodule command was actually seen by qdoc in
+ the qdoc comment for the node.
+
+ A key prerequisite in this case is the call to
+ mergeCollections(cn). We must determine whether
+ this group, module, QML module, or JavaScript
+ module has members in other modules. We know at
+ this point that cn's members list contains only
+ members in the current module. Therefore, before
+ outputting the page for cn, we must search for
+ members of cn in the other modules and add them
+ to the members list.
+ */
+ CollectionNode* cn = static_cast<CollectionNode*>(node);
+ if (cn->wasSeen()) {
+ qdb_->mergeCollections(cn);
+ beginSubPage(node, fileName(node));
+ generateCollectionNode(cn, marker);
+ endSubPage();
+ } else if (cn->isGenericCollection()) {
+ // Currently used only for the module's related orphans page
+ // but can be generalized for other kinds of collections if
+ // other use cases pop up.
+ QString name = cn->name().toLower();
+ name.replace(QChar(' '), QString("-"));
+ QString filename = cn->tree()->physicalModuleName() + "-" + name + "." + fileExtension();
+ beginSubPage(node, filename);
+ generateGenericCollectionPage(cn, marker);
+ endSubPage();
+ }
+ } else if (node->isTextPageNode()) {
beginSubPage(node, fileName(node));
- QmlBasicTypeNode* qbtn = static_cast<QmlBasicTypeNode*>(node);
- generateQmlBasicTypePage(qbtn, marker);
+ generatePageNode(static_cast<PageNode*>(node), marker);
endSubPage();
+ } else if (node->isAggregate()) {
+ if ((node->isClassNode() || node->isHeader() || node->isNamespace()) &&
+ node->docMustBeGenerated()) {
+ beginSubPage(node, fileName(node));
+ generateCppReferencePage(static_cast<Aggregate*>(node), marker);
+ endSubPage();
+ } else if (node->isQmlType() || node->isJsType()) {
+ beginSubPage(node, fileName(node));
+ QmlTypeNode* qcn = static_cast<QmlTypeNode*>(node);
+ generateQmlTypePage(qcn, marker);
+ endSubPage();
+ } else if (node->isQmlBasicType() || node->isJsBasicType()) {
+ beginSubPage(node, fileName(node));
+ QmlBasicTypeNode* qbtn = static_cast<QmlBasicTypeNode*>(node);
+ generateQmlBasicTypePage(qbtn, marker);
+ endSubPage();
+ } else if (node->isProxyNode()) {
+ beginSubPage(node, fileName(node));
+ generateProxyPage(static_cast<Aggregate*>(node), marker);
+ endSubPage();
+ }
}
}
if (node->isAggregate()) {
Aggregate* aggregate = static_cast<Aggregate*>(node);
- int i = 0;
- while (i < aggregate->childNodes().count()) {
- Node *c = aggregate->childNodes().at(i);
- if (c->isAggregate() && !c->isPrivate()) {
- generateDocumentation((Aggregate*)c);
- }
- else if (c->isCollectionNode()) {
- /*
- A collection node collects: groups, C++ modules,
- QML modules or JavaScript modules.
-
- Don't output an HTML page for the collection
- node unless the \group, \module, \qmlmodule or
- \jsmodule command was actually seen by qdoc in
- the qdoc comment for the node.
-
- A key prerequisite in this case is the call to
- mergeCollections(cn). We must determine whether
- this group, module, QML module, or JavaScript
- module has members in other modules. We know at
- this point that cn's members list contains only
- members in the current module. Therefore, before
- outputting the page for cn, we must search for
- members of cn in the other modules and add them
- to the members list.
- */
- CollectionNode* cn = static_cast<CollectionNode*>(c);
- if (cn->wasSeen()) {
- qdb_->mergeCollections(cn);
- beginSubPage(c, fileName(c));
- generateCollectionNode(cn, marker);
- endSubPage();
- }
- }
- ++i;
+ const NodeList &children = aggregate->childNodes();
+ foreach (Node *n, children) {
+ if (n->isPageNode() && !n->isPrivate())
+ generateDocumentation(n);
}
}
}
@@ -1242,13 +1256,6 @@ void Generator::generateQmlInheritedBy(const QmlTypeNode* qcn,
}
/*!
- */
-void Generator::generateQmlInherits(QmlTypeNode* , CodeMarker* )
-{
- // stub.
-}
-
-/*!
Extract sections of markup text surrounded by \e qmltext
and \e endqmltext and output them.
*/
@@ -1260,7 +1267,7 @@ bool Generator::generateQmlText(const Text& text,
const Atom* atom = text.firstAtom();
bool result = false;
- if (atom != 0) {
+ if (atom != nullptr) {
initializeTextOutput();
while (atom) {
if (atom->type() != Atom::QmlText)
@@ -1279,19 +1286,34 @@ bool Generator::generateQmlText(const Text& text,
return result;
}
-void Generator::generateReimplementedFrom(const FunctionNode *fn, CodeMarker *marker)
+void Generator::generateReimplementsClause(const FunctionNode *fn, CodeMarker *marker)
{
- if (!fn->reimplementedFrom().isEmpty()) {
- if (fn->parent()->isClass()) {
+ if (!fn->overridesThis().isEmpty()) {
+ if (fn->parent()->isClassNode()) {
ClassNode* cn = static_cast<ClassNode*>(fn->parent());
- const FunctionNode *from = cn->findOverriddenFunction(fn);
- if (from && from->access() != Node::Private && from->parent()->access() != Node::Private) {
+ const FunctionNode *overrides = cn->findOverriddenFunction(fn);
+ if (overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
+ if (overrides->hasDoc()) {
+ Text text;
+ text << Atom::ParaLeft << "Reimplements: ";
+ QString fullName = overrides->parent()->name() + "::" + overrides->signature(false, true);
+ appendFullName(text, overrides->parent(), fullName, overrides);
+ text << "." << Atom::ParaRight;
+ generateText(text, fn, marker);
+ return;
+ }
+ }
+ const PropertyNode* sameName = cn->findOverriddenProperty(fn);
+ if (sameName && sameName->hasDoc()) {
Text text;
- text << Atom::ParaLeft << "Reimplemented from ";
- QString fullName = from->parent()->name() + "::" + from->name() + "()";
- appendFullName(text, from->parent(), fullName, from);
+ text << Atom::ParaLeft << "Reimplements an access function for property: ";
+ QString fullName = sameName->parent()->name() + "::" + sameName->name();
+ appendFullName(text, sameName->parent(), fullName, sameName);
text << "." << Atom::ParaRight;
generateText(text, fn, marker);
+ } else {
+ fn->doc().location().warning(tr("Illegal \\reimp; no documented virtual function for %1")
+ .arg(fn->plainSignature()));
}
}
}
@@ -1304,7 +1326,7 @@ void Generator::generateSince(const Node *node, CodeMarker *marker)
text << Atom::ParaLeft
<< "This "
<< typeString(node);
- if (node->type() == Node::Enum)
+ if (node->isEnumType())
text << " was introduced or modified in ";
else
text << " was introduced in ";
@@ -1415,7 +1437,7 @@ bool Generator::generateText(const Text& text,
CodeMarker *marker)
{
bool result = false;
- if (text.firstAtom() != 0) {
+ if (text.firstAtom() != nullptr) {
int numAtoms = 0;
initializeTextOutput();
generateAtomList(text.firstAtom(),
@@ -1444,30 +1466,28 @@ static bool hasExceptions(const Node* node,
{
bool result = false;
Node::ThreadSafeness ts = node->threadSafeness();
- const Aggregate* a = static_cast<const Aggregate*>(node);
- NodeList::ConstIterator c = a->childNodes().constBegin();
- while (c != a->childNodes().constEnd()) {
- if (!(*c)->isObsolete()){
- switch ((*c)->threadSafeness()) {
+ const NodeList &children = static_cast<const Aggregate*>(node)->childNodes();
+ foreach (Node *n, children) {
+ if (!n->isObsolete()){
+ switch (n->threadSafeness()) {
case Node::Reentrant:
- reentrant.append(*c);
+ reentrant.append(n);
if (ts == Node::ThreadSafe)
result = true;
break;
case Node::ThreadSafe:
- threadsafe.append(*c);
+ threadsafe.append(n);
if (ts == Node::Reentrant)
result = true;
break;
case Node::NonReentrant:
- nonreentrant.append(*c);
+ nonreentrant.append(n);
result = true;
break;
default:
break;
}
}
- ++c;
}
return result;
}
@@ -1596,17 +1616,18 @@ void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
/*!
If the node is an overloaded signal, and a node with an example on how to connect to it
+
+ Someone didn't finish writing this comment, and I don't know what this
+ function is supposed to do, so I have not tried to complete the comment
+ yet.
*/
void Generator::generateOverloadedSignal(const Node* node, CodeMarker* marker)
{
- if (node->type() != Node::Function)
+ if (!node->isFunction())
return;
const FunctionNode *func = static_cast<const FunctionNode *>(node);
- if (!func->isSignal())
+ if (!func->isSignal() || !func->hasOverloads())
return;
- if (node->parent()->overloads(node->name()).count() <= 1)
- return;
-
// Compute a friendly name for the object of that instance.
// e.g: "QAbstractSocket" -> "abstractSocket"
@@ -1622,23 +1643,9 @@ void Generator::generateOverloadedSignal(const Node* node, CodeMarker* marker)
// it is very unlikely that we will ever have public API overloading
// signals by const.
QString code = "connect(" + objectName + ", QOverload<";
- for (int i = 0; i < func->parameters().size(); ++i) {
- if (i != 0)
- code += ", ";
- code += func->parameters().at(i).dataType();
- }
-
+ func->parameters().getTypeList(code);
code += ">::of(&" + func->parent()->name() + "::" + func->name() + "),\n [=](";
-
- for (int i = 0; i < func->parameters().size(); ++i) {
- if (i != 0)
- code += ", ";
- const Parameter &p = func->parameters().at(i);
- code += p.dataType();
- if (code[code.size()-1].isLetterOrNumber())
- code += QLatin1Char(' ');
- code += p.name();
- }
+ func->parameters().getTypeAndNameList(code);
code += "){ /* ... */ });";
@@ -1678,7 +1685,7 @@ Generator *Generator::generatorForFormat(const QString& format)
return *g;
++g;
}
- return 0;
+ return nullptr;
}
/*!
@@ -1949,7 +1956,7 @@ void Generator::initializeGenerator(const Config& config)
bool Generator::matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
{
- return atom->next() != 0 && atom->next()->type() == expectedAtomType;
+ return atom->next() != nullptr && atom->next()->type() == expectedAtomType;
}
/*!
@@ -2114,7 +2121,7 @@ int Generator::skipAtoms(const Atom *atom, Atom::AtomType type) const
{
int skipAhead = 0;
atom = atom->next();
- while (atom != 0 && atom->type() != type) {
+ while (atom != nullptr && atom->type() != type) {
skipAhead++;
atom = atom->next();
}
@@ -2139,30 +2146,30 @@ void Generator::initializeTextOutput()
void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
{
if (node->isFunction() && !node->isMacro()) {
- const FunctionNode *func = static_cast<const FunctionNode *>(node);
- if (func->overloadNumber() == 0) {
+ const FunctionNode *fn = static_cast<const FunctionNode *>(node);
+ if (fn->overloadNumber() == 0) {
QString alternateName;
- const FunctionNode *alternateFunc = 0;
+ const FunctionNode *alternateFunc = nullptr;
- if (func->name().startsWith("set") && func->name().size() >= 4) {
- alternateName = func->name()[3].toLower();
- alternateName += func->name().mid(4);
- alternateFunc = func->parent()->findFunctionNode(alternateName, QString());
+ if (fn->name().startsWith("set") && fn->name().size() >= 4) {
+ alternateName = fn->name()[3].toLower();
+ alternateName += fn->name().mid(4);
+ alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
if (!alternateFunc) {
- alternateName = "is" + func->name().mid(3);
- alternateFunc = func->parent()->findFunctionNode(alternateName, QString());
+ alternateName = "is" + fn->name().mid(3);
+ alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
if (!alternateFunc) {
- alternateName = "has" + func->name().mid(3);
- alternateFunc = func->parent()->findFunctionNode(alternateName, QString());
+ alternateName = "has" + fn->name().mid(3);
+ alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
}
}
}
- else if (!func->name().isEmpty()) {
+ else if (!fn->name().isEmpty()) {
alternateName = "set";
- alternateName += func->name()[0].toUpper();
- alternateName += func->name().mid(1);
- alternateFunc = func->parent()->findFunctionNode(alternateName, QString());
+ alternateName += fn->name()[0].toUpper();
+ alternateName += fn->name().mid(1);
+ alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
}
if (alternateFunc && alternateFunc->access() != Node::Private) {
@@ -2225,35 +2232,47 @@ QString Generator::trimmedTrailing(const QString& string, const QString &prefix,
QString Generator::typeString(const Node *node)
{
- switch (node->type()) {
+ switch (node->nodeType()) {
case Node::Namespace:
return "namespace";
case Node::Class:
return "class";
+ case Node::Struct:
+ return "struct";
+ case Node::Union:
+ return "union";
case Node::QmlType:
return "type";
case Node::QmlBasicType:
return "type";
- case Node::Document:
+ case Node::Page:
return "documentation";
case Node::Enum:
return "enum";
case Node::Typedef:
return "typedef";
- case Node::Function:
+ case Node::Function: {
+ const FunctionNode *fn = static_cast<const FunctionNode*>(node);
+ switch (fn->metaness()) {
+ case FunctionNode::JsSignal:
+ case FunctionNode::QmlSignal:
+ return "signal";
+ case FunctionNode::JsSignalHandler:
+ case FunctionNode::QmlSignalHandler:
+ return "signal handler";
+ case FunctionNode::JsMethod:
+ case FunctionNode::QmlMethod:
+ return "method";
+ default:
+ break;
+ }
return "function";
+ }
case Node::Property:
case Node::QmlProperty:
return "property";
- case Node::QmlPropertyGroup:
- return "property group";
- case Node::QmlSignal:
- return "signal";
- case Node::QmlSignalHandler:
- return "signal handler";
- case Node::QmlMethod:
- return "method";
case Node::Module:
+ case Node::JsModule:
case Node::QmlModule:
return "module";
default: