diff --git a/src/tools/qdoc/cppcodemarker.cpp b/src/tools/qdoc/cppcodemarker.cpp index c85c5c64ef..ff7d9254fc 100644 --- a/src/tools/qdoc/cppcodemarker.cpp +++ b/src/tools/qdoc/cppcodemarker.cpp @@ -1287,17 +1287,15 @@ QList
CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode, Syno } ++c; } - const DocNode* dn = current->qmlBaseNode(); - if (dn) { - if (dn->subType() == Node::QmlClass) - current = static_cast(dn); - else { - dn->doc().location().warning(tr("Base class of QML class '%1' is ambgiguous").arg(current->name())); - current = 0; - } + current = current->qmlBaseNode(); + while (current) { + if (current->isAbstract()) + break; + if (current->isInternal()) + current = current->qmlBaseNode(); + else + break; } - else - current = 0; } append(sections, all, true); } diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index 22694dd0d3..21bbdafaf5 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -4198,7 +4198,10 @@ void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* { if (!qcn) return; - const DocNode* base = qcn->qmlBaseNode(); + const QmlClassNode* base = qcn->qmlBaseNode(); + while (base && base->isInternal()) { + base = base->qmlBaseNode(); + } if (base) { writeStartTag(DT_qmlInherits); //writeStartTag(DT_qmlTypeDef); diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index 2666f10eb4..69d1a69d31 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -3870,7 +3870,10 @@ void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* mar { if (!qcn) return; - const DocNode* base = qcn->qmlBaseNode(); + const QmlClassNode* base = qcn->qmlBaseNode(); + while (base && base->isInternal()) { + base = base->qmlBaseNode(); + } if (base) { Text text; text << Atom::ParaLeft << "Inherits "; diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index 5fbdb487db..abb348eb51 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -2161,10 +2161,13 @@ void QmlClassNode::subclasses(const QString& base, NodeList& subs) This function splits \a arg on the blank character to get a QML module name and version number. It then spilts the version number on the '.' character to get a major version number and - a minor vrsion number. Both version numbers must be present. - It stores these components separately. If all three are found, - true is returned. If any of the three is not found or is not - correct, false is returned. + a minor vrsion number. Both major the major and minor version + numbers should be present, but the minor version number is not + absolutely necessary. + + It stores the three components separately in this node. If all + three are found, true is returned. If any of the three is not + found or is not in the correct format, false is returned. */ bool Node::setQmlModuleInfo(const QString& arg) { diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index ecb3c5771e..f76b115416 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -65,7 +65,6 @@ typedef QList NodeList; typedef QMap NodeMap; typedef QMultiMap NodeMultiMap; typedef QMultiMap ExampleNodeMap; -typedef QList > ImportList; class Node { @@ -206,7 +205,7 @@ public: virtual bool hasProperty(const QString& ) const { return false; } virtual void getMemberNamespaces(NodeMap& ) { } virtual void getMemberClasses(NodeMap& ) { } - bool isInternal() const; + virtual bool isInternal() const; bool isIndexNode() const { return indexNodeFlag_; } bool wasSeen() const { return seen_; } Type type() const { return nodeType_; } @@ -530,6 +529,26 @@ private: QString imageFileName_; }; +struct ImportRec { + QString name_; // module name + QString version_; // . + QString importId_; // "as" name + QString importUri_; // subdirectory of module directory + + ImportRec(const QString& name, + const QString& version, + const QString& importId, + const QString& importUri) + : name_(name), version_(version), importId_(importId), importUri_(importUri) { } + QString& name() { return name_; } + QString& version() { return version_; } + QString& importId() { return importId_; } + QString& importUri() { return importUri_; } + bool isEmpty() const { return name_.isEmpty(); } +}; + +typedef QList ImportList; + class QmlClassNode : public DocNode { public: @@ -543,12 +562,13 @@ public: virtual void clearCurrentChild(); virtual bool isAbstract() const { return abstract_; } virtual void setAbstract(bool b) { abstract_ = b; } + virtual bool isInternal() const { return (status() == Internal); } const ImportList& importList() const { return importList_; } void setImportList(const ImportList& il) { importList_ = il; } const QString& qmlBaseName() const { return baseName_; } void setQmlBaseName(const QString& name) { baseName_ = name; } - const DocNode* qmlBaseNode() const { return baseNode_; } - void setQmlBaseNode(DocNode* b) { baseNode_ = b; } + const QmlClassNode* qmlBaseNode() const { return baseNode_; } + void setQmlBaseNode(QmlClassNode* b) { baseNode_ = b; } void requireCppClass() { cnodeRequired_ = true; } bool cppClassRequired() const { return cnodeRequired_; } static void addInheritedBy(const QString& base, Node* sub); @@ -564,7 +584,7 @@ private: bool cnodeRequired_; ClassNode* cnode_; QString baseName_; - DocNode* baseNode_; + QmlClassNode* baseNode_; ImportList importList_; }; diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index 4208d529c8..256bc62dd2 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -291,18 +291,29 @@ DocNode* QDocDatabase::addToModule(const QString& name, Node* node) */ DocNode* QDocDatabase::addToQmlModule(const QString& name, Node* node) { + QString longQmid, shortQmid; + QStringList dotSplit; + QStringList blankSplit = name.split(QLatin1Char(' ')); + if (blankSplit.size() > 1) { + longQmid = blankSplit[0] + blankSplit[1]; + dotSplit = blankSplit[1].split(QLatin1Char('.')); + shortQmid = blankSplit[0] + dotSplit[0]; + } DocNode* dn = findQmlModule(name); dn->addMember(node); node->setQmlModuleInfo(name); if (node->subType() == Node::QmlClass) { - QString t = node->qmlModuleIdentifier() + "::" + node->name(); QmlClassNode* n = static_cast(node); - if (!qmlTypeMap_.contains(t)) - qmlTypeMap_.insert(t,n); - if (!masterMap_.contains(t)) - masterMap_.insert(t,node); - if (!masterMap_.contains(node->name(),node)) - masterMap_.insert(node->name(),node); + QString key = longQmid + "::" + node->name(); + for (int i=0; i<2; ++i) { + if (!qmlTypeMap_.contains(key)) + qmlTypeMap_.insert(key,n); + if (!masterMap_.contains(key)) + masterMap_.insert(key,node); + if (!masterMap_.contains(node->name(),node)) + masterMap_.insert(node->name(),node); + key = shortQmid + "::" + node->name(); + } } return dn; } @@ -332,7 +343,45 @@ QmlClassNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name } } return 0; +} +/*! + Looks up the QML type node identified by the Qml module id + constructed from the strings in the \a import record and the + QML type \a name and returns a pointer to the QML type node. + If a QML type node is not found, 0 is returned. + */ +QmlClassNode* QDocDatabase::findQmlType(const ImportRec& import, const QString& name) const +{ + if (!import.isEmpty()) { + QStringList dotSplit; + dotSplit = name.split(QLatin1Char('.')); + QString qmName; + if (import.importUri_.isEmpty()) + qmName = import.name_; + else + qmName = import.importUri_; + for (int i=0; i 1) { + int dot = import.version_.lastIndexOf(QChar('.')); + if (dot > 0) { + qmid = import.name_ + import.version_.left(dot); + qualifiedName = qmid + "::" + dotSplit[i]; + qcn = qmlTypeMap_.value(qualifiedName); + if (qcn) { + return qcn; + } + } + } + } + } + return 0; } /*! @@ -836,14 +885,23 @@ void QDocDatabase::resolveQmlInheritance(InnerNode* root) if (child->type() == Node::Document && child->subType() == Node::QmlClass) { QmlClassNode* qcn = static_cast(child); if ((qcn->qmlBaseNode() == 0) && !qcn->qmlBaseName().isEmpty()) { - QmlClassNode* bqcn = findQmlType(QString(), qcn->qmlBaseName()); + QmlClassNode* bqcn = 0; + const ImportList& imports = qcn->importList(); + for (int i=0; iqmlBaseName()); + if (bqcn) + break; + } + if (bqcn == 0) { + bqcn = findQmlType(QString(), qcn->qmlBaseName()); + } if (bqcn) { qcn->setQmlBaseNode(bqcn); } #if 0 else { - qDebug() << "Unable to resolve QML base type:" << qcn->qmlBaseName() - << "for QML type:" << qcn->name(); + qDebug() << "Temporary error message (ignore): UNABLE to resolve QML base type:" + << qcn->qmlBaseName() << "for QML type:" << qcn->name(); } #endif } @@ -851,6 +909,24 @@ void QDocDatabase::resolveQmlInheritance(InnerNode* root) } } +#if 0 +void QDocDatabase::resolveQmlInheritance(InnerNode* root) +{ + // Dop we need recursion? + foreach (Node* child, root->childNodes()) { + if (child->type() == Node::Document && child->subType() == Node::QmlClass) { + QmlClassNode* qcn = static_cast(child); + if ((qcn->qmlBaseNode() == 0) && !qcn->qmlBaseName().isEmpty()) { + QmlClassNode* bqcn = findQmlType(QString(), qcn->qmlBaseName()); + if (bqcn) { + qcn->setQmlBaseNode(bqcn); + } + } + } + } +} +#endif + /*! */ void QDocDatabase::resolveTargets(InnerNode* root) diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h index 9c6810941d..19dde361f8 100644 --- a/src/tools/qdoc/qdocdatabase.h +++ b/src/tools/qdoc/qdocdatabase.h @@ -107,6 +107,7 @@ class QDocDatabase DocNode* addToQmlModule(const QString& name, Node* node); QmlClassNode* findQmlType(const QString& qmid, const QString& name) const; + QmlClassNode* findQmlType(const ImportRec& import, const QString& name) const; void findAllClasses(const InnerNode *node); void findAllFunctions(const InnerNode *node); diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp index 358c2a14d9..63d8639d0f 100644 --- a/src/tools/qdoc/qdocindexfiles.cpp +++ b/src/tools/qdoc/qdocindexfiles.cpp @@ -190,6 +190,9 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, ((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) { QmlClassNode* qcn = new QmlClassNode(parent, name); qcn->setTitle(element.attribute("title")); + QString qmlModuleName = element.attribute("qml-module-name"); + QString qmlModuleVersion = element.attribute("qml-module-version"); + qdb_->addToQmlModule(qmlModuleName + " " + qmlModuleVersion, qcn); if (element.hasAttribute("location")) name = element.attribute("location", QString()); if (!indexUrl.isEmpty()) @@ -209,6 +212,31 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, location = Location(name); node = qbtn; } + else if (element.nodeName() == "qmlproperty") { + QmlClassNode* qcn = static_cast(parent); + QString type = element.attribute("type"); + bool attached = false; + if (element.attribute("attached") == "true") + attached = true; + bool readonly = false; + if (element.attribute("writable") == "false") + readonly = true; + QmlPropertyNode* qpn = new QmlPropertyNode(qcn, name, type, attached); + qpn->setReadOnly(readonly); + node = qpn; + } + else if ((element.nodeName() == "qmlmethod") || + (element.nodeName() == "qmlsignal") || + (element.nodeName() == "qmlsignalhandler")) { + Node::Type t = Node::QmlMethod; + if (element.nodeName() == "qmlsignal") + t = Node::QmlSignal; + else if (element.nodeName() == "qmlsignalhandler") + t = Node::QmlSignalHandler; + bool attached = false; + FunctionNode* fn = new FunctionNode(t, parent, name, attached); + node = fn; + } else if (element.nodeName() == "page") { Node::SubType subtype; Node::PageType ptype = Node::NoPageType; @@ -476,17 +504,22 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, InnerNode* inner = static_cast(node); QDomElement child = element.firstChildElement(); while (!child.isNull()) { - if (element.nodeName() == "class") + if (element.nodeName() == "class") { readIndexSection(child, inner, indexUrl); - else if (element.nodeName() == "qmlclass") + } + else if (element.nodeName() == "qmlclass") { readIndexSection(child, inner, indexUrl); - else if (element.nodeName() == "page") + } + else if (element.nodeName() == "page") { readIndexSection(child, inner, indexUrl); - else if (element.nodeName() == "namespace" && !name.isEmpty()) + } + else if (element.nodeName() == "namespace" && !name.isEmpty()) { // The root node in the index is a namespace with an empty name. readIndexSection(child, inner, indexUrl); - else + } + else { readIndexSection(child, parent, indexUrl); + } child = child.nextSiblingElement(); } } @@ -562,6 +595,8 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, return false; QString nodeName; + QString qmlModuleName; + QString qmlModuleVersion; switch (node->type()) { case Node::Namespace: nodeName = "namespace"; @@ -571,8 +606,11 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, break; case Node::Document: nodeName = "page"; - if (node->subType() == Node::QmlClass) + if (node->subType() == Node::QmlClass) { nodeName = "qmlclass"; + qmlModuleName = node->qmlModuleName(); + qmlModuleVersion = node->qmlModuleVersion(); + } else if (node->subType() == Node::QmlBasicType) nodeName = "qmlbasictype"; break; @@ -687,6 +725,10 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, writer.writeAttribute("status", status); writer.writeAttribute("name", objName); + if (!qmlModuleName.isEmpty()) { + writer.writeAttribute("qml-module-name", qmlModuleName); + writer.writeAttribute("qml-module-version", qmlModuleVersion); + } QString fullName = node->fullDocumentName(); if (fullName != objName) writer.writeAttribute("fullname", fullName); diff --git a/src/tools/qdoc/qmlvisitor.cpp b/src/tools/qdoc/qmlvisitor.cpp index 15af03d9ba..8b6f81de3e 100644 --- a/src/tools/qdoc/qmlvisitor.cpp +++ b/src/tools/qdoc/qmlvisitor.cpp @@ -373,6 +373,24 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation, } } +/*! + Reconstruct the qualified \a id using dot notation + and return the fully qualified string. + */ +QString QmlDocVisitor::getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id) +{ + QString result; + if (id) { + result = id->name.toString(); + id = id->next; + while (id != 0) { + result += QChar('.') + id->name.toString(); + id = id->next; + } + } + return result; +} + /*! Begin the visit of the object \a definition, recording it in the qdoc database. Increment the object nesting level, which is used @@ -381,7 +399,7 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation, */ bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition) { - QString type = definition->qualifiedTypeNameId->name.toString(); + QString type = getFullyQualifiedId(definition->qualifiedTypeNameId); nestingLevel++; if (current->type() == Node::Namespace) { @@ -420,17 +438,18 @@ void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *definition) */ bool QmlDocVisitor::visit(QQmlJS::AST::UiImportList *imports) { - QQmlJS::AST::UiImport* imp = imports->import; - quint32 length = imp->versionToken.offset - imp->fileNameToken.offset - 1; - QString module = document.mid(imp->fileNameToken.offset,length); - QString version = document.mid(imp->versionToken.offset, imp->versionToken.length); - if (version.size() > 1) { - int dot = version.lastIndexOf(QChar('.')); - if (dot > 0) - version = version.left(dot); - } - importList.append(QPair(module, version)); + while (imports != 0) { + QQmlJS::AST::UiImport* imp = imports->import; + QString name = document.mid(imp->fileNameToken.offset, imp->fileNameToken.length); + if (name[0] == '\"') + name = name.mid(1, name.length()-2); + QString version = document.mid(imp->versionToken.offset, imp->versionToken.length); + QString importId = document.mid(imp->importIdToken.offset, imp->importIdToken.length); + QString importUri = getFullyQualifiedId(imp->importUri); + importList.append(ImportRec(name, version, importId, importUri)); + imports = imports->next; + } return true; } diff --git a/src/tools/qdoc/qmlvisitor.h b/src/tools/qdoc/qmlvisitor.h index 172aca579a..bfec61eb34 100644 --- a/src/tools/qdoc/qmlvisitor.h +++ b/src/tools/qdoc/qmlvisitor.h @@ -97,6 +97,7 @@ public: void endVisit(QQmlJS::AST::UiQualifiedId *); private: + QString getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id); QQmlJS::AST::SourceLocation precedingComment(quint32 offset) const; bool applyDocumentation(QQmlJS::AST::SourceLocation location, Node *node); void applyMetacommands(QQmlJS::AST::SourceLocation location, Node* node, Doc& doc); @@ -110,7 +111,7 @@ private: QString filePath; QString name; QString document; - QList > importList; + ImportList importList; QSet commands; QSet topics; QSet usedComments;