From 9cdca3a92b470c010aba563f30f1997ac0007821 Mon Sep 17 00:00:00 2001 From: Roberto Raggi <roberto.raggi@nokia.com> Date: Tue, 26 Jan 2010 17:23:18 +0100 Subject: [PATCH] Some initial support for relative imports. --- src/libs/qmljs/qmljsbind.cpp | 83 ++++++---- src/libs/qmljs/qmljsdocument.cpp | 28 ++-- src/libs/qmljs/qmljsdocument.h | 13 +- src/plugins/qmljseditor/qmlcodecompletion.cpp | 145 ------------------ src/plugins/qmljseditor/qmlhoverhandler.cpp | 4 +- src/plugins/qmljseditor/qmllookupcontext.cpp | 2 +- 6 files changed, 88 insertions(+), 187 deletions(-) diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index cf042d9e538..2a4713eda9c 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -30,6 +30,7 @@ #include "parser/qmljsast_p.h" #include "qmljsbind.h" #include "qmljsmetatypesystem.h" +#include <QtCore/QDebug> using namespace QmlJS; using namespace QmlJS::AST; @@ -125,7 +126,10 @@ static QString serialize(UiQualifiedId *qualifiedId, QChar delimiter) */ bool Bind::visit(UiImport *ast) { - ObjectValue *namespaceObject; + if (! (ast->importUri || ast->fileName)) + return false; // nothing to do. + + ObjectValue *namespaceObject = 0; if (ast->asToken.isValid()) { // with namespace we insert an object in the type env. to hold the imported types if (!ast->importId) @@ -142,31 +146,56 @@ bool Bind::visit(UiImport *ast) // look at files first // else try the metaobject system - if (!ast->importUri) - return false; - - const QString package = serialize(ast->importUri, '/'); - int majorVersion = -1; // ### TODO: Check these magic version numbers - int minorVersion = -1; // ### TODO: Check these magic version numbers - - if (ast->versionToken.isValid()) { - const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length); - const int dotIdx = versionString.indexOf(QLatin1Char('.')); - if (dotIdx == -1) { - // only major (which is probably invalid, but let's handle it anyway) - majorVersion = versionString.toInt(); - minorVersion = 0; // ### TODO: Check with magic version numbers above - } else { - majorVersion = versionString.left(dotIdx).toInt(); - minorVersion = versionString.mid(dotIdx + 1).toInt(); + if (ast->importUri) { + const QString package = serialize(ast->importUri, '/'); + int majorVersion = -1; // ### TODO: Check these magic version numbers + int minorVersion = -1; // ### TODO: Check these magic version numbers + + if (ast->versionToken.isValid()) { + const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length); + const int dotIdx = versionString.indexOf(QLatin1Char('.')); + if (dotIdx == -1) { + // only major (which is probably invalid, but let's handle it anyway) + majorVersion = versionString.toInt(); + minorVersion = 0; // ### TODO: Check with magic version numbers above + } else { + majorVersion = versionString.left(dotIdx).toInt(); + minorVersion = versionString.mid(dotIdx + 1).toInt(); + } } - } - #ifndef NO_DECLARATIVE_BACKEND - foreach (QmlObjectValue *object, _interp->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) { - namespaceObject->setProperty(object->qmlTypeName(), object); - } + foreach (QmlObjectValue *object, _interp->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) { + namespaceObject->setProperty(object->qmlTypeName(), object); + } #endif // NO_DECLARATIVE_BACKEND + } else if (ast->fileName) { + // got an import "contents" + const QString relativePath = ast->fileName->asString(); + const QList<Document::Ptr> userComponents = _snapshot.importedDocuments(_doc, relativePath); + foreach (Document::Ptr userComponent, userComponents) { + if (UiProgram *program = userComponent->qmlProgram()) { + if (UiObjectMemberList *members = program->members) { + if (UiObjectDefinition *def = cast<UiObjectDefinition *>(members->member)) { + const ObjectValue *prototype = lookupType(def->qualifiedTypeNameId); + ObjectValue *objectValue = _interp->newObject(prototype); + if (def->initializer) { + for (AST::UiObjectMemberList *it = def->initializer->members; it; it = it->next) { + if (AST::UiPublicMember *prop = AST::cast<AST::UiPublicMember *>(it->member)) { + if (prop->name && prop->memberType) { + const QString propName = prop->name->asString(); + const QString propType = prop->memberType->asString(); + objectValue->setProperty(propName, _interp->defaultValueForBuiltinType(propType)); + } + } + } + } + + _typeEnvironment->setProperty(userComponent->componentName(), objectValue); + } + } + } + } + } return false; } @@ -199,15 +228,15 @@ const ObjectValue *Bind::lookupType(UiQualifiedId *qualifiedTypeNameId) { const ObjectValue *objectValue = _typeEnvironment; - for (UiQualifiedId *iter = qualifiedTypeNameId; iter; iter = iter->next) { - if (! (iter->name)) + for (UiQualifiedId *iter = qualifiedTypeNameId; objectValue && iter; iter = iter->next) { + if (! iter->name) return 0; + const Value *value = objectValue->property(iter->name->asString()); if (!value) return 0; + objectValue = value->asObjectValue(); - if (!objectValue) - return 0; } return objectValue; diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index ff736fff68f..b8aef81f6af 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -34,6 +34,7 @@ #include <qmljs/parser/qmljsparser_p.h> #include <qmljs/parser/qmljsnodepool_p.h> #include <qmljs/parser/qmljsastfwd_p.h> +#include <QtCore/QDir> using namespace QmlJS; using namespace QmlJS; @@ -47,12 +48,14 @@ Document::Document(const QString &fileName) , _parsedCorrectly(false) , _fileName(fileName) { - const int slashIdx = fileName.lastIndexOf('/'); - if (slashIdx != -1) - _path = fileName.left(slashIdx); - - if (fileName.toLower().endsWith(".qml")) - _componentName = fileName.mid(slashIdx + 1, fileName.size() - (slashIdx + 1) - 4); + QFileInfo fileInfo(fileName); + _path = fileInfo.absolutePath(); + _componentName = fileInfo.baseName(); + if (! _componentName.isEmpty()) { + // ### TODO: check the component name. + if (! _componentName.at(0).isUpper()) + _componentName.clear(); + } } Document::~Document() @@ -220,7 +223,7 @@ Snapshot::~Snapshot() void Snapshot::insert(const Document::Ptr &document) { if (document && (document->qmlProgram() || document->jsProgram())) - QMap<QString, Document::Ptr>::insert(document->fileName(), document); + _documents.insert(document->fileName(), document); } Document::PtrList Snapshot::importedDocuments(const Document::Ptr &doc, const QString &importPath) const @@ -229,11 +232,16 @@ Document::PtrList Snapshot::importedDocuments(const Document::Ptr &doc, const QS Document::PtrList result; - const QString docPath = doc->path() + '/' + importPath; + QString docPath = doc->path(); + docPath += QLatin1Char('/'); + docPath += importPath; + docPath = QDir::cleanPath(docPath); - foreach (Document::Ptr candidate, *this) { + foreach (Document::Ptr candidate, _documents) { if (candidate == doc) - continue; + continue; // ignore this document + else if (! candidate->qmlProgram()) + continue; // skip JS documents if (candidate->path() == doc->path() || candidate->path() == docPath) result.append(candidate); diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index bcf57c5ac74..ac3a9c34e04 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -101,16 +101,25 @@ private: QmlJS::Symbol::List _symbols; }; -class QMLJS_EXPORT Snapshot: public QMap<QString, Document::Ptr> +class QMLJS_EXPORT Snapshot { + typedef QMap<QString, Document::Ptr> _Base; + QMap<QString, Document::Ptr> _documents; + public: Snapshot(); ~Snapshot(); + typedef _Base::iterator iterator; + typedef _Base::const_iterator const_iterator; + + const_iterator begin() const { return _documents.begin(); } + const_iterator end() const { return _documents.end(); } + void insert(const Document::Ptr &document); Document::Ptr document(const QString &fileName) const - { return value(fileName); } + { return _documents.value(fileName); } Document::PtrList importedDocuments(const Document::Ptr &doc, const QString &importPath) const; QMap<QString, Document::Ptr> componentsDefinedByImportedDocuments(const Document::Ptr &doc, const QString &importPath) const; diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index e844d179e22..55c64c19fa6 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -95,72 +95,6 @@ static QIcon iconForColor(const QColor &color) return pix; } - -#if 0 -static QString qualifiedNameId(AST::UiQualifiedId *it) -{ - QString text; - - for (; it; it = it->next) { - if (! it->name) - continue; - - text += it->name->asString(); - - if (it->next) - text += QLatin1Char('.'); - } - - return text; -} - -static Interpreter::ObjectValue *newComponent(Interpreter::Engine *engine, const QString &name, - const QHash<QString, Document::Ptr> &userComponents, - QSet<QString> *processed) -{ - if (Interpreter::ObjectValue *object = engine->newQmlObject(name)) - return object; - - else if (! processed->contains(name)) { - processed->insert(name); - - if (Document::Ptr doc = userComponents.value(name)) { - if (AST::UiProgram *program = doc->qmlProgram()) { - if (program->members) { - if (AST::UiObjectDefinition *def = AST::cast<AST::UiObjectDefinition *>(program->members->member)) { - const QString component = qualifiedNameId(def->qualifiedTypeNameId); - Interpreter::ObjectValue *object = newComponent(engine, component, userComponents, processed); - if (def->initializer) { - for (AST::UiObjectMemberList *it = def->initializer->members; it; it = it->next) { - if (AST::UiPublicMember *prop = AST::cast<AST::UiPublicMember *>(it->member)) { - if (prop->name && prop->memberType) { - const QString propName = prop->name->asString(); - const QString propType = prop->memberType->asString(); - - object->setProperty(propName, engine->defaultValueForBuiltinType(propType)); - } - } - } - } - return object; - } - } - } - } - } - - return 0; -} - -static Interpreter::ObjectValue *newComponent(Interpreter::Engine *engine, - const QString &name, - const QHash<QString, Document::Ptr> &userComponents) -{ - QSet<QString> processed; - return newComponent(engine, name, userComponents, &processed); -} -#endif - namespace { class ExpressionUnderCursor @@ -725,36 +659,6 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) userComponents.insert(typeName, doc); } - foreach (Document::Ptr doc, snapshot) { - const QFileInfo fileInfo(doc->fileName()); - const QString absolutePath = fileInfo.absolutePath(); - - // ### generalize - if (fileInfo.suffix() != QLatin1String("qml")) - continue; - else if (absolutePath != currentFilePath && ! isImported(qmlDocument, absolutePath)) - continue; - -#if 0 - QMapIterator<QString, IdSymbol *> it(doc->ids()); - while (it.hasNext()) { - it.next(); - - if (IdSymbol *symbol = it.value()) { - const QString id = it.key(); - - if (symbol->parentNode()) { - const QString component = symbol->parentNode()->name(); - - if (const Interpreter::ObjectValue *object = newComponent(&interp, component, userComponents)) { - interp.globalObject()->setProperty(id, object); - } - } - } - } -#endif - } - // Set up the current scope chain. Interpreter::ObjectValue *scope = interp.globalObject(); @@ -772,55 +676,6 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } } -#if 0 - // ### TODO: remove me. This is just a quick and dirty hack to get some completion - // for the property definitions. - SearchPropertyDefinitions searchPropertyDefinitions; - - const QList<AST::UiPublicMember *> properties = searchPropertyDefinitions(qmlDocument); - foreach (AST::UiPublicMember *prop, properties) { - if (! (prop->name && prop->memberType)) - continue; - - const QString propName = prop->name->asString(); - const QString propType = prop->memberType->asString(); - - interp.globalObject()->setProperty(propName, interp.defaultValueForBuiltinType(propType)); - } - - // Get the name of the declaring item. - QString declaringItemName = QLatin1String("Item"); - - if (AST::UiObjectDefinition *binding = AST::cast<AST::UiObjectDefinition *>(declaringMember)) - declaringItemName = qualifiedNameId(binding->qualifiedTypeNameId); - else if (AST::UiObjectBinding *binding = AST::cast<AST::UiObjectBinding *>(declaringMember)) - declaringItemName = qualifiedNameId(binding->qualifiedTypeNameId); - - Interpreter::ObjectValue *declaringItem = newComponent(&interp, declaringItemName, userComponents); - if (! declaringItem) - declaringItem = interp.newQmlObject(QLatin1String("Item")); - - if (declaringItem) { - scope->setScope(declaringItem); - declaringItem->setScope(interp.globalObject()); - } - - // Get the name of the parent of the declaring item. - QString parentItemName = QLatin1String("Item"); - - if (AST::UiObjectDefinition *binding = AST::cast<AST::UiObjectDefinition *>(parentMember)) - parentItemName = qualifiedNameId(binding->qualifiedTypeNameId); - else if (AST::UiObjectBinding *binding = AST::cast<AST::UiObjectBinding *>(parentMember)) - parentItemName = qualifiedNameId(binding->qualifiedTypeNameId); - - Interpreter::ObjectValue *parentItem = newComponent(&interp, parentItemName, userComponents); - if (! parentItem) - parentItem = interp.newQmlObject(QLatin1String("Item")); - - if (parentItem) - scope->setProperty(QLatin1String("parent"), parentItem); -#endif - Bind bind(qmlDocument, snapshot, &interp); scope = bind(declaringMember); } diff --git a/src/plugins/qmljseditor/qmlhoverhandler.cpp b/src/plugins/qmljseditor/qmlhoverhandler.cpp index f3d3c361258..f132708453e 100644 --- a/src/plugins/qmljseditor/qmlhoverhandler.cpp +++ b/src/plugins/qmljseditor/qmlhoverhandler.cpp @@ -150,9 +150,9 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in if (!scriptEditor) return; - const Snapshot documents = m_modelManager->snapshot(); + const Snapshot snapshot = m_modelManager->snapshot(); const QString fileName = editor->file()->fileName(); - Document::Ptr doc = documents.value(fileName); + Document::Ptr doc = snapshot.document(fileName); if (!doc) return; // nothing to do diff --git a/src/plugins/qmljseditor/qmllookupcontext.cpp b/src/plugins/qmljseditor/qmllookupcontext.cpp index 007afb6651a..dea7547cf03 100644 --- a/src/plugins/qmljseditor/qmllookupcontext.cpp +++ b/src/plugins/qmljseditor/qmllookupcontext.cpp @@ -132,7 +132,7 @@ Symbol *QmlLookupContext::resolve(const QString &name) Symbol *QmlLookupContext::resolveType(const QString &name, const QString &fileName) { // TODO: handle import-as. - Document::Ptr document = _snapshot[fileName]; + Document::Ptr document = _snapshot.document(fileName); if (document.isNull()) return 0; -- GitLab