From 9ea01cf5fb09f5bbef5e55cc0b0f04d48baa8eda Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Tue, 16 Mar 2010 16:34:33 +0100
Subject: [PATCH] Improve Qml code model import handling.

* Fill the snapshot with files that could be imported.
* Implement package imports.

* The qmldir file is not parsed yet.

Reviewed-by: Erik Verbruggen
---
 src/libs/qmljs/qmljsbind.cpp                  |  15 +-
 src/libs/qmljs/qmljsbind.h                    |   2 -
 src/libs/qmljs/qmljscheck.cpp                 |   4 +-
 src/libs/qmljs/qmljscheck.h                   |   2 +-
 src/libs/qmljs/qmljsinterpreter.cpp           |  14 +-
 src/libs/qmljs/qmljsinterpreter.h             |   7 +-
 src/libs/qmljs/qmljslink.cpp                  | 211 ++++++------------
 src/libs/qmljs/qmljslink.h                    |  13 +-
 .../qmljseditor/qmljscodecompletion.cpp       |   2 +-
 src/plugins/qmljseditor/qmljseditor.cpp       |  17 +-
 src/plugins/qmljseditor/qmljseditor.h         |   2 +
 src/plugins/qmljseditor/qmljshoverhandler.cpp |   2 +-
 src/plugins/qmljseditor/qmljsmodelmanager.cpp | 105 +++++++++
 src/plugins/qmljseditor/qmljsmodelmanager.h   |  12 +
 .../qmljseditor/qmljsmodelmanagerinterface.h  |   4 +
 src/plugins/qmlprojectmanager/qmlproject.cpp  |  15 +-
 src/plugins/qmlprojectmanager/qmlproject.h    |   1 +
 17 files changed, 252 insertions(+), 176 deletions(-)

diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp
index 9d4aa676bc9..0c31052bfd0 100644
--- a/src/libs/qmljs/qmljsbind.cpp
+++ b/src/libs/qmljs/qmljsbind.cpp
@@ -59,11 +59,6 @@ QStringList Bind::includedScripts() const
     return _includedScripts;
 }
 
-QStringList Bind::localImports() const
-{
-    return _localImports;
-}
-
 Interpreter::ObjectValue *Bind::currentObjectValue() const
 {
     return _currentObjectValue;
@@ -210,16 +205,8 @@ bool Bind::visit(AST::Program *)
     return true;
 }
 
-bool Bind::visit(UiImport *ast)
+bool Bind::visit(UiImport *)
 {
-    if (ast->fileName) {
-        QString path = _doc->path();
-        path += QLatin1Char('/');
-        path += ast->fileName->asString();
-        path = QDir::cleanPath(path);
-        _localImports.append(path);
-    }
-
     return false;
 }
 
diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h
index 45fd68d030d..e9f5fef931f 100644
--- a/src/libs/qmljs/qmljsbind.h
+++ b/src/libs/qmljs/qmljsbind.h
@@ -51,7 +51,6 @@ public:
     virtual ~Bind();
 
     QStringList includedScripts() const;
-    QStringList localImports() const;
 
     Interpreter::ObjectValue *currentObjectValue() const;
     Interpreter::ObjectValue *idEnvironment() const;
@@ -101,7 +100,6 @@ private:
 
     QHash<AST::Node *, Interpreter::ObjectValue *> _qmlObjects;
     QStringList _includedScripts;
-    QStringList _localImports;
 };
 
 } // end of namespace Qml
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index cf769810047..7ee01be8e8a 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -170,11 +170,11 @@ public:
 } // end of anonymous namespace
 
 
-Check::Check(Document::Ptr doc, const Snapshot &snapshot)
+Check::Check(Document::Ptr doc, const Snapshot &snapshot, const QStringList &importPaths)
     : _doc(doc)
     , _snapshot(snapshot)
     , _context(&_engine)
-    , _link(&_context, doc, snapshot)
+    , _link(&_context, snapshot, importPaths)
     , _scopeBuilder(doc, &_context)
 {
 }
diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h
index 40e30002c72..a27c5cb61c5 100644
--- a/src/libs/qmljs/qmljscheck.h
+++ b/src/libs/qmljs/qmljscheck.h
@@ -41,7 +41,7 @@ namespace QmlJS {
 class QMLJS_EXPORT Check: protected AST::Visitor
 {
 public:
-    Check(Document::Ptr doc, const Snapshot &snapshot);
+    Check(Document::Ptr doc, const Snapshot &snapshot, const QStringList &importPaths);
     virtual ~Check();
 
     QList<DiagnosticMessage> operator()();
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index 618e1e034fe..fb825e7acc8 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -1300,9 +1300,10 @@ Context::~Context()
 {
 }
 
-void Context::build(const QList<Node *> &astPath, QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot)
+void Context::build(const QList<Node *> &astPath, QmlJS::Document::Ptr doc,
+                    const QmlJS::Snapshot &snapshot, const QStringList &importPaths)
 {
-    Link link(this, doc, snapshot);
+    Link link(this, snapshot, importPaths);
     link.scopeChainAt(doc, astPath);
 }
 
@@ -1333,12 +1334,12 @@ void Context::setLookupMode(LookupMode lookupMode)
 
 const ObjectValue *Context::typeEnvironment(const QmlJS::Document *doc) const
 {
-    return _typeEnvironments.value(doc, 0);
+    return _typeEnvironments.value(doc->fileName(), 0);
 }
 
 void Context::setTypeEnvironment(const QmlJS::Document *doc, const ObjectValue *typeEnvironment)
 {
-    _typeEnvironments[doc] = typeEnvironment;
+    _typeEnvironments[doc->fileName()] = typeEnvironment;
 }
 
 const Value *Context::lookup(const QString &name)
@@ -1953,6 +1954,11 @@ QmlObjectValue *MetaTypeSystem::staticTypeForImport(const QString &qualifiedName
     return previousCandidate;
 }
 
+bool MetaTypeSystem::hasPackage(const QString &package) const
+{
+    return _importedTypes.contains(package);
+}
+
 ConvertToNumber::ConvertToNumber(Engine *engine)
     : _engine(engine), _result(0)
 {
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 89ad9d02bd5..7c9d0201d9d 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -289,7 +289,8 @@ public:
     Context(Engine *engine);
     ~Context();
 
-    void build(const QList<AST::Node *> &astPath, const Document::Ptr doc, const Snapshot &snapshot);
+    void build(const QList<AST::Node *> &astPath, const Document::Ptr doc,
+               const Snapshot &snapshot, const QStringList &importPaths);
 
     Engine *engine() const;
     const ScopeChain &scopeChain() const;
@@ -313,7 +314,7 @@ private:
     Engine *_engine;
     LookupMode _lookupMode;
     QHash<const ObjectValue *, Properties> _properties;
-    QHash<const Document *, const ObjectValue *> _typeEnvironments;
+    QHash<QString, const ObjectValue *> _typeEnvironments;
     ScopeChain _scopeChain;
     int _qmlScopeObjectIndex;
     bool _qmlScopeObjectSet;
@@ -526,6 +527,8 @@ public:
     QList<Interpreter::QmlObjectValue *> staticTypesForImport(const QString &prefix, int majorVersion, int minorVersion) const;
     Interpreter::QmlObjectValue *staticTypeForImport(const QString &qualifiedName) const;
 
+    bool hasPackage(const QString &package) const;
+
 private:
     QHash<QString, QList<QmlObjectValue *> > _importedTypes;
 };
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index e99c3bf7404..dfadb9d91da 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -13,11 +13,14 @@ using namespace QmlJS;
 using namespace QmlJS::Interpreter;
 using namespace QmlJS::AST;
 
-Link::Link(Context *context, Document::Ptr currentDoc, const Snapshot &snapshot)
+Link::Link(Context *context, const Snapshot &snapshot,
+           const QStringList &importPaths)
     : _snapshot(snapshot)
     , _context(context)
+    , _importPaths(importPaths)
 {
-    _docs = reachableDocuments(currentDoc, snapshot);
+    foreach (Document::Ptr doc, snapshot)
+        _documentByPath.insert(doc->path(), doc);
     linkImports();
 }
 
@@ -58,7 +61,7 @@ void Link::scopeChainAt(Document::Ptr doc, const QList<Node *> &astPath)
         // the global scope of a js file does not see the instantiating component
         if (astPath.size() > 0) {
             // add scope chains for all components that source this document
-            foreach (Document::Ptr otherDoc, _docs) {
+            foreach (Document::Ptr otherDoc, _snapshot) {
                 if (otherDoc->bind()->includedScripts().contains(doc->fileName())) {
                     ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
                     componentScopes.insert(otherDoc.data(), component);
@@ -93,7 +96,7 @@ void Link::makeComponentChain(
     Bind *bind = doc->bind();
 
     // add scopes for all components instantiating this one
-    foreach (Document::Ptr otherDoc, _docs) {
+    foreach (Document::Ptr otherDoc, _snapshot) {
         if (otherDoc == doc)
             continue;
         if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) {
@@ -129,7 +132,7 @@ void Link::makeComponentChain(
 
 void Link::linkImports()
 {
-    foreach (Document::Ptr doc, _docs) {
+    foreach (Document::Ptr doc, _snapshot) {
         ObjectValue *typeEnv = engine()->newObject(/*prototype =*/0); // ### FIXME
 
         // Populate the _typeEnvironment with imports.
@@ -142,6 +145,9 @@ void Link::linkImports()
 static QString componentName(const QString &fileName)
 {
     QString componentName = fileName;
+    int sepIndex = componentName.lastIndexOf(QDir::separator());
+    if (sepIndex != -1)
+        componentName.remove(0, sepIndex + 1);
     int dotIndex = componentName.indexOf(QLatin1Char('.'));
     if (dotIndex != -1)
         componentName.truncate(dotIndex);
@@ -159,22 +165,13 @@ void Link::populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Pt
     if (scriptValue)
         typeEnv->setProperty("Script", scriptValue);
 
-    QFileInfo fileInfo(doc->fileName());
-    const QString absolutePath = fileInfo.absolutePath();
-
     // implicit imports:
     // qml files in the same directory are available without explicit imports
-    foreach (Document::Ptr otherDoc, _docs) {
+    foreach (Document::Ptr otherDoc, _documentByPath.values(doc->path())) {
         if (otherDoc == doc)
             continue;
 
-        QFileInfo otherFileInfo(otherDoc->fileName());
-        const QString otherAbsolutePath = otherFileInfo.absolutePath();
-
-        if (otherAbsolutePath != absolutePath)
-            continue;
-
-        typeEnv->setProperty(componentName(otherFileInfo.fileName()),
+        typeEnv->setProperty(componentName(otherDoc->fileName()),
                              otherDoc->bind()->rootObjectValue());
     }
 
@@ -184,7 +181,7 @@ void Link::populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Pt
             continue;
 
         if (it->import->fileName) {
-            importFile(typeEnv, doc, it->import, absolutePath);
+            importFile(typeEnv, doc, it->import);
         } else if (it->import->importUri) {
             importNonFile(typeEnv, doc, it->import);
         }
@@ -200,46 +197,44 @@ void Link::populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Pt
     import "http://www.ovi.com/" as Ovi
 */
 void Link::importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
-                      AST::UiImport *import, const QString &startPath)
+                      AST::UiImport *import)
 {
     Q_UNUSED(doc)
 
     if (!import->fileName)
         return;
 
-    QString path = startPath;
+    QString path = doc->path();
     path += QLatin1Char('/');
     path += import->fileName->asString();
     path = QDir::cleanPath(path);
 
-    ObjectValue *importNamespace = 0;
-
-    foreach (Document::Ptr otherDoc, _docs) {
-        QFileInfo otherFileInfo(otherDoc->fileName());
-        const QString otherAbsolutePath = otherFileInfo.absolutePath();
-
-        bool directoryImport = (path == otherAbsolutePath);
-        bool fileImport = (path == otherDoc->fileName());
-        if (!directoryImport && !fileImport)
-            continue;
+    ObjectValue *importNamespace = typeEnv;
 
-        if (directoryImport && import->importId && !importNamespace) {
+    // directory import
+    if (_documentByPath.contains(path)) {
+        if (import->importId) {
             importNamespace = engine()->newObject(/*prototype =*/0);
             typeEnv->setProperty(import->importId->asString(), importNamespace);
         }
 
+        foreach (Document::Ptr importedDoc, _documentByPath.values(path)) {
+            const QString targetName = componentName(importedDoc->fileName());
+            importNamespace->setProperty(targetName, importedDoc->bind()->rootObjectValue());
+        }
+    }
+    // file import
+    else if (Document::Ptr importedDoc = _snapshot.document(path)) {
         QString targetName;
-        if (fileImport && import->importId) {
+        if (import->importId) {
             targetName = import->importId->asString();
         } else {
-            targetName = componentName(otherFileInfo.fileName());
+            targetName = componentName(importedDoc->fileName());
         }
 
-        ObjectValue *importInto = typeEnv;
-        if (importNamespace)
-            importInto = importNamespace;
-
-        importInto->setProperty(targetName, otherDoc->bind()->rootObjectValue());
+        importNamespace->setProperty(targetName, importedDoc->bind()->rootObjectValue());
+    } else {
+        // error!
     }
 }
 
@@ -250,6 +245,9 @@ void Link::importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
 */
 void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, AST::UiImport *import)
 {
+    if (! import->importUri)
+        return;
+
     ObjectValue *namespaceObject = 0;
 
     if (import->importId) { // with namespace we insert an object in the type env. to hold the imported types
@@ -260,28 +258,47 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A
         namespaceObject = typeEnv;
     }
 
-    // try the metaobject system
-    if (import->importUri) {
-        const QString package = Bind::toString(import->importUri, '/');
-        int majorVersion = QmlObjectValue::NoVersion;
-        int minorVersion = QmlObjectValue::NoVersion;
-
-        if (import->versionToken.isValid()) {
-            const QString versionString = doc->source().mid(import->versionToken.offset, import->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();
-            }
+    const QString package = Bind::toString(import->importUri, '/');
+    int majorVersion = QmlObjectValue::NoVersion;
+    int minorVersion = QmlObjectValue::NoVersion;
+
+    if (import->versionToken.isValid()) {
+        const QString versionString = doc->source().mid(import->versionToken.offset, import->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 the package is in the meta type system, use it
+    if (engine()->metaTypeSystem().hasPackage(package)) {
         foreach (QmlObjectValue *object, engine()->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) {
             namespaceObject->setProperty(object->className(), object);
         }
+    } else {
+        // check the filesystem
+        QStringList localImportPaths = _importPaths;
+        localImportPaths.prepend(doc->path());
+        foreach (const QString &importPath, localImportPaths) {
+            QDir dir(importPath);
+            if (!dir.cd(package))
+                continue;
+            if (!dir.exists("qmldir"))
+                continue;
+
+
+            // ### Should read qmldir file and import accordingly.
+            foreach (Document::Ptr otherDoc, _documentByPath.values(dir.path())) {
+                namespaceObject->setProperty(componentName(otherDoc->fileName()), otherDoc->bind()->rootObjectValue());
+            }
+
+            break;
+        }
     }
 }
 
@@ -294,89 +311,3 @@ UiQualifiedId *Link::qualifiedTypeNameId(Node *node)
     else
         return 0;
 }
-
-QT_BEGIN_NAMESPACE
-static uint qHash(Document::Ptr doc)
-{
-    return qHash(doc.data());
-}
-QT_END_NAMESPACE
-
-QList<Document::Ptr> Link::reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot)
-{
-    QSet<Document::Ptr> docs;
-
-    if (! startDoc)
-        return docs.values();
-
-    QMultiHash<QString, Document::Ptr> documentByPath;
-    foreach (Document::Ptr doc, snapshot)
-        documentByPath.insert(doc->path(), doc);
-
-    // ### TODO: This doesn't scale well. Maybe just use the whole snapshot?
-    // Find all documents that (indirectly) include startDoc
-    {
-        QList<Document::Ptr> todo;
-        todo += startDoc;
-
-        while (! todo.isEmpty()) {
-            Document::Ptr doc = todo.takeFirst();
-
-            docs += doc;
-
-            Snapshot::const_iterator it, end = snapshot.end();
-            for (it = snapshot.begin(); it != end; ++it) {
-                Document::Ptr otherDoc = *it;
-                if (docs.contains(otherDoc))
-                    continue;
-
-                QStringList localImports = otherDoc->bind()->localImports();
-                if (localImports.contains(doc->fileName())
-                    || localImports.contains(doc->path())
-                    || otherDoc->bind()->includedScripts().contains(doc->fileName())
-                ) {
-                    todo += otherDoc;
-                }
-            }
-        }
-    }
-
-    // Find all documents that are included by these (even if indirectly).
-    {
-        QSet<QString> processed;
-        QStringList todo;
-        foreach (Document::Ptr doc, docs)
-            todo.append(doc->fileName());
-
-        while (! todo.isEmpty()) {
-            QString path = todo.takeFirst();
-
-            if (processed.contains(path))
-                continue;
-            processed.insert(path);
-
-            if (Document::Ptr doc = snapshot.document(path)) {
-                docs += doc;
-
-                if (doc->qmlProgram())
-                    path = doc->path();
-                else
-                    continue;
-            }
-
-            QStringList localImports;
-            foreach (Document::Ptr doc, documentByPath.values(path)) {
-                if (doc->qmlProgram()) {
-                    docs += doc;
-                    localImports += doc->bind()->localImports();
-                    localImports += doc->bind()->includedScripts();
-                }
-            }
-
-            localImports.removeDuplicates();
-            todo += localImports;
-        }
-    }
-
-    return docs.values();
-}
diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h
index 9bea65b0ea4..b12e0aa46fe 100644
--- a/src/libs/qmljs/qmljslink.h
+++ b/src/libs/qmljs/qmljslink.h
@@ -7,6 +7,7 @@
 
 #include <QtCore/QList>
 #include <QtCore/QHash>
+#include <QtCore/QStringList>
 
 namespace QmlJS {
 
@@ -18,8 +19,8 @@ class NameId;
 class Link
 {
 public:
-    // Link all documents in snapshot reachable from doc.
-    Link(Interpreter::Context *context, Document::Ptr doc, const Snapshot &snapshot);
+    // Link all documents in snapshot
+    Link(Interpreter::Context *context, const Snapshot &snapshot, const QStringList &importPaths);
     ~Link();
 
     // Get the scope chain for the currentObject inside doc.
@@ -33,14 +34,15 @@ private:
             Interpreter::ScopeChain::QmlComponentChain *target,
             QHash<Document *, Interpreter::ScopeChain::QmlComponentChain *> *components);
 
-    static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot);
+    static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot,
+                                                   const QStringList &importPaths);
     static AST::UiQualifiedId *qualifiedTypeNameId(AST::Node *node);
 
     void linkImports();
 
     void populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Ptr doc);
     void importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
-                    AST::UiImport *import, const QString &startPath);
+                    AST::UiImport *import);
     void importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
                        AST::UiImport *import);
     void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId *targetNamespace);
@@ -48,7 +50,8 @@ private:
 private:
     Snapshot _snapshot;
     Interpreter::Context *_context;
-    QList<Document::Ptr> _docs;
+    QMultiHash<QString, Document::Ptr> _documentByPath;
+    const QStringList _importPaths;
 };
 
 } // namespace QmlJS
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp
index d46bdf9ce77..2eab2db74fb 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp
@@ -671,7 +671,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 
     // Set up the current scope chain.
     QList<AST::Node *> astPath = semanticInfo.astPath(editor->position());
-    context.build(astPath , document, snapshot);
+    context.build(astPath , document, snapshot, m_modelManager->importPaths());
 
     // Search for the operator that triggered the completion.
     QChar completionOperator;
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp
index 1d3d24c5a61..e2a99d338bb 100644
--- a/src/plugins/qmljseditor/qmljseditor.cpp
+++ b/src/plugins/qmljseditor/qmljseditor.cpp
@@ -643,6 +643,7 @@ QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
     m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<ModelManagerInterface>();
 
     if (m_modelManager) {
+        m_semanticHighlighter->setModelManager(m_modelManager);
         connect(m_modelManager, SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
                 this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr)));
     }
@@ -980,7 +981,8 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &
 
     Interpreter::Engine interp;
     Interpreter::Context context(&interp);
-    context.build(semanticInfo.astPath(cursorPosition), semanticInfo.document, semanticInfo.snapshot);
+    context.build(semanticInfo.astPath(cursorPosition), semanticInfo.document,
+                  semanticInfo.snapshot, m_modelManager->importPaths());
 
     Evaluate check(&context);
     const Interpreter::Value *value = check.reference(node);
@@ -1266,7 +1268,8 @@ SemanticHighlighter::Source QmlJSTextEditor::currentSource(bool force)
 
 SemanticHighlighter::SemanticHighlighter(QObject *parent)
         : QThread(parent),
-          m_done(false)
+          m_done(false),
+          m_modelManager(0)
 {
 }
 
@@ -1357,8 +1360,16 @@ SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
     semanticInfo.snapshot = snapshot;
     semanticInfo.document = doc;
 
-    Check checker(doc, snapshot);
+    QStringList importPaths;
+    if (m_modelManager)
+        importPaths = m_modelManager->importPaths();
+    Check checker(doc, snapshot, importPaths);
     semanticInfo.semanticMessages = checker();
 
     return semanticInfo;
 }
+
+void SemanticHighlighter::setModelManager(ModelManagerInterface *modelManager)
+{
+    m_modelManager = modelManager;
+}
diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h
index c8b314b224a..179934c9b48 100644
--- a/src/plugins/qmljseditor/qmljseditor.h
+++ b/src/plugins/qmljseditor/qmljseditor.h
@@ -176,6 +176,7 @@ public:
     };
 
     void rehighlight(const Source &source);
+    void setModelManager(ModelManagerInterface *modelManager);
 
 Q_SIGNALS:
     void changed(const QmlJSEditor::Internal::SemanticInfo &semanticInfo);
@@ -193,6 +194,7 @@ private:
     bool m_done;
     Source m_source;
     SemanticInfo m_lastSemanticInfo;
+    ModelManagerInterface *m_modelManager;
 };
 
 class QmlJSTextEditor : public TextEditor::BaseTextEditor
diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp
index bb0c7495a66..c76ad66d346 100644
--- a/src/plugins/qmljseditor/qmljshoverhandler.cpp
+++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp
@@ -172,7 +172,7 @@ void HoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int p
 
             Interpreter::Engine interp;
             Interpreter::Context context(&interp);
-            context.build(astPath, qmlDocument, snapshot);
+            context.build(astPath, qmlDocument, snapshot, m_modelManager->importPaths());
 
             Evaluate check(&context);
             const Interpreter::Value *value = check(node);
diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.cpp b/src/plugins/qmljseditor/qmljsmodelmanager.cpp
index 6ef82166190..e765b5fa377 100644
--- a/src/plugins/qmljseditor/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljseditor/qmljsmodelmanager.cpp
@@ -39,8 +39,10 @@
 #include <texteditor/itexteditor.h>
 
 #include <QDir>
+#include <QDirIterator>
 #include <QFile>
 #include <QFileInfo>
+#include <QLibraryInfo>
 #include <QtConcurrentRun>
 #include <qtconcurrent/runextensions.h>
 #include <QTextStream>
@@ -51,6 +53,8 @@ using namespace QmlJS;
 using namespace QmlJSEditor;
 using namespace QmlJSEditor::Internal;
 
+static QStringList environmentImportPaths();
+
 ModelManager::ModelManager(QObject *parent):
         ModelManagerInterface(parent),
         m_core(Core::ICore::instance())
@@ -63,6 +67,10 @@ ModelManager::ModelManager(QObject *parent):
             this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr)));
 
     loadQmlTypeDescriptions();
+
+    m_defaultImportPaths << environmentImportPaths();
+    m_defaultImportPaths << QLibraryInfo::location(QLibraryInfo::ImportsPath);
+    refreshSourceDirectories(m_defaultImportPaths);
 }
 
 void ModelManager::loadQmlTypeDescriptions()
@@ -124,6 +132,42 @@ QFuture<void> ModelManager::refreshSourceFiles(const QStringList &sourceFiles)
     return result;
 }
 
+void ModelManager::updateSourceDirectories(const QStringList &directories)
+{
+    refreshSourceDirectories(directories);
+}
+
+QFuture<void> ModelManager::refreshSourceDirectories(const QStringList &sourceDirectories)
+{
+    if (sourceDirectories.isEmpty()) {
+        return QFuture<void>();
+    }
+
+    const QMap<QString, WorkingCopy> workingCopy = buildWorkingCopyList();
+
+    QFuture<void> result = QtConcurrent::run(&ModelManager::parseDirectories,
+                                              workingCopy, sourceDirectories,
+                                              this);
+
+    if (m_synchronizer.futures().size() > 10) {
+        QList<QFuture<void> > futures = m_synchronizer.futures();
+
+        m_synchronizer.clearFutures();
+
+        foreach (QFuture<void> future, futures) {
+            if (! (future.isFinished() || future.isCanceled()))
+                m_synchronizer.addFuture(future);
+        }
+    }
+
+    m_synchronizer.addFuture(result);
+
+    m_core->progressManager()->addTask(result, tr("Indexing"),
+                                       QmlJSEditor::Constants::TASK_INDEX);
+
+    return result;
+}
+
 QMap<QString, ModelManager::WorkingCopy> ModelManager::buildWorkingCopyList()
 {
     QMap<QString, WorkingCopy> workingCopy;
@@ -212,6 +256,32 @@ void ModelManager::parse(QFutureInterface<void> &future,
     future.setProgressValue(files.size());
 }
 
+void ModelManager::parseDirectories(QFutureInterface<void> &future,
+                                    QMap<QString, WorkingCopy> workingCopy,
+                                    QStringList directories,
+                                    ModelManager *modelManager)
+{
+    Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase();
+    Core::MimeType jsSourceTy = db->findByType(QLatin1String("application/javascript"));
+    Core::MimeType qmlSourceTy = db->findByType(QLatin1String("application/x-qml"));
+
+    QStringList pattern;
+    foreach (const QRegExp &glob, jsSourceTy.globPatterns())
+        pattern << glob.pattern();
+    foreach (const QRegExp &glob, qmlSourceTy.globPatterns())
+        pattern << glob.pattern();
+
+    QStringList importedFiles;
+    foreach (const QString &path, directories) {
+        QDirIterator fileIterator(path, pattern, QDir::Files,
+                                  QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
+        while (fileIterator.hasNext())
+            importedFiles << fileIterator.next();
+    }
+
+    parse(future, workingCopy, importedFiles, modelManager);
+}
+
 // Check whether fileMimeType is the same or extends knownMimeType
 bool ModelManager::matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType)
 {
@@ -231,3 +301,38 @@ bool ModelManager::matchesMimeType(const Core::MimeType &fileMimeType, const Cor
 
     return false;
 }
+
+void ModelManager::setProjectImportPaths(const QStringList &importPaths)
+{
+    m_projectImportPaths = importPaths;
+
+    refreshSourceDirectories(importPaths);
+}
+
+QStringList ModelManager::importPaths() const
+{
+    QStringList paths;
+    paths << m_projectImportPaths;
+    paths << m_defaultImportPaths;
+    return paths;
+}
+
+static QStringList environmentImportPaths()
+{
+    QStringList paths;
+
+    QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
+
+#if defined(Q_OS_WIN)
+    QLatin1Char pathSep(';');
+#else
+    QLatin1Char pathSep(':');
+#endif
+    foreach (const QString &path, QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts)) {
+        QString canonicalPath = QDir(path).canonicalPath();
+        if (!canonicalPath.isEmpty() && !paths.contains(canonicalPath))
+            paths.append(canonicalPath);
+    }
+
+    return paths;
+}
diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.h b/src/plugins/qmljseditor/qmljsmodelmanager.h
index 50e504db2cc..aafdc2a89f5 100644
--- a/src/plugins/qmljseditor/qmljsmodelmanager.h
+++ b/src/plugins/qmljseditor/qmljsmodelmanager.h
@@ -55,9 +55,13 @@ public:
 
     virtual QmlJS::Snapshot snapshot() const;
     virtual void updateSourceFiles(const QStringList &files);
+    virtual void updateSourceDirectories(const QStringList &directories);
 
     void emitDocumentUpdated(QmlJS::Document::Ptr doc);
 
+    virtual void setProjectImportPaths(const QStringList &importPaths);
+    virtual QStringList importPaths() const;
+
 Q_SIGNALS:
     void projectPathChanged(const QString &projectPath);
     void aboutToRemoveFiles(const QStringList &files);
@@ -75,6 +79,7 @@ protected:
     };
 
     QFuture<void> refreshSourceFiles(const QStringList &sourceFiles);
+    QFuture<void> refreshSourceDirectories(const QStringList &sourceDirectories);
     QMap<QString, WorkingCopy> buildWorkingCopyList();
 
     static void parse(QFutureInterface<void> &future,
@@ -82,6 +87,11 @@ protected:
                       QStringList files,
                       ModelManager *modelManager);
 
+    static void parseDirectories(QFutureInterface<void> &future,
+                                 QMap<QString, WorkingCopy> workingCopy,
+                                 QStringList directories,
+                                 ModelManager *modelManager);
+
     void loadQmlTypeDescriptions();
 
 private:
@@ -90,6 +100,8 @@ private:
     mutable QMutex m_mutex;
     Core::ICore *m_core;
     QmlJS::Snapshot _snapshot;
+    QStringList m_projectImportPaths;
+    QStringList m_defaultImportPaths;
 
     QFutureSynchronizer<void> m_synchronizer;
 };
diff --git a/src/plugins/qmljseditor/qmljsmodelmanagerinterface.h b/src/plugins/qmljseditor/qmljsmodelmanagerinterface.h
index f3b3f80dbd4..e1a3ab18910 100644
--- a/src/plugins/qmljseditor/qmljsmodelmanagerinterface.h
+++ b/src/plugins/qmljseditor/qmljsmodelmanagerinterface.h
@@ -54,6 +54,10 @@ public:
 
     virtual QmlJS::Snapshot snapshot() const = 0;
     virtual void updateSourceFiles(const QStringList &files) = 0;
+    virtual void updateSourceDirectories(const QStringList &directories) = 0;
+
+    virtual void setProjectImportPaths(const QStringList &importPaths) = 0;
+    virtual QStringList importPaths() const = 0;
 
 signals:
     void documentUpdated(QmlJS::Document::Ptr doc);
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index b529db36a99..ccbb81e6c00 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -95,6 +95,8 @@ void QmlProject::parseProject(RefreshOptions options)
                     && qobject_cast<QmlProjectItem*>(component->create())) {
                     m_projectItem = qobject_cast<QmlProjectItem*>(component->create());
                     connect(m_projectItem.data(), SIGNAL(qmlFilesChanged()), this, SLOT(refreshFiles()));
+                    connect(m_projectItem.data(), SIGNAL(libraryPathsChanged()), this, SLOT(refreshImportPaths()));
+                    refreshImportPaths();
                 } else {
                     Core::MessageManager *messageManager = Core::ICore::instance()->messageManager();
                     messageManager->printToOutputPane(tr("Error while loading project file!"));
@@ -104,7 +106,13 @@ void QmlProject::parseProject(RefreshOptions options)
         }
         if (m_projectItem) {
             m_projectItem.data()->setSourceDirectory(projectDir().path());
-            m_modelManager->updateSourceFiles(m_projectItem.data()->files());
+
+            QSet<QString> sourceDirectories;
+            foreach (const QString &file, m_projectItem.data()->files()) {
+                sourceDirectories.insert(QFileInfo(file).path());
+            }
+
+            m_modelManager->updateSourceDirectories(sourceDirectories.toList());
         }
         m_rootNode->refresh();
     }
@@ -185,6 +193,11 @@ void QmlProject::refreshFiles()
     refresh(Files);
 }
 
+void QmlProject::refreshImportPaths()
+{
+    m_modelManager->setProjectImportPaths(libraryPaths());
+}
+
 QString QmlProject::displayName() const
 {
     return m_projectName;
diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h
index 02a2fe94fe9..6639a109c11 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.h
+++ b/src/plugins/qmlprojectmanager/qmlproject.h
@@ -106,6 +106,7 @@ public:
 private slots:
     void refreshProjectFile();
     void refreshFiles();
+    void refreshImportPaths();
 
 protected:
     bool fromMap(const QVariantMap &map);
-- 
GitLab