Commit 2fc150b9 authored by Fawzi Mohamed's avatar Fawzi Mohamed
Browse files

qmljs: scan imports



Change-Id: Ied59f5d56c5816d9da57f23a619d604acec76000
Reviewed-by: default avatarThomas Hartmann <Thomas.Hartmann@digia.com>
parent 7fb87fbb
......@@ -212,7 +212,7 @@ void Document::setLanguage(Language::Enum l)
QString Document::importId() const
{
return path();
return _fileName;
}
QByteArray Document::fingerprint() const
......@@ -501,7 +501,7 @@ void Snapshot::insert(const Document::Ptr &document, bool allowInvalid)
CoreImport cImport;
cImport.importId = document->importId();
cImport.language = document->language();
cImport.possibleExports << Export(ImportKey(ImportType::File, document->path()),
cImport.possibleExports << Export(ImportKey(ImportType::File, fileName),
QString(), true);
cImport.fingerprint = document->fingerprint();
_dependencies.addCoreImport(cImport);
......@@ -512,13 +512,34 @@ void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
{
QTC_CHECK(info.fingerprint() == info.calculateFingerprint());
_libraries.insert(QDir::cleanPath(path), info);
if (!info.wasFound()) return;
CoreImport cImport;
cImport.importId = path;
cImport.language = Language::Unknown;
QSet<ImportKey> packages;
foreach (const ModuleApiInfo &moduleInfo, info.moduleApis()) {
ImportKey iKey(ImportType::Library, moduleInfo.uri, moduleInfo.version.majorVersion(),
moduleInfo.version.minorVersion());
cImport.possibleExports << Export(iKey, path, true);
packages.insert(iKey);
}
foreach (const LanguageUtils::FakeMetaObject::ConstPtr &metaO, info.metaObjects()) {
foreach (const LanguageUtils::FakeMetaObject::Export &e, metaO->exports()) {
ImportKey iKey(ImportType::Library, e.package, e.version.majorVersion(),
e.version.minorVersion());
packages.insert(iKey);
}
}
QStringList splitPath = path.split(QLatin1Char('/'));
foreach (const ImportKey &importKey, packages) {
QString requiredPath = QStringList(splitPath.mid(0, splitPath.size() - importKey.splitPath.size()))
.join(QLatin1Char('/'));
cImport.possibleExports << Export(importKey, requiredPath, true);
}
foreach (const QmlDirParser::Component &component, info.components()) {
foreach (const Export &e, cImport.possibleExports)
// renaming of type name not really represented here... fix?
_dependencies.addExport(component.fileName, e.exportName, e.pathRequired);
}
cImport.fingerprint = info.fingerprint();
_dependencies.addCoreImport(cImport);
......
......@@ -201,6 +201,9 @@ public:
bool wasScanned() const
{ return _status != NotScanned; }
bool wasFound() const
{ return _status != NotFound; }
PluginTypeInfoStatus pluginTypeInfoStatus() const
{ return _dumpStatus; }
......
......@@ -40,6 +40,8 @@
namespace QmlJS {
static bool debugImportDependencies = false;
ImportKind::Enum toImportKind(ImportType::Enum type)
{
switch (type) {
......@@ -408,6 +410,40 @@ ImportKey::DirCompareInfo ImportKey::compareDir(const ImportKey &superDir) const
return SameDir;
}
QString ImportKey::toString() const
{
QString res;
switch (type) {
case ImportType::UnknownFile:
case ImportType::File:
res = path();
break;
case ImportType::Directory:
case ImportType::ImplicitDirectory:
res = path() + QLatin1Char('/');
break;
case ImportType::QrcDirectory:
res = QLatin1String("qrc:") + path() + QLatin1Char('/');
break;
case ImportType::QrcFile:
res = QLatin1String("qrc:") + path() + QLatin1Char('/');
break;
case ImportType::Invalid:
res = path();
break;
case ImportType::Library:
res = splitPath.join(QLatin1Char('.'));
break;
}
if (majorVersion != LanguageUtils::ComponentVersion::NoVersion
|| minorVersion != LanguageUtils::ComponentVersion::NoVersion)
return res + QLatin1Char(' ') + QString::number(majorVersion)
+ QLatin1Char('.') + QString::number(minorVersion);
return res;
}
uint qHash(const ImportKey &info)
{
uint res = ::qHash(info.type) ^
......@@ -688,6 +724,12 @@ void ImportDependencies::addCoreImport(const CoreImport &import)
newImport.possibleExports.append(e);
}
m_coreImports.insert(newImport.importId, newImport);
if (debugImportDependencies) {
QDebug dbg(qDebug());
dbg << "added import "<< newImport.importId << " for";
foreach (const Export &e, newImport.possibleExports)
dbg << " " << e.exportName.toString() << "(" << e.pathRequired << ")";
}
}
void ImportDependencies::removeCoreImport(const QString &importId)
......@@ -705,6 +747,8 @@ void ImportDependencies::removeCoreImport(const QString &importId)
cImport.possibleExports = newExports;
else
m_coreImports.remove(importId);
if (debugImportDependencies)
qDebug() << "removed import with id:"<< importId;
}
void ImportDependencies::addExport(const QString &importId, const ImportKey &importKey,
......@@ -719,6 +763,9 @@ void ImportDependencies::addExport(const QString &importId, const ImportKey &imp
}
CoreImport &importValue = m_coreImports[importId];
importValue.possibleExports.append(Export(importKey, requiredPath, false));
if (debugImportDependencies)
qDebug() << "added export "<< importKey.toString() << " for id " <<importId
<< " (" << requiredPath << ")";
}
void ImportDependencies::removeExport(const QString &importId, const ImportKey &importKey,
......@@ -726,28 +773,31 @@ void ImportDependencies::removeExport(const QString &importId, const ImportKey &
{
if (!m_coreImports.contains(importId)) {
qDebug() << "non existing core import for removeExport(" << importId << ", "
<< importKey.path() << ")";
<< importKey.toString() << ")";
} else {
CoreImport &importValue = m_coreImports[importId];
if (!importValue.possibleExports.removeOne(Export(importKey, requiredPath, false))) {
qDebug() << "non existing export for removeExport(" << importId << ", "
<< importKey.path() << ")";
<< importKey.toString() << ")";
}
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 "
qDebug() << "missing possibleExport for " << importKey.toString() << " when removing export of "
<< importId;
} else {
QStringList &cImp = m_importCache[importKey];
if (!cImp.removeOne(importId)) {
qDebug() << "missing possibleExport backpointer for " << importKey.path() << " to "
qDebug() << "missing possibleExport backpointer for " << importKey.toString() << " to "
<< importId;
}
if (cImp.isEmpty())
m_importCache.remove(importKey);
}
if (debugImportDependencies)
qDebug() << "removed export "<< importKey.toString() << " for id " << importId
<< " (" << requiredPath << ")";
}
void ImportDependencies::iterateOnCoreImports(
......@@ -774,6 +824,8 @@ void ImportDependencies::iterateOnLibraryImports(
iter_t i = m_importCache.lowerBound(firstLib);
iter_t end = m_importCache.constEnd();
while (i != end && i.key().type == ImportType::Library) {
if (debugImportDependencies)
qDebug() << "libloop:" << i.key().toString() << i.value();
foreach (const QString &cImportName, i.value()) {
CoreImport cImport = coreImport(cImportName);
if (vContext.languageIsCompatible(cImport.language)) {
......@@ -781,6 +833,9 @@ void ImportDependencies::iterateOnLibraryImports(
if (e.visibleInVContext(vContext) && e.exportName.type == ImportType::Library) {
ImportMatchStrength m = e.exportName.matchImport(i.key(), vContext);
if (m.hasMatch()) {
if (debugImportDependencies)
qDebug() << "import iterate:" << e.exportName.toString()
<< " (" << e.pathRequired << "), id:" << cImport.importId;
if (!iterF(m, e, cImport))
return;
}
......
......@@ -118,6 +118,7 @@ public:
int compare(const ImportKey &other) const;
bool isDirectoryLike() const;
DirCompareInfo compareDir(const ImportKey &other) const;
QString toString() const;
};
uint qHash(const ImportKey &info);
......
......@@ -64,6 +64,7 @@ public:
attachPID(-1),
useTerminal(false),
breakOnMain(false),
continueAfterAttach(false),
multiProcess(false),
languages(AnyLanguage),
qmlServerAddress(QLatin1String("127.0.0.1")),
......@@ -102,6 +103,7 @@ public:
qint64 attachPID;
bool useTerminal;
bool breakOnMain;
bool continueAfterAttach;
bool multiProcess;
DebuggerLanguages languages;
......
......@@ -49,6 +49,7 @@
#include <qtsupport/qmldumptool.h>
#include <qtsupport/qtsupportconstants.h>
#include <utils/hostosinfo.h>
#include <utils/function.h>
#include <extensionsystem/pluginmanager.h>
#include <QDir>
......@@ -360,7 +361,7 @@ QFuture<void> ModelManager::refreshSourceFiles(const QStringList &sourceFiles,
QFuture<void> result = QtConcurrent::run(&ModelManager::parse,
workingCopy(), sourceFiles,
this,
this, Language::Qml,
emitDocumentOnDiskChanged);
if (m_synchronizer.futures().size() > 10) {
......@@ -386,7 +387,7 @@ void ModelManager::fileChangedOnDisk(const QString &path)
{
QtConcurrent::run(&ModelManager::parse,
workingCopy(), QStringList() << path,
this, true);
this, Language::Unknown, true);
}
void ModelManager::removeFiles(const QStringList &files)
......@@ -700,7 +701,8 @@ static bool findNewQmlLibraryInPath(const QString &path,
ModelManager *modelManager,
QStringList *importedFiles,
QSet<QString> *scannedPaths,
QSet<QString> *newLibraries)
QSet<QString> *newLibraries,
bool ignoreMissing)
{
// if we know there is a library, done
const LibraryInfo &existingInfo = snapshot.libraryInfo(path);
......@@ -715,8 +717,10 @@ static bool findNewQmlLibraryInPath(const QString &path,
const QDir dir(path);
QFile qmldirFile(dir.filePath(QLatin1String("qmldir")));
if (!qmldirFile.exists()) {
LibraryInfo libraryInfo(LibraryInfo::NotFound);
modelManager->updateLibraryInfo(path, libraryInfo);
if (!ignoreMissing) {
LibraryInfo libraryInfo(LibraryInfo::NotFound);
modelManager->updateLibraryInfo(path, libraryInfo);
}
return false;
}
......@@ -765,18 +769,18 @@ static void findNewQmlLibrary(
QString::number(version.minorVersion()));
findNewQmlLibraryInPath(
libraryPath, snapshot, modelManager,
importedFiles, scannedPaths, newLibraries);
importedFiles, scannedPaths, newLibraries, false);
libraryPath = QString::fromLatin1("%1.%2").arg(
path,
QString::number(version.majorVersion()));
findNewQmlLibraryInPath(
libraryPath, snapshot, modelManager,
importedFiles, scannedPaths, newLibraries);
importedFiles, scannedPaths, newLibraries, false);
findNewQmlLibraryInPath(
path, snapshot, modelManager,
importedFiles, scannedPaths, newLibraries);
importedFiles, scannedPaths, newLibraries, false);
}
static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snapshot,
......@@ -785,7 +789,7 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap
{
// scan current dir
findNewQmlLibraryInPath(doc->path(), snapshot, modelManager,
importedFiles, scannedPaths, newLibraries);
importedFiles, scannedPaths, newLibraries, false);
// scan dir and lib imports
const QStringList importPaths = modelManager->importPaths();
......@@ -793,7 +797,7 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap
if (import.type() == ImportType::Directory) {
const QString targetPath = import.path();
findNewQmlLibraryInPath(targetPath, snapshot, modelManager,
importedFiles, scannedPaths, newLibraries);
importedFiles, scannedPaths, newLibraries, false);
}
if (import.type() == ImportType::Library) {
......@@ -808,24 +812,18 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap
}
}
void ModelManager::parse(QFutureInterface<void> &future,
WorkingCopy workingCopy,
QStringList files,
ModelManager *modelManager,
bool emitDocChangedOnDisk)
void ModelManager::parseLoop(QSet<QString> &scannedPaths,
QSet<QString> &newLibraries,
WorkingCopy workingCopy,
QStringList files,
ModelManager *modelManager,
Language::Enum mainLanguage,
bool emitDocChangedOnDisk,
Utils::function<bool(qreal)> reportProgress)
{
int progressRange = files.size();
future.setProgressRange(0, progressRange);
// paths we have scanned for files and added to the files list
QSet<QString> scannedPaths;
// libraries we've found while scanning imports
QSet<QString> newLibraries;
for (int i = 0; i < files.size(); ++i) {
if (future.isCanceled())
break;
future.setProgressValue(qreal(i) / files.size() * progressRange);
if (!reportProgress(qreal(i) / files.size()))
return;
const QString fileName = files.at(i);
......@@ -835,7 +833,9 @@ void ModelManager::parse(QFutureInterface<void> &future,
modelManager->updateQrcFile(fileName);
continue;
}
if (language == Language::Qml
&& (mainLanguage == Language::QmlQtQuick1 || Language::QmlQtQuick2))
language = mainLanguage;
QString contents;
int documentRevision = 0;
......@@ -878,7 +878,116 @@ void ModelManager::parse(QFutureInterface<void> &future,
if (emitDocChangedOnDisk)
modelManager->emitDocumentChangedOnDisk(doc);
}
}
class FutureReporter
{
public:
FutureReporter(QFutureInterface<void> &future, int multiplier = 100, int base = 0)
:future(future), multiplier(multiplier), base(base)
{ }
bool operator()(qreal val)
{
if (future.isCanceled())
return false;
future.setProgressValue(int(base + multiplier * val));
return true;
}
private:
QFutureInterface<void> &future;
int multiplier;
int base;
};
void ModelManager::parse(QFutureInterface<void> &future,
WorkingCopy workingCopy,
QStringList files,
ModelManager *modelManager,
Language::Enum mainLanguage,
bool emitDocChangedOnDisk)
{
FutureReporter reporter(future);
future.setProgressRange(0, 100);
// paths we have scanned for files and added to the files list
QSet<QString> scannedPaths;
// libraries we've found while scanning imports
QSet<QString> newLibraries;
parseLoop(scannedPaths, newLibraries, workingCopy, files, modelManager, mainLanguage,
emitDocChangedOnDisk, reporter);
future.setProgressValue(100);
}
struct ScanItem {
QString path;
int depth;
ScanItem(QString path = QString(), int depth = 0)
: path(path), depth(depth)
{ }
};
void ModelManager::importScan(QFutureInterface<void> &future,
ModelManagerInterface::WorkingCopy workingCopy,
QStringList paths, ModelManager *modelManager,
Language::Enum language,
bool emitDocChangedOnDisk)
{
// paths we have scanned for files and added to the files list
QSet<QString> scannedPaths = modelManager->m_scannedPaths;
// libraries we've found while scanning imports
QSet<QString> newLibraries;
QVector<ScanItem> pathsToScan;
pathsToScan.reserve(paths.size());
foreach (const QString &path, paths) {
QString cPath = QDir::cleanPath(path);
if (modelManager->m_scannedPaths.contains(cPath))
continue;
pathsToScan.append(ScanItem(cPath));
}
const int maxScanDepth = 5;
int progressRange = pathsToScan.size() * (1 << (2 + maxScanDepth));
int totalWork(progressRange), workDone(0);
future.setProgressRange(0, progressRange); // update max length while iterating?
const bool libOnly = true; // FIXME remove when tested more
while (!pathsToScan.isEmpty() && !future.isCanceled()) {
ScanItem toScan = pathsToScan.last();
pathsToScan.removeLast();
int pathBudget = (maxScanDepth + 2 - toScan.depth);
if (!scannedPaths.contains(toScan.path)) {
QStringList importedFiles;
const Snapshot snapshot = modelManager->snapshot();
if (!findNewQmlLibraryInPath(toScan.path, snapshot, modelManager, &importedFiles,
&scannedPaths, &newLibraries, true)
&& !libOnly && snapshot.documentsInDirectory(toScan.path).isEmpty())
importedFiles += qmlFilesInDirectory(toScan.path);
workDone += 1;
future.setProgressValue(progressRange * workDone / totalWork);
if (!importedFiles.isEmpty()) {
FutureReporter reporter(future, progressRange * pathBudget / (4 * totalWork),
progressRange * workDone / totalWork);
parseLoop(scannedPaths, newLibraries, workingCopy, importedFiles, modelManager,
language, emitDocChangedOnDisk, reporter); // run in parallel??
importedFiles.clear();
}
workDone += pathBudget / 4 - 1;
future.setProgressValue(progressRange * workDone / totalWork);
} else {
workDone += pathBudget / 4;
}
// always descend tree, as we might have just scanned with a smaller depth
if (toScan.depth < maxScanDepth) {
QDir dir(toScan.path);
QStringList subDirs(dir.entryList(QDir::Dirs));
workDone += 1;
totalWork += pathBudget / 2 * subDirs.size() - pathBudget * 3 / 4 + 1;
foreach (const QString path, subDirs)
pathsToScan.append(ScanItem(dir.absoluteFilePath(path), toScan.depth + 1));
} else {
workDone += pathBudget *3 / 4;
}
future.setProgressValue(progressRange * workDone / totalWork);
}
future.setProgressValue(progressRange);
}
......@@ -993,6 +1102,33 @@ void ModelManager::updateImportPaths()
findNewLibraryImports(doc, snapshot, this, &importedFiles, &scannedPaths, &newLibraries);
updateSourceFiles(importedFiles, true);
QStringList pathToScan;
foreach (QString importPath, allImportPaths)
if (!m_scannedPaths.contains(importPath))
pathToScan.append(importPath);
if (pathToScan.count() > 1) {
QFuture<void> result = QtConcurrent::run(&ModelManager::importScan,
workingCopy(), pathToScan,
this, Language::Qml,
true);
if (m_synchronizer.futures().size() > 10) {
QList<QFuture<void> > futures = m_synchronizer.futures();
m_synchronizer.clearFutures();
foreach (const QFuture<void> &future, futures) {
if (! (future.isFinished() || future.isCanceled()))
m_synchronizer.addFuture(future);
}
}
m_synchronizer.addFuture(result);
ProgressManager::addTask(result, tr("Qml import scan"), Constants::TASK_IMPORT_SCAN);
}
}
void ModelManager::loadPluginTypes(const QString &libraryPath, const QString &importPath,
......@@ -1133,6 +1269,7 @@ ViewerContext ModelManager::completeVContext(const ViewerContext &vCtx,
case ViewerContext::AddAllPaths:
res.paths << importPaths();
}
res.flags = ViewerContext::Complete;
return res;
}
......
......@@ -34,6 +34,7 @@
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljsqrcparser.h>
#include <qmljs/qmljsconstants.h>
#include <cplusplus/CppDocument.h>
#include <utils/qtcoverride.h>
......@@ -135,11 +136,22 @@ protected:
QFuture<void> refreshSourceFiles(const QStringList &sourceFiles,
bool emitDocumentOnDiskChanged);
static void parseLoop(QSet<QString> &scannedPaths, QSet<QString> &newLibraries,
WorkingCopy workingCopy, QStringList files, ModelManager *modelManager,
QmlJS::Language::Enum mainLanguage, bool emitDocChangedOnDisk,
Utils::function<bool (qreal)> reportProgress);
static void parse(QFutureInterface<void> &future,
WorkingCopy workingCopy,
QStringList files,
ModelManager *modelManager,
QmlJS::Language::Enum mainLanguage,
bool emitDocChangedOnDisk);
static void importScan(QFutureInterface<void> &future,
WorkingCopy workingCopy,
QStringList paths,
ModelManager *modelManager,
QmlJS::Language::Enum mainLanguage,
bool emitDocChangedOnDisk);
void loadQmlTypeDescriptions();
void loadQmlTypeDescriptions(const QString &path);
......@@ -167,6 +179,7 @@ private:
QmlJS::QmlLanguageBundles m_activeBundles;
QmlJS::QmlLanguageBundles m_extendedBundles;
QmlJS::ViewerContext m_vContext;
QSet<QString> m_scannedPaths;
QTimer *m_updateCppQmlTypesTimer;
QTimer *m_asyncResetTimer;
......
......@@ -43,6 +43,7 @@ const char JS_MIMETYPE[] = "application/javascript";
const char JSON_MIMETYPE[] = "application/json";
const char TASK_INDEX[] = "QmlJSEditor.TaskIndex";
const char TASK_IMPORT_SCAN[] = "QmlJSEditor.TaskImportScan";
const char QML_JS_CODE_STYLE_SETTINGS_ID[] = "A.Code Style";
const char QML_JS_CODE_STYLE_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("QmlJSTools", "Code Style");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment