diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 9d4aa676bc9923c9487e707449cc47cee32a7d4b..0c31052bfd00974d4608f4e8d54f99d8b59ba57a 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 45fd68d030dbe3ab8b2e88c8ffc5715a046873fb..e9f5fef931f9857c287dee83d1f4ac88ff81910f 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 cf769810047e6be03353f0258306847a72a3f599..7ee01be8e8ab43db7f132aa360e7fa3eb60ead21 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 40e30002c7279cda72a13943df1cd624b891bc6c..a27c5cb61c59af0ad0f994e756c48a19e12bc78d 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 618e1e034fedd6d79052ab0916247137a54272df..fb825e7acc8fa5db9b7cbdada9b23d4fde19c97b 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 89ad9d02bd590c9a19f964e04de037862050823d..7c9d0201d9d9a32f040f3f17c35c876d447be80d 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 e99c3bf7404320ea3259647b2b51a1504dafc044..dfadb9d91da86c84085a2f87df0b70c389fe2579 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 9bea65b0ea4b238a853e9af1ec8f825f4713d81a..b12e0aa46fe4e72fabb95660563c0357caee1ccd 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 d46bdf9ce7704d8d21ecdeca7f035e77d7dd21da..2eab2db74fbd189b936d7641e27317ace10ad426 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 1d3d24c5a61ce0d7cdf1790819de6db10f8cba1a..e2a99d338bb8e9f4c548f5d826de1eb3af09529d 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 c8b314b224a02e558b1c2964b71da025f62cc7f1..179934c9b48e33acc266f92ae59ec94c38a0abca 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 bb0c7495a6661d7e15a5c95d318541919669f806..c76ad66d3467d238b798c41b3adf885ec4ceb89d 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 6ef821661908a6a6c3c80c970752465738ccd2f0..e765b5fa3770204221d04c14204a78e73b938abf 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 50e504db2ccc654715f80c35d2bc9c1285d2f2b7..aafdc2a89f5ca0b0b58e54509305c5ff53e5d1d2 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 f3b3f80dbd49699eed5a8b41d92f26c670ee547e..e1a3ab18910afb0195a951e874d81a066d481652 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 b529db36a99ec24b649fdac6157d03aa5b19adeb..ccbb81e6c004c30648ce988b72aa46a6be1aa159 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 02a2fe94fe9328113003af3855a591e1c67095d5..6639a109c1197a5dd39169e256337698f09f3e2f 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);