From e1b44e870f5c15daad136f609df2175278e1f62c Mon Sep 17 00:00:00 2001 From: Fawzi Mohamed Date: Wed, 16 Oct 2013 15:08:27 +0200 Subject: [PATCH] qmljs: new import/dep tracking Change-Id: I9f4de2a06aad3afb80372a4b80e56db658683575 Reviewed-by: Thomas Hartmann --- src/libs/qmljs/qmljs-lib.pri | 9 +- src/libs/qmljs/qmljs.qbs | 4 +- src/libs/qmljs/qmljsconstants.h | 18 +- src/libs/qmljs/qmljscontext.cpp | 11 +- src/libs/qmljs/qmljscontext.h | 8 +- src/libs/qmljs/qmljsdocument.cpp | 19 + src/libs/qmljs/qmljsdocument.h | 7 + src/libs/qmljs/qmljsimportdependencies.cpp | 826 ++++++++++++++++++ src/libs/qmljs/qmljsimportdependencies.h | 223 +++++ src/libs/qmljs/qmljsinterpreter.h | 2 + src/libs/qmljs/qmljslink.cpp | 13 +- src/libs/qmljs/qmljslink.h | 2 +- src/libs/qmljs/qmljsmodelmanagerinterface.h | 9 +- src/libs/qmljs/qmljsviewercontext.cpp | 82 ++ src/libs/qmljs/qmljsviewercontext.h | 63 ++ .../metainfo/subcomponentmanager.cpp | 2 +- .../designercore/model/texttomodelmerger.cpp | 10 +- .../qmljseditor/qmljsfindreferences.cpp | 2 +- .../qmljseditor/qmljssemanticinfoupdater.cpp | 2 +- src/plugins/qmljseditor/qmltaskmanager.cpp | 6 +- src/plugins/qmljseditor/qmltaskmanager.h | 2 +- src/plugins/qmljstools/qmljsmodelmanager.cpp | 28 + src/plugins/qmljstools/qmljsmodelmanager.h | 9 + src/plugins/qmljstools/qmljstools_test.cpp | 2 +- 24 files changed, 1327 insertions(+), 32 deletions(-) create mode 100644 src/libs/qmljs/qmljsimportdependencies.cpp create mode 100644 src/libs/qmljs/qmljsimportdependencies.h create mode 100644 src/libs/qmljs/qmljsviewercontext.cpp create mode 100644 src/libs/qmljs/qmljsviewercontext.h diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri index d4e83127daa..e2c78c4d72d 100644 --- a/src/libs/qmljs/qmljs-lib.pri +++ b/src/libs/qmljs/qmljs-lib.pri @@ -39,7 +39,9 @@ HEADERS += \ $$PWD/qmljssimplereader.h \ $$PWD/persistenttrie.h \ $$PWD/qmljsqrcparser.h \ - $$PWD/qmljsconstants.h + $$PWD/qmljsconstants.h \ + $$PWD/qmljsimportdependencies.h \ + $$PWD/viewercontext.h SOURCES += \ $$PWD/qmljsbind.cpp \ @@ -70,7 +72,10 @@ SOURCES += \ $$PWD/consoleitem.cpp \ $$PWD/qmljssimplereader.cpp \ $$PWD/persistenttrie.cpp \ - $$PWD/qmljsqrcparser.cpp + $$PWD/qmljsqrcparser.cpp \ + $$PWD/qmljsimportdependencies.cpp \ + $$PWD/qmljsviewercontext.cpp + RESOURCES += \ $$PWD/qmljs.qrc diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs index c79197475fe..94968cf313b 100644 --- a/src/libs/qmljs/qmljs.qbs +++ b/src/libs/qmljs/qmljs.qbs @@ -35,6 +35,7 @@ QtcLibrary { "qmljsevaluate.cpp", "qmljsevaluate.h", "qmljsicons.cpp", "qmljsicons.h", "qmljsicontextpane.h", + "qmljsimportdependencies.cpp", "qmljsimportdependencies.h", "qmljsindenter.cpp", "qmljsindenter.h", "qmljsinterpreter.cpp", "qmljsinterpreter.h", "qmljslineinfo.cpp", "qmljslineinfo.h", @@ -52,7 +53,8 @@ QtcLibrary { "qmljsstaticanalysismessage.cpp", "qmljsstaticanalysismessage.h", "qmljstypedescriptionreader.cpp", "qmljstypedescriptionreader.h", "qmljsutils.cpp", "qmljsutils.h", - "qmljsvalueowner.cpp", "qmljsvalueowner.h" + "qmljsvalueowner.cpp", "qmljsvalueowner.h", + "qmljsviewercontext.cpp", "qmljsviewercontext.h" ] } diff --git a/src/libs/qmljs/qmljsconstants.h b/src/libs/qmljs/qmljsconstants.h index fcb117050f0..6f0f4c7fadf 100644 --- a/src/libs/qmljs/qmljsconstants.h +++ b/src/libs/qmljs/qmljsconstants.h @@ -35,14 +35,22 @@ namespace QmlJS { namespace ImportType { enum Enum { Invalid, - ImplicitDirectory, Library, - File, Directory, - QrcFile, + ImplicitDirectory, + File, + UnknownFile, // refers a file/directory that wasn't found (or to an url) QrcDirectory, - ImplicitQrcDirectory, - UnknownFile // refers a file/directory that wasn't found + QrcFile +}; +} + +namespace ImportKind { +enum Enum { + Invalid, + Library, + Path, + QrcPath, }; } diff --git a/src/libs/qmljs/qmljscontext.cpp b/src/libs/qmljs/qmljscontext.cpp index ff601e37a63..c04206c1825 100644 --- a/src/libs/qmljs/qmljscontext.cpp +++ b/src/libs/qmljs/qmljscontext.cpp @@ -54,17 +54,20 @@ using namespace QmlJS::AST; QmlJSTextEditorWidget::semanticInfo()::context. */ -ContextPtr Context::create(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports) +ContextPtr Context::create(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, + const ImportsPerDocument &imports, const ViewerContext &vContext) { - QSharedPointer result(new Context(snapshot, valueOwner, imports)); + QSharedPointer result(new Context(snapshot, valueOwner, imports, vContext)); result->_ptr = result; return result; } -Context::Context(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports) +Context::Context(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, + const ImportsPerDocument &imports, const ViewerContext &vContext) : _snapshot(snapshot), _valueOwner(valueOwner), - _imports(imports) + _imports(imports), + _vContext(vContext) { } diff --git a/src/libs/qmljs/qmljscontext.h b/src/libs/qmljs/qmljscontext.h index 63466a2316d..ad3d8ed1187 100644 --- a/src/libs/qmljs/qmljscontext.h +++ b/src/libs/qmljs/qmljscontext.h @@ -32,6 +32,7 @@ #include "qmljs_global.h" #include "qmljsvalueowner.h" +#include "qmljsviewercontext.h" #include @@ -50,7 +51,8 @@ public: typedef QHash > ImportsPerDocument; // Context takes ownership of valueOwner - static ContextPtr create(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports); + static ContextPtr create(const Snapshot &snapshot, ValueOwner *valueOwner, + const ImportsPerDocument &imports, const ViewerContext &vContext); ~Context(); ContextPtr ptr() const; @@ -69,11 +71,13 @@ public: private: // Context takes ownership of valueOwner - Context(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports); + Context(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports, + const ViewerContext &vContext); Snapshot _snapshot; QSharedPointer _valueOwner; ImportsPerDocument _imports; + ViewerContext _vContext; QWeakPointer _ptr; }; diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index fe169f795c4..7d8db768bed 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -30,6 +30,7 @@ #include "qmljsdocument.h" #include "qmljsbind.h" #include "qmljsconstants.h" +#include "qmljsimportdependencies.h" #include #include @@ -396,6 +397,14 @@ Snapshot::~Snapshot() { } +Snapshot::Snapshot(const Snapshot &o) + : _documents(o._documents), + _documentsByPath(o._documentsByPath), + _libraries(o._libraries), + _dependencies(o._dependencies) +{ +} + void Snapshot::insert(const Document::Ptr &document, bool allowInvalid) { if (document && (allowInvalid || document->qmlProgram() || document->jsProgram())) { @@ -427,6 +436,16 @@ void Snapshot::remove(const QString &fileName) } } +const QmlJS::ImportDependencies *Snapshot::importDependencies() const +{ + return &_dependencies; +} + +QmlJS::ImportDependencies *Snapshot::importDependencies() +{ + return &_dependencies; +} + Document::MutablePtr Snapshot::documentFromSource( const QString &code, const QString &fileName, Language::Enum language) const diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index 5d228908808..65712178095 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -39,11 +39,13 @@ #include "parser/qmljsengine_p.h" #include "qmljs_global.h" #include "qmljsconstants.h" +#include "qmljsimportdependencies.h" namespace QmlJS { class Bind; class Snapshot; +class ImportDependencies; class QMLJS_EXPORT Document { @@ -204,9 +206,11 @@ class QMLJS_EXPORT Snapshot QHash _documents; QHash > _documentsByPath; QHash _libraries; + ImportDependencies _dependencies; public: Snapshot(); + Snapshot(const Snapshot &o); ~Snapshot(); typedef _Base::iterator iterator; @@ -219,6 +223,9 @@ public: void insertLibraryInfo(const QString &path, const LibraryInfo &info); void remove(const QString &fileName); + const ImportDependencies *importDependencies() const; + ImportDependencies *importDependencies(); + Document::Ptr document(const QString &fileName) const; QList documentsInDirectory(const QString &path) const; LibraryInfo libraryInfo(const QString &path) const; diff --git a/src/libs/qmljs/qmljsimportdependencies.cpp b/src/libs/qmljs/qmljsimportdependencies.cpp new file mode 100644 index 00000000000..f948a0208ca --- /dev/null +++ b/src/libs/qmljs/qmljsimportdependencies.cpp @@ -0,0 +1,826 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljsimportdependencies.h" +#include "qmljsinterpreter.h" + +#include +#include + +#include + +#include + +namespace QmlJS { + +ImportKind::Enum toImportKind(ImportType::Enum type) +{ + switch (type) { + case ImportType::Invalid: + break; + case ImportType::Library: + return ImportKind::Library; + case ImportType::ImplicitDirectory: + case ImportType::File: + case ImportType::Directory: + case ImportType::UnknownFile: + return ImportKind::Path; + case ImportType::QrcFile: + case ImportType::QrcDirectory: + return ImportKind::QrcPath; + } + return ImportKind::Invalid; +} + +ImportMatchStrength::ImportMatchStrength(QList match) + : m_match(match) +{ } + +int ImportMatchStrength::compareMatch(const ImportMatchStrength &o) const +{ + int len1 = m_match.size(); + int len2 = o.m_match.size(); + int len = ((len1 < len2) ? len1 : len2); + for (int i = 0; i < len; ++ i) { + int v1 = m_match.at(i); + int v2 = o.m_match.at(i); + if (v1 < v2) + return -1; + if (v2 > v1) + return 1; + } + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; + return 0; +} + +bool ImportMatchStrength::hasNoMatch() +{ + return m_match.isEmpty(); +} + +bool ImportMatchStrength::hasMatch() +{ + return !m_match.isEmpty(); +} + +bool operator ==(const ImportMatchStrength &m1, const ImportMatchStrength &m2) +{ + return m1.m_match == m2.m_match; +} + +bool operator !=(const ImportMatchStrength &m1, const ImportMatchStrength &m2) +{ + return !(m1 == m2); +} + +bool operator <(const ImportMatchStrength &m1, const ImportMatchStrength &m2) +{ + return m1.compareMatch(m2) < 0; +} + +ImportKey::ImportKey() + : type(ImportType::Invalid), + majorVersion(LanguageUtils::ComponentVersion::NoVersion), + minorVersion(LanguageUtils::ComponentVersion::NoVersion) +{ } + +void ImportKey::addToHash(QCryptographicHash &hash) const +{ + hash.addData(reinterpret_cast(&type), sizeof(type)); + hash.addData(reinterpret_cast(&majorVersion), sizeof(majorVersion)); + hash.addData(reinterpret_cast(&minorVersion), sizeof(minorVersion)); + foreach (const QString &s, splitPath) { + hash.addData("/", 1); + hash.addData(reinterpret_cast(s.constData()), sizeof(QChar) * s.size()); + } + hash.addData("/", 1); +} + +ImportKey ImportKey::flatKey() const { + switch (type) { + case ImportType::Invalid: + return *this; + case ImportType::ImplicitDirectory: + case ImportType::Library: + case ImportType::File: + case ImportType::Directory: + case ImportType::QrcFile: + case ImportType::QrcDirectory: + case ImportType::UnknownFile: + break; + } + QStringList flatPath = splitPath; + int i = 0; + while (i < flatPath.size()) { + if (flatPath.at(i).startsWith(QLatin1Char('+'))) + flatPath.removeAt(i); + else + ++i; + } + if (flatPath.size() == splitPath.size()) + return *this; + ImportKey res = *this; + res.splitPath = flatPath; + return res; +} + +ImportKey::ImportKey(const ImportInfo &info) + : type(info.type()) + , majorVersion(info.version().majorVersion()) + , minorVersion(info.version().minorVersion()) +{ + splitPath = QFileInfo(info.path()).canonicalFilePath().split(QChar::fromLatin1('/'), + QString::KeepEmptyParts); +} + +QString ImportKey::path() const +{ + QString res = splitPath.join(QString::fromLatin1("/")); + if (res.isEmpty() && !splitPath.isEmpty()) + return QLatin1String("/"); + return res; +} + +ImportMatchStrength ImportKey::matchImport(const ImportKey &o, const ViewerContext &vContext) const +{ + if (majorVersion != o.majorVersion || minorVersion > o.minorVersion) + return ImportMatchStrength(); + bool dirToFile = false; + switch (o.type) { + case ImportType::Invalid: + return ImportMatchStrength(); + case ImportType::ImplicitDirectory: + case ImportType::Directory: + switch (type) { + case ImportType::File: + case ImportType::UnknownFile: + dirToFile = true; + break; + case ImportType::ImplicitDirectory: + case ImportType::Directory: + break; + default: + return ImportMatchStrength(); + } + break; + case ImportType::Library: + if (type != ImportType::Library) + return ImportMatchStrength(); + break; + case ImportType::QrcDirectory: + switch (type) { + case ImportType::QrcFile: + dirToFile = true; + break; + case ImportType::QrcDirectory: + break; + default: + return ImportMatchStrength(); + } + break; + case ImportType::QrcFile: + if (type != ImportType::QrcFile) + return ImportMatchStrength(); + case ImportType::UnknownFile: + case ImportType::File: + switch (type) { + case ImportType::UnknownFile: + case ImportType::File: + break; + default: + return ImportMatchStrength(); + } + break; + } + + QList res; + int iPath1 = 0; + int lenPath1 = splitPath.size(); + int iPath2 = 0; + int lenPath2 = o.splitPath.size(); + if (dirToFile) + --lenPath1; + int iSelector = 0; + int nSelectors = vContext.selectors.size(); + while (iPath1 < lenPath1) { + if (lenPath2 - iPath2 > lenPath1 - iPath1) + return ImportMatchStrength(); + QString p1 = splitPath.at(iPath1); + if (iPath2 < lenPath2) { + QString p2 = splitPath.at(iPath2); + if (p1 == p2) { + ++iPath1; + ++iPath2; + continue; + } + } + if (!p1.startsWith(QLatin1Char('+'))) + return QList(); + QStringRef selectorAtt(&p1, 1, p1.size()-1); + while (iSelector < nSelectors) { + if (selectorAtt == vContext.selectors.at(iSelector)) + break; + ++iSelector; + } + if (iSelector == nSelectors) + return QList(); + res << (nSelectors - iSelector); + ++iSelector; + ++iPath1; + } + if (iPath2 != lenPath2) + return QList(); + if (res.isEmpty()) + res << 0; + return ImportMatchStrength(res); +} + +int ImportKey::compare(const ImportKey &other) const +{ + ImportKind::Enum k1 = toImportKind(type); + ImportKind::Enum k2 = toImportKind(other.type); + if (k1 < k2) + return -1; + if (k1 > k2) + return 1; + int len1 = splitPath.size(); + int len2 = other.splitPath.size(); + int len = ((len1 < len2) ? len1 : len2); + for (int i = 0; i < len; ++ i) { + QString v1 = splitPath.at(i); + QString v2 = other.splitPath.at(i); + if (v1 < v2) + return -1; + if (v2 > v1) + return 1; + } + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; + if (majorVersion < other.majorVersion) + return -1; + if (majorVersion > other.majorVersion) + return 1; + if (minorVersion < other.minorVersion) + return -1; + if (minorVersion > other.minorVersion) + return 1; + if (type < other.type) + return -1; + if (type > other.type) + return 1; + return 0; +} + +bool ImportKey::isDirectoryLike() const +{ + switch (type) { + case ImportType::Directory: + case ImportType::ImplicitDirectory: + case ImportType::QrcDirectory: + return true; + default: + return false; + } +} + +ImportKey::DirCompareInfo ImportKey::compareDir(const ImportKey &superDir) const +{ + // assumes dir/+selectors/file (i.e. no directories inside selectors) + switch (superDir.type) { + case ImportType::UnknownFile: + case ImportType::File: + case ImportType::Directory: + case ImportType::ImplicitDirectory: + if (type != ImportType::File && type != ImportType::ImplicitDirectory + && type != ImportType::Directory && type != ImportType::UnknownFile) + return Incompatible; + break; + case ImportType::QrcDirectory: + case ImportType::QrcFile: + if (type != ImportType::QrcDirectory && type != ImportType::QrcFile) + return Incompatible; + break; + case ImportType::Invalid: + case ImportType::Library: + return Incompatible; + } + bool isDir1 = isDirectoryLike(); + bool isDir2 = superDir.isDirectoryLike(); + int len1 = splitPath.size(); + int len2 = superDir.splitPath.size(); + if (isDir1 && len1 > 0) + --len1; + if (isDir2 && len2 > 0) + --len2; + + int i1 = 0; + int i2 = 0; + while (i1 < len1 && i2 < len2) { + QString p1 = splitPath.at(i1); + QString p2 = superDir.splitPath.at(i2); + if (p1 == p2) { + ++i1; + ++i2; + continue; + } + if (p1.startsWith(QLatin1Char('+'))) { + if (p2.startsWith(QLatin1Char('+'))) + return SameDir; + return SecondInFirst; + } + if (p2.startsWith(QLatin1Char('+'))) + return FirstInSecond; + return Different; + } + if (i1 < len1) { + if (splitPath.at(i1).startsWith(QLatin1Char('+'))) + return SameDir; + return SecondInFirst; + } + if (i2 < len2) { + if (superDir.splitPath.at(i2).startsWith(QLatin1Char('+'))) + return SameDir; + return SecondInFirst; + } + return SameDir; +} + +uint qHash(const ImportKey &info) +{ + uint res = ::qHash(info.type) ^ + ::qHash(info.majorVersion) ^ ::qHash(info.minorVersion); + foreach (const QString &s, info.splitPath) + res = res ^ ::qHash(s); + return res; +} + +bool operator==(const ImportKey &i1, const ImportKey &i2) +{ + return i1.type == i2.type + && i1.splitPath == i2.splitPath + && i1.majorVersion == i2.majorVersion + && i1.minorVersion == i2.minorVersion; +} + +bool operator !=(const ImportKey &i1, const ImportKey &i2) +{ + return ! (i1 == i2); +} + +bool operator <(const ImportKey &i1, const ImportKey &i2) +{ + return i1.compare(i2) < 0; +} + +Export::Export() + : intrinsic(false) +{ } + +Export::Export(ImportKey exportName, QString pathRequired, bool intrinsic) + : exportName(exportName), pathRequired(pathRequired), intrinsic(intrinsic) +{ } + +bool Export::visibleInVContext(const ViewerContext &vContext) const +{ + return pathRequired.isEmpty() || vContext.paths.contains(pathRequired); +} + +bool operator ==(const Export &i1, const Export &i2) +{ + return i1.exportName == i2.exportName + && i1.pathRequired == i2.pathRequired + && i1.intrinsic == i2.intrinsic; +} + +bool operator !=(const Export &i1, const Export &i2) +{ + return !(i1 == i2); +} + +CoreImport::CoreImport() : language(Language::Qml) { } + +CoreImport::CoreImport(const QString &importId, QList possibleExports, + Language::Enum language, QByteArray fingerprint) + : importId(importId), possibleExports(possibleExports), language(language), + fingerprint(fingerprint) +{ } + +bool CoreImport::valid() { + return !fingerprint.isEmpty(); +} + +QByteArray DependencyInfo::calculateFingerprint(const ImportDependencies &deps) +{ + QCryptographicHash hash(QCryptographicHash::Sha1); + rootImport.addToHash(hash); + QStringList coreImports = allCoreImports.toList(); + coreImports.sort(); + foreach (const QString importId, coreImports) { + hash.addData(reinterpret_cast(importId.constData()), importId.size() * sizeof(QChar)); + QByteArray coreImportFingerprint = deps.coreImport(importId).fingerprint; + hash.addData(coreImportFingerprint); + } + hash.addData("/", 1); + QList imports(allImports.toList()); + std::sort(imports.begin(), imports.end()); + foreach (const ImportKey &k, imports) + k.addToHash(hash); + return hash.result(); +} + +MatchedImport::MatchedImport() +{ } + +MatchedImport::MatchedImport(ImportMatchStrength matchStrength, ImportKey importKey, + QString coreImportId) + : matchStrength(matchStrength), importKey(importKey), coreImportId(coreImportId) +{ } + +int MatchedImport::compare(const MatchedImport &o) const { + int res = matchStrength.compareMatch(o.matchStrength); + if (res != 0) + return res; + res = importKey.compare(o.importKey); + if (res != 0) + return res; + res = coreImportId.compare(o.coreImportId); + return res; +} + +bool operator ==(const MatchedImport &m1, const MatchedImport &m2) +{ + return m1.compare(m2) == 0; +} + +bool operator !=(const MatchedImport &m1, const MatchedImport &m2) +{ + return m1.compare(m2) != 0; +} + +bool operator <(const MatchedImport &m1, const MatchedImport &m2) +{ + return m1.compare(m2) < 0; +} + +ImportDependencies::ImportDependencies() +{ } + +ImportDependencies::~ImportDependencies() +{ } + +void ImportDependencies::filter(const ViewerContext &vContext) +{ + QMap newCoreImports; + QMap newImportCache; + QMapIterator j(m_coreImports); + bool hasChanges = false; + while (j.hasNext()) { + j.next(); + const CoreImport &cImport = j.value(); + if (vContext.languageIsCompatible(cImport.language)) { + QList newExports; + foreach (const Export &e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + newExports.append(e); + QStringList &candidateImports = newImportCache[e.exportName]; + if (!candidateImports.contains(cImport.importId)) + candidateImports.append(cImport.importId); + } + } + if (newExports.size() == cImport.possibleExports.size()) { + newCoreImports.insert(cImport.importId, cImport); + } else if (newExports.length() > 0) { + CoreImport newCImport = cImport; + newCImport.possibleExports = newExports; + newCoreImports.insert(newCImport.importId, newCImport); + hasChanges = true; + } else { + hasChanges = true; + } + } else { + hasChanges = true; + } + } + if (!hasChanges) + return; + m_coreImports = newCoreImports; + m_importCache = newImportCache; +} + +CoreImport ImportDependencies::coreImport(const QString &importId) const +{ + return m_coreImports.value(importId); +} + +void ImportDependencies::iterateOnCandidateImports( + const ImportKey &key, const ViewerContext &vContext, + Utils::function + const &iterF) const +{ + switch (key.type) { + case ImportType::Directory: + case ImportType::QrcDirectory: + case ImportType::ImplicitDirectory: + break; + default: + { + QStringList imp = m_importCache.value(key.flatKey()); + foreach (const QString &cImportName, imp) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + ImportMatchStrength m = e.exportName.matchImport(key, vContext); + if (m.hasMatch()) { + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + return; + } + } + QMap::const_iterator lb = m_importCache.lowerBound(key.flatKey()); + QMap::const_iterator end = m_importCache.constEnd(); + while (lb != end) { + ImportKey::DirCompareInfo c = key.compareDir(lb.key()); + if (c == ImportKey::SameDir) { + foreach (const QString &cImportName, lb.value()) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + ImportMatchStrength m = e.exportName.matchImport(key, vContext); + if (m.hasMatch()) { + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + } else if (c != ImportKey::SecondInFirst) { + break; + } + ++lb; + } +} + +class CollectCandidateImports +{ +public: + ImportDependencies::ImportElements &res; + + CollectCandidateImports(ImportDependencies::ImportElements & res) + : res(res) + { } + + bool operator ()(const ImportMatchStrength &m, const Export &e, const CoreImport &cI) const + { + ImportKey flatName = e.exportName.flatKey(); + res[flatName].append(MatchedImport(m, e.exportName, cI.importId)); + return true; + } +}; + +ImportDependencies::ImportElements ImportDependencies::candidateImports( + const ImportKey &key, + const ViewerContext &vContext) const +{ + ImportDependencies::ImportElements res; + CollectCandidateImports collector(res); + iterateOnCandidateImports(key, vContext, collector); + typedef QMap >::iterator iter_t; + iter_t i = res.begin(); + iter_t end = res.end(); + while (i != end) { + std::sort(i.value().begin(), i.value().end()); + ++i; + } + return res; +} + +QList ImportDependencies::createDependencyInfos( + const ImportKey &mainDoc, const ViewerContext &vContext) const +{ + Q_UNUSED(mainDoc); + Q_UNUSED(vContext); + QList res; + QTC_CHECK(false); + return res; +} + +void ImportDependencies::addCoreImport(const CoreImport &import) +{ + CoreImport newImport = import; + if (m_coreImports.contains(import.importId)) { + CoreImport oldVal = m_coreImports.value(import.importId); + foreach (const Export &e, oldVal.possibleExports) + if (!e.intrinsic) + newImport.possibleExports.append(e); + } + m_coreImports.insert(newImport.importId, newImport); +} + +void ImportDependencies::removeCoreImport(const QString &importId) +{ + if (!m_coreImports.contains(importId)) { + qDebug() << "missing importId in removeCoreImport(" << importId << ")"; + return; + } + CoreImport &cImport = m_coreImports[importId]; + QList newExports; + foreach (const Export &e, cImport.possibleExports) + if (!e.intrinsic) + newExports.append(e); + if (newExports.size()>0) + cImport.possibleExports = newExports; + else + m_coreImports.remove(importId); +} + +void ImportDependencies::addExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath) +{ + if (!m_coreImports.contains(importId)) { + CoreImport newImport(importId); + newImport.language = Language::Unknown; + newImport.possibleExports.append(Export(importKey, requiredPath, false)); + m_coreImports.insert(newImport.importId, newImport); + return; + } + CoreImport &importValue = m_coreImports[importId]; + importValue.possibleExports.append(Export(importKey, requiredPath, false)); +} + +void ImportDependencies::removeExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath) +{ + if (!m_coreImports.contains(importId)) { + qDebug() << "non existing core import for removeExport(" << importId << ", " + << importKey.path() << ")"; + } else { + CoreImport &importValue = m_coreImports[importId]; + if (!importValue.possibleExports.removeOne(Export(importKey, requiredPath, false))) { + qDebug() << "non existing export for removeExport(" << importId << ", " + << importKey.path() << ")"; + } + if (importValue.possibleExports.isEmpty() && importValue.fingerprint.isEmpty()) + m_coreImports.remove(importId); + } + if (!m_importCache.contains(importKey)) { + qDebug() << "missing possibleExport for " << importKey.path() << " when removing export of " + << importId; + } else { + QStringList &cImp = m_importCache[importKey]; + if (!cImp.removeOne(importId)) { + qDebug() << "missing possibleExport backpointer for " << importKey.path() << " to " + << importId; + } + if (cImp.isEmpty()) + m_importCache.remove(importKey); + } +} + +void ImportDependencies::iterateOnCoreImports( + const ViewerContext &vContext, + Utils::function const &iterF) const +{ + QMapIterator i(m_coreImports); + while (i.hasNext()) { + i.next(); + if (vContext.languageIsCompatible(i.value().language)) + iterF(i.value()); // check also that at least one export is visible? + } +} + +void ImportDependencies::iterateOnLibraryImports( + const ViewerContext &vContext, + Utils::function const &iterF) const +{ + typedef QMap::const_iterator iter_t; + ImportKey firstLib; + firstLib.type = ImportType::Library; + iter_t i = m_importCache.lowerBound(firstLib); + iter_t end = m_importCache.constEnd(); + while (i != end && i.key().type == ImportType::Library) { + foreach (const QString &cImportName, i.value()) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export &e, cImport.possibleExports) { + if (e.visibleInVContext(vContext) && e.exportName.type == ImportType::Library) { + ImportMatchStrength m = e.exportName.matchImport(i.key(), vContext); + if (m.hasMatch()) { + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + ++i; + } +} + +void ImportDependencies::iterateOnSubImports( + const ImportKey &baseKey, + const ViewerContext &vContext, + Utils::function const &iterF) const +{ + typedef QMap::const_iterator iter_t; + iter_t i = m_importCache.lowerBound(baseKey); + iter_t end = m_importCache.constEnd(); + while (i != end) { + ImportKey::DirCompareInfo c = baseKey.compareDir(i.key()); + if (c != ImportKey::SameDir && c != ImportKey::SecondInFirst) + break; + foreach (const QString &cImportName, i.value()) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export &e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + ImportMatchStrength m = e.exportName.matchImport(i.key(), vContext); + if (m.hasMatch()) { + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + ++i; + } +} + +class CollectImportKeys { +public: + QSet &imports; + CollectImportKeys(QSet &imports) + : imports(imports) + { } + bool operator()(const ImportMatchStrength &m, + const Export &e, + const CoreImport &cI) const + { + Q_UNUSED(m); + Q_UNUSED(cI); + imports.insert(e.exportName.flatKey()); + return true; + } +}; + +QSet ImportDependencies::libraryImports(const ViewerContext &viewContext) const +{ + QSet res; + CollectImportKeys importCollector(res); + iterateOnLibraryImports(viewContext, importCollector); + return res; +} + +QSet ImportDependencies::subdirImports( + const ImportKey &baseKey, const ViewerContext &viewContext) const +{ + QSet res; + CollectImportKeys importCollector(res); + iterateOnSubImports(baseKey, viewContext, importCollector); + return res; +} + +} // namespace QmlJS diff --git a/src/libs/qmljs/qmljsimportdependencies.h b/src/libs/qmljs/qmljsimportdependencies.h new file mode 100644 index 00000000000..5524ec92870 --- /dev/null +++ b/src/libs/qmljs/qmljsimportdependencies.h @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLJSIMPORTCACHE_H +#define QMLJSIMPORTCACHE_H + +#include "qmljsviewercontext.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QCryptographicHash; +QT_END_NAMESPACE + +namespace QmlJS { +class ImportInfo; +namespace Internal { +class ImportDependenciesPrivate; +} +class ImportDependencies; + +// match strenght wrt to the selectors of a ViewerContext +// this is valid only within a ViewerContext +class QMLJS_EXPORT ImportMatchStrength +{ +public: + explicit ImportMatchStrength() {} + ImportMatchStrength(QList match); + + int compareMatch(const ImportMatchStrength &o) const; + + bool hasNoMatch(); + + bool hasMatch(); +private: + friend bool operator ==(const ImportMatchStrength &m1, const ImportMatchStrength &m2); + QList m_match; +}; +bool operator ==(const ImportMatchStrength &m1, const ImportMatchStrength &m2); +bool operator !=(const ImportMatchStrength &m1, const ImportMatchStrength &m2); +bool operator <(const ImportMatchStrength &m1, const ImportMatchStrength &m2); + +/*! + * \brief The ImportKey class represent an import (or export), and can be used as hash key + * + * This represent only what is to be imported, *not* how (i.e. no as clause) + */ +class QMLJS_EXPORT ImportKey +{ +public: + enum DirCompareInfo { + SameDir, + FirstInSecond, + SecondInFirst, + Different, + Incompatible + }; + + explicit ImportKey(); + explicit ImportKey(const ImportInfo &info); + + ImportType::Enum type; + QStringList splitPath; + int majorVersion; + int minorVersion; + + QString path() const; + + void addToHash(QCryptographicHash &hash) const; + ImportKey flatKey() const; + + // wrap QList in a special type? + ImportMatchStrength matchImport(const ImportKey &o, const ViewerContext &vContext) const; + int compare(const ImportKey &other) const; + bool isDirectoryLike() const; + DirCompareInfo compareDir(const ImportKey &other) const; +}; + +uint qHash(const ImportKey &info); +bool operator ==(const ImportKey &i1, const ImportKey &i2); +bool operator !=(const ImportKey &i1, const ImportKey &i2); +bool operator <(const ImportKey &i1, const ImportKey &i2); + +class QMLJS_EXPORT Export +{ +public: + Export(); + Export(ImportKey exportName, QString pathRequired, bool intrinsic = false); + ImportKey exportName; + QString pathRequired; + bool intrinsic; + bool visibleInVContext(const ViewerContext &vContext) const; +}; +bool operator ==(const Export &i1, const Export &i2); +bool operator !=(const Export &i1, const Export &i2); + +class QMLJS_EXPORT CoreImport +{ +public: + CoreImport(); + CoreImport(const QString &importId, QList possibleExports = QList(), + Language::Enum language = Language::Qml, QByteArray fingerprint = QByteArray()); + QString importId; + QList possibleExports; + Language::Enum language; + QByteArray fingerprint; + bool valid(); +}; + +class QMLJS_EXPORT DependencyInfo +{ +public: + typedef QSharedPointer ConstPtr; + typedef QSharedPointer Ptr; + + QByteArray calculateFingerprint(const ImportDependencies &deps); + + ImportKey rootImport; + QSet allCoreImports; + QSet allImports; + QByteArray fingerprint; +}; + +class QMLJS_EXPORT MatchedImport +{ +public: + MatchedImport(); + MatchedImport(ImportMatchStrength matchStrength, ImportKey importKey, QString coreImportId); + + ImportMatchStrength matchStrength; + ImportKey importKey; + QString coreImportId; + int compare(const MatchedImport &o) const; +}; +bool operator ==(const MatchedImport &m1, const MatchedImport &m2); +bool operator !=(const MatchedImport &m1, const MatchedImport &m2); +bool operator <(const MatchedImport &m1, const MatchedImport &m2); + +class QMLJS_EXPORT ImportDependencies +{ +public: + typedef QMap > ImportElements; + + explicit ImportDependencies(); + ~ImportDependencies(); + + void filter(const ViewerContext &vContext); + + CoreImport coreImport(const QString &importId) const; + void iterateOnCandidateImports(const ImportKey &key, const ViewerContext &vContext, + Utils::function const &iterF) const; + ImportElements candidateImports(const ImportKey &key, const ViewerContext &vContext) const; + + QList createDependencyInfos(const ImportKey &mainDoc, + const ViewerContext &vContext) const; + + void addCoreImport(const CoreImport &import); + void removeCoreImport(const QString &importId); + + void addExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath); + void removeExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath); + + void iterateOnCoreImports(const ViewerContext &vContext, + Utils::function const &iterF) const; + void iterateOnLibraryImports(const ViewerContext &vContext, + Utils::function const &iterF) const; + void iterateOnSubImports(const ImportKey &baseKey, const ViewerContext &vContext, + Utils::function const &iterF) const; + + QSet libraryImports(const ViewerContext &viewContext) const; + QSet subdirImports(const ImportKey &baseKey, const ViewerContext &viewContext) const; +private: + QMap m_importCache; + QMap m_coreImports; +}; + +} // namespace QmlJS + +#endif // QMLJSIMPORTCACHE_H diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index cf8d22a3846..ef5577a90cd 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -912,6 +913,7 @@ public: // const! ObjectValue *object; ImportInfo info; + DependencyInfo::ConstPtr deps; // uri imports: path to library, else empty QString libraryPath; // whether the import succeeded diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 950d238f1b3..01100454bc8 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -34,7 +34,8 @@ #include "qmljsbind.h" #include "qmljsutils.h" #include "qmljsmodelmanagerinterface.h" -#include +#include "qmljsqrcparser.h" +#include "qmljsconstants.h" #include #include @@ -83,6 +84,7 @@ public: ValueOwner *valueOwner; QStringList importPaths; LibraryInfo builtins; + ViewerContext vContext; QHash importCache; @@ -131,13 +133,14 @@ public: \l{QmlJSEditor::SemanticInfo} of a \l{QmlJSEditor::QmlJSTextEditorWidget}. */ -Link::Link(const Snapshot &snapshot, const QStringList &importPaths, const LibraryInfo &builtins) +Link::Link(const Snapshot &snapshot, const ViewerContext &vContext, const LibraryInfo &builtins) : d(new LinkPrivate) { d->valueOwner = new ValueOwner; d->snapshot = snapshot; - d->importPaths = importPaths; + d->importPaths = vContext.paths; d->builtins = builtins; + d->vContext = vContext; d->diagnosticMessages = 0; d->allDiagnosticMessages = 0; @@ -173,14 +176,14 @@ Link::Link(const Snapshot &snapshot, const QStringList &importPaths, const Libra ContextPtr Link::operator()(QHash > *messages) { d->allDiagnosticMessages = messages; - return Context::create(d->snapshot, d->valueOwner, d->linkImports()); + return Context::create(d->snapshot, d->valueOwner, d->linkImports(), d->vContext); } ContextPtr Link::operator()(const Document::Ptr &doc, QList *messages) { d->document = doc; d->diagnosticMessages = messages; - return Context::create(d->snapshot, d->valueOwner, d->linkImports()); + return Context::create(d->snapshot, d->valueOwner, d->linkImports(), d->vContext); } Link::~Link() diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index 7d7875e4213..dbb2904e0d2 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -48,7 +48,7 @@ class QMLJS_EXPORT Link Q_DECLARE_TR_FUNCTIONS(QmlJS::Link) public: - Link(const Snapshot &snapshot, const QStringList &importPaths, const LibraryInfo &builtins); + Link(const Snapshot &snapshot, const ViewerContext &vContext, const LibraryInfo &builtins); // Link all documents in snapshot, collecting all diagnostic messages (if messages != 0) ContextPtr operator()(QHash > *messages = 0); diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h index cdcaef362dc..45a7c879919 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.h +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h @@ -33,7 +33,8 @@ #include "qmljs_global.h" #include "qmljsdocument.h" #include "qmljsbundle.h" - +#include "qmljsconstants.h" +#include "qmljsviewercontext.h" #include #include @@ -175,6 +176,12 @@ public: virtual LibraryInfo builtins(const Document::Ptr &doc) const = 0; + virtual ViewerContext completeVContext(const ViewerContext &vCtx, + const Document::Ptr &doc = Document::Ptr(0)) const = 0; + virtual ViewerContext defaultVContext(bool autoComplete = true, + const Document::Ptr &doc = Document::Ptr(0)) const = 0; + virtual void setDefaultVContext(const ViewerContext &vContext) = 0; + // Blocks until all parsing threads are done. Used for testing. virtual void joinAllThreads() = 0; diff --git a/src/libs/qmljs/qmljsviewercontext.cpp b/src/libs/qmljs/qmljsviewercontext.cpp new file mode 100644 index 00000000000..5391a8bc87e --- /dev/null +++ b/src/libs/qmljs/qmljsviewercontext.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljsviewercontext.h" + +namespace QmlJS { +/*! + \class QmlJS::ViewerContext + \brief The ViewerContext class encapsulate selector and paths for a given viewer. + + Using a a different viewer context can emulate (the pure qml part) of a device. + This allows checking how a given qml would be interpreted on another platform/viewer. + + Screen information will also most likely need to be added here. +*/ +ViewerContext::ViewerContext() + : language(Language::Qml), flags(AddAllPaths) +{ } + +ViewerContext::ViewerContext(QStringList selectors, QStringList paths, + QmlJS::Language::Enum language, + QmlJS::ViewerContext::Flags flags) + : selectors(selectors), paths(paths), language(language), + flags(flags) +{ } + + +/* + which languages might be imported in this context + */ +bool ViewerContext::languageIsCompatible(Language::Enum l) const +{ + switch (language) { + case Language::JavaScript: + return l == Language::JavaScript; + case Language::Json: + return l == Language::Json; + case Language::Qml: + return l == Language::Qml || l == Language::QmlQtQuick1 || l == Language::QmlQtQuick2 + || Language::JavaScript; + case Language::QmlProject: + return l == Language::QmlProject; + case Language::QmlQbs: + return l == Language::QmlQbs; + case Language::QmlQtQuick1: + return l == Language::Qml || l == Language::QmlQtQuick1 || Language::JavaScript; + case Language::QmlQtQuick2: + return l == Language::Qml || l == Language::QmlQtQuick2 || Language::JavaScript; + case Language::QmlTypeInfo: + return l == Language::QmlTypeInfo; + case Language::Unknown: // ? + return true; + } +} + +} // namespace QmlJS diff --git a/src/libs/qmljs/qmljsviewercontext.h b/src/libs/qmljs/qmljsviewercontext.h new file mode 100644 index 00000000000..97620b5353e --- /dev/null +++ b/src/libs/qmljs/qmljsviewercontext.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VIEWERCONTEXT_H +#define VIEWERCONTEXT_H + +#include "qmljs_global.h" +#include "qmljsconstants.h" + +#include + +namespace QmlJS { + +class QMLJS_EXPORT ViewerContext +{ +public: + enum Flags { + Complete, + AddAllPaths, + AddQtPath + }; + + ViewerContext(); + ViewerContext(QStringList selectors, QStringList paths, + Language::Enum language = Language::Qml, + Flags flags = AddAllPaths); + + bool languageIsCompatible(Language::Enum l) const; + + QStringList selectors; + QStringList paths; + Language::Enum language; + Flags flags; +}; + +} // namespace QmlJS +#endif // VIEWERCONTEXT_H diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 2405ef8412c..badaa52ba3a 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -101,7 +101,7 @@ static inline bool checkIfDerivedFromItem(const QString &fileName) snapshot.insert(document); - QmlJS::Link link(snapshot, modelManager->importPaths(), QmlJS::ModelManagerInterface::instance()->builtins(document)); + QmlJS::Link link(snapshot, modelManager->defaultVContext(), QmlJS::ModelManagerInterface::instance()->builtins(document)); QList diagnosticLinkMessages; QmlJS::ContextPtr context = link(document, &diagnosticLinkMessages); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 20eec5c8e14..e37d2711605 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -306,10 +306,10 @@ class ReadingContext { public: ReadingContext(const Snapshot &snapshot, const Document::Ptr &doc, - const QStringList importPaths) + const ViewerContext &vContext) : m_snapshot(snapshot) , m_doc(doc) - , m_link(snapshot, importPaths, + , m_link(snapshot, vContext, QmlJS::ModelManagerInterface::instance()->builtins(doc)) , m_context(m_link(doc, &m_diagnosticLinkMessages)) , m_scopeChain(doc, m_context) @@ -751,7 +751,11 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH return false; } snapshot.insert(doc); - ReadingContext ctxt(snapshot, doc, importPaths); + QmlJS::ViewerContext vContext; + vContext.language = QmlJS::Language::Qml; + vContext.paths = importPaths; + vContext.flags = QmlJS::ViewerContext::Complete; + ReadingContext ctxt(snapshot, doc, vContext); m_scopeChain = QSharedPointer( new ScopeChain(ctxt.scopeChain())); m_document = doc; diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index a8b7d70914d..48a16d0de09 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -831,7 +831,7 @@ static void find_helper(QFutureInterface &future, QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); - Link link(snapshot, modelManager->importPaths(), modelManager->builtins(doc)); + Link link(snapshot, modelManager->defaultVContext(), modelManager->builtins(doc)); ContextPtr context = link(); ScopeChain scopeChain(doc, context); diff --git a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp index 2e4a73b57e7..30b3422cc53 100644 --- a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp +++ b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp @@ -122,7 +122,7 @@ QmlJSTools::SemanticInfo SemanticInfoUpdater::makeNewSemanticInfo(const QmlJS::D ModelManagerInterface *modelManager = ModelManagerInterface::instance(); - Link link(semanticInfo.snapshot, modelManager->importPaths(), modelManager->builtins(doc)); + Link link(semanticInfo.snapshot, modelManager->defaultVContext(), modelManager->builtins(doc)); semanticInfo.context = link(doc, &semanticInfo.semanticMessages); ScopeChain *scopeChain = new ScopeChain(doc, semanticInfo.context); diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp index 42f78bec198..40d19319aa9 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.cpp +++ b/src/plugins/qmljseditor/qmltaskmanager.cpp @@ -92,13 +92,13 @@ static QList convertToTasks(const QList &future, Snapshot snapshot, QList projectInfos, - QStringList importPaths, bool updateSemantic) + ViewerContext vContext, bool updateSemantic) { foreach (const ModelManagerInterface::ProjectInfo &info, projectInfos) { QHash > linkMessages; ContextPtr context; if (updateSemantic) { - Link link(snapshot, importPaths, snapshot.libraryInfo(info.qtImportsPath)); + Link link(snapshot, vContext, snapshot.libraryInfo(info.qtImportsPath)); context = link(&linkMessages); } @@ -161,7 +161,7 @@ void QmlTaskManager::updateMessagesNow(bool updateSemantic) QFuture future = QtConcurrent::run( &collectMessages, modelManager->newestSnapshot(), modelManager->projectInfos(), - modelManager->importPaths(), updateSemantic); + modelManager->defaultVContext(), updateSemantic); m_messageCollector.setFuture(future); } diff --git a/src/plugins/qmljseditor/qmltaskmanager.h b/src/plugins/qmljseditor/qmltaskmanager.h index 986a2e65f35..f7aaf0ab742 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.h +++ b/src/plugins/qmljseditor/qmltaskmanager.h @@ -85,7 +85,7 @@ private: static void collectMessages(QFutureInterface &future, QmlJS::Snapshot snapshot, QList projectInfos, - QStringList importPaths, + QmlJS::ViewerContext vContext, bool updateSemantic); private: diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp index 46beb6a3456..33e65996838 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.cpp +++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp @@ -1121,6 +1121,34 @@ LibraryInfo ModelManager::builtins(const Document::Ptr &doc) const return _validSnapshot.libraryInfo(info.qtImportsPath); } +ViewerContext ModelManager::completeVContext(const ViewerContext &vCtx, + const Document::Ptr &doc) const +{ + Q_UNUSED(doc); + ViewerContext res = vCtx; + switch (res.flags) { + case ViewerContext::Complete: + break; + case ViewerContext::AddQtPath: + case ViewerContext::AddAllPaths: + res.paths << importPaths(); + } + return res; +} + +ViewerContext ModelManager::defaultVContext(bool autoComplete, const Document::Ptr &doc) const +{ + if (autoComplete) + return completeVContext(m_vContext, doc); + else + return m_vContext; +} + +void ModelManager::setDefaultVContext(const ViewerContext &vContext) +{ + m_vContext = vContext; +} + void ModelManager::joinAllThreads() { foreach (QFuture future, m_synchronizer.futures()) diff --git a/src/plugins/qmljstools/qmljsmodelmanager.h b/src/plugins/qmljstools/qmljsmodelmanager.h index 04cb3d5d4e9..9b3111afd52 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.h +++ b/src/plugins/qmljstools/qmljsmodelmanager.h @@ -115,6 +115,14 @@ public: QmlJS::LibraryInfo builtins(const QmlJS::Document::Ptr &doc) const QTC_OVERRIDE; + QmlJS::ViewerContext completeVContext( + const QmlJS::ViewerContext &vCtx, + const QmlJS::Document::Ptr &doc = QmlJS::Document::Ptr(0)) const QTC_OVERRIDE; + QmlJS::ViewerContext defaultVContext( + bool autoComplete = true, + const QmlJS::Document::Ptr &doc = QmlJS::Document::Ptr(0)) const QTC_OVERRIDE; + void setDefaultVContext(const QmlJS::ViewerContext &vContext) QTC_OVERRIDE; + void joinAllThreads() QTC_OVERRIDE; public slots: @@ -158,6 +166,7 @@ private: QStringList m_defaultImportPaths; QmlJS::QmlLanguageBundles m_activeBundles; QmlJS::QmlLanguageBundles m_extendedBundles; + QmlJS::ViewerContext m_vContext; QTimer *m_updateCppQmlTypesTimer; QTimer *m_asyncResetTimer; diff --git a/src/plugins/qmljstools/qmljstools_test.cpp b/src/plugins/qmljstools/qmljstools_test.cpp index e4336b01462..010da935dc5 100644 --- a/src/plugins/qmljstools/qmljstools_test.cpp +++ b/src/plugins/qmljstools/qmljstools_test.cpp @@ -53,7 +53,7 @@ void QmlJSTools::Internal::QmlJSToolsPlugin::test_basic() Document::Ptr doc = snapshot.document(welcomescreenRootPath); QVERIFY(doc && doc->isQmlDocument()); - ContextPtr context = Link(snapshot, QStringList(), LibraryInfo())(); + ContextPtr context = Link(snapshot, ViewerContext(), LibraryInfo())(); QVERIFY(context); const CppComponentValue *rectangleValue = context->valueOwner()->cppQmlTypes().objectByQualifiedName(