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