diff --git a/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.cpp b/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.cpp index 95fc125d7c..9c982a563e 100644 --- a/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -1,1530 +1,1538 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** 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 The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "qmljsbind.h" #include "qmljsconstants.h" -#include "qmljsfindexportedcpptypes.h" +// #include "qmljsfindexportedcpptypes.h" #include "qmljsinterpreter.h" #include "qmljsmodelmanagerinterface.h" #include "qmljsplugindumper.h" #include "qmljstypedescriptionreader.h" #include "qmljsdialect.h" #include "qmljsviewercontext.h" #include "qmljsutils.h" -#include +// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace QmlJS { QMLJS_EXPORT Q_LOGGING_CATEGORY(qmljsLog, "qtc.qmljs.common") /*! \class QmlJS::ModelManagerInterface \brief The ModelManagerInterface class acts as an interface to the global state of the QmlJS code model. \sa QmlJS::Document QmlJS::Snapshot QmlJSTools::Internal::ModelManager The ModelManagerInterface is an interface for global state and actions in the QmlJS code model. It is implemented by \l{QmlJSTools::Internal::ModelManager} and the instance can be accessed through ModelManagerInterface::instance(). One of its primary concerns is to keep the Snapshots it maintains up to date by parsing documents and finding QML modules. It has a Snapshot that contains only valid Documents, accessible through ModelManagerInterface::snapshot() and a Snapshot with potentially more recent, but invalid documents that is exposed through ModelManagerInterface::newestSnapshot(). */ static ModelManagerInterface *g_instance = 0; const char qtQuickUISuffix[] = "ui.qml"; static QStringList environmentImportPaths() { QStringList paths; QByteArray envImportPath = qgetenv("QML_IMPORT_PATH"); foreach (const QString &path, QString::fromLatin1(envImportPath) .split(Utils::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts)) { QString canonicalPath = QDir(path).canonicalPath(); if (!canonicalPath.isEmpty() && !paths.contains(canonicalPath)) paths.append(canonicalPath); } return paths; } ModelManagerInterface::ModelManagerInterface(QObject *parent) : QObject(parent), m_shouldScanImports(false), m_defaultProject(0), m_pluginDumper(new PluginDumper(this)) { m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1"; +#if 0 m_updateCppQmlTypesTimer = new QTimer(this); m_updateCppQmlTypesTimer->setInterval(1000); m_updateCppQmlTypesTimer->setSingleShot(true); connect(m_updateCppQmlTypesTimer, &QTimer::timeout, this, &ModelManagerInterface::startCppQmlTypeUpdate); +#endif m_asyncResetTimer = new QTimer(this); m_asyncResetTimer->setInterval(15000); m_asyncResetTimer->setSingleShot(true); connect(m_asyncResetTimer, &QTimer::timeout, this, &ModelManagerInterface::resetCodeModel); qRegisterMetaType("QmlJS::Document::Ptr"); qRegisterMetaType("QmlJS::LibraryInfo"); qRegisterMetaType("QmlJS::Dialect"); qRegisterMetaType("QmlJS::PathAndLanguage"); qRegisterMetaType("QmlJS::PathsAndLanguages"); m_defaultProjectInfo.qtImportsPath = QFileInfo( QLibraryInfo::location(QLibraryInfo::ImportsPath)).canonicalFilePath(); m_defaultProjectInfo.qtQmlPath = QFileInfo( QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)).canonicalFilePath(); m_defaultImportPaths << environmentImportPaths(); updateImportPaths(); Q_ASSERT(! g_instance); g_instance = this; } ModelManagerInterface::~ModelManagerInterface() { +#if 0 m_cppQmlTypesUpdater.cancel(); m_cppQmlTypesUpdater.waitForFinished(); +#endif Q_ASSERT(g_instance == this); g_instance = 0; } static QHash defaultLanguageMapping() { static QHash res{ {QLatin1String("js"), Dialect::JavaScript}, {QLatin1String("qml"), Dialect::Qml}, {QLatin1String("qmltypes"), Dialect::QmlTypeInfo}, {QLatin1String("qmlproject"), Dialect::QmlProject}, {QLatin1String("json"), Dialect::Json}, {QLatin1String("qbs"), Dialect::QmlQbs}, {QLatin1String(qtQuickUISuffix), Dialect::QmlQtQuick2Ui} }; return res; } Dialect ModelManagerInterface::guessLanguageOfFile(const QString &fileName) { QHash lMapping; if (instance()) lMapping = instance()->languageForSuffix(); else lMapping = defaultLanguageMapping(); const QFileInfo info(fileName); QString fileSuffix = info.suffix(); /* * I was reluctant to use complete suffix in all cases, because it is a huge * change in behaivour. But in case of .qml this should be safe. */ if (fileSuffix == QLatin1String("qml")) fileSuffix = info.completeSuffix(); return lMapping.value(fileSuffix, Dialect::NoLanguage); } QStringList ModelManagerInterface::globPatternsForLanguages(const QList languages) { QHash lMapping; if (instance()) lMapping = instance()->languageForSuffix(); else lMapping = defaultLanguageMapping(); QStringList patterns; QHashIterator i(lMapping); while (i.hasNext()) { i.next(); if (languages.contains(i.value())) patterns << QLatin1String("*.") + i.key(); } return patterns; } ModelManagerInterface *ModelManagerInterface::instance() { return g_instance; } void ModelManagerInterface::writeWarning(const QString &msg) { if (ModelManagerInterface *i = instance()) i->writeMessageInternal(msg); else qCWarning(qmljsLog) << msg; } ModelManagerInterface::WorkingCopy ModelManagerInterface::workingCopy() { if (ModelManagerInterface *i = instance()) return i->workingCopyInternal(); return WorkingCopy(); } void ModelManagerInterface::activateScan() { if (!m_shouldScanImports) { m_shouldScanImports = true; updateImportPaths(); } } QHash ModelManagerInterface::languageForSuffix() const { return defaultLanguageMapping(); } void ModelManagerInterface::writeMessageInternal(const QString &msg) const { qCDebug(qmljsLog) << msg; } ModelManagerInterface::WorkingCopy ModelManagerInterface::workingCopyInternal() const { ModelManagerInterface::WorkingCopy res; return res; } void ModelManagerInterface::addTaskInternal(QFuture result, const QString &msg, const char *taskId) const { Q_UNUSED(result); qCDebug(qmljsLog) << "started " << taskId << " " << msg; } void ModelManagerInterface::loadQmlTypeDescriptionsInternal(const QString &resourcePath) { const QDir typeFileDir(resourcePath + QLatin1String("/qml-type-descriptions")); const QStringList qmlTypesExtensions = QStringList("*.qmltypes"); QFileInfoList qmlTypesFiles = typeFileDir.entryInfoList( qmlTypesExtensions, QDir::Files, QDir::Name); QStringList errors; QStringList warnings; // filter out the actual Qt builtins for (int i = 0; i < qmlTypesFiles.size(); ++i) { if (qmlTypesFiles.at(i).baseName() == QLatin1String("builtins")) { QFileInfoList list; list.append(qmlTypesFiles.at(i)); CppQmlTypesLoader::defaultQtObjects = CppQmlTypesLoader::loadQmlTypes(list, &errors, &warnings); qmlTypesFiles.removeAt(i); break; } } // load the fallbacks for libraries CppQmlTypesLoader::defaultLibraryObjects.unite( CppQmlTypesLoader::loadQmlTypes(qmlTypesFiles, &errors, &warnings)); foreach (const QString &error, errors) writeMessageInternal(error); foreach (const QString &warning, warnings) writeMessageInternal(warning); } void ModelManagerInterface::setDefaultProject(const ModelManagerInterface::ProjectInfo &pInfo, ProjectExplorer::Project *p) { QMutexLocker l(mutex()); m_defaultProject = p; m_defaultProjectInfo = pInfo; } Snapshot ModelManagerInterface::snapshot() const { QMutexLocker locker(&m_mutex); return m_validSnapshot; } Snapshot ModelManagerInterface::newestSnapshot() const { QMutexLocker locker(&m_mutex); return m_newestSnapshot; } void ModelManagerInterface::updateSourceFiles(const QStringList &files, bool emitDocumentOnDiskChanged) { if (!m_indexerEnabled) return; refreshSourceFiles(files, emitDocumentOnDiskChanged); } void ModelManagerInterface::cleanupFutures() { if (m_futures.size() > 10) { QList > futures = m_futures; m_futures.clear(); foreach (const QFuture &future, futures) { if (!(future.isFinished() || future.isCanceled())) m_futures.append(future); } } } QFuture ModelManagerInterface::refreshSourceFiles(const QStringList &sourceFiles, bool emitDocumentOnDiskChanged) { if (sourceFiles.isEmpty()) return QFuture(); QFuture result = Utils::runAsync(&ModelManagerInterface::parse, workingCopyInternal(), sourceFiles, this, Dialect(Dialect::Qml), emitDocumentOnDiskChanged); cleanupFutures(); m_futures.append(result); if (sourceFiles.count() > 1) addTaskInternal(result, tr("Parsing QML Files"), Constants::TASK_INDEX); if (sourceFiles.count() > 1 && !m_shouldScanImports) { bool scan = false; { QMutexLocker l(&m_mutex); if (!m_shouldScanImports) { m_shouldScanImports = true; scan = true; } } if (scan) updateImportPaths(); } return result; } void ModelManagerInterface::fileChangedOnDisk(const QString &path) { Utils::runAsync(&ModelManagerInterface::parse, workingCopyInternal(), QStringList(path), this, Dialect(Dialect::AnyLanguage), true); } void ModelManagerInterface::removeFiles(const QStringList &files) { emit aboutToRemoveFiles(files); QMutexLocker locker(&m_mutex); foreach (const QString &file, files) { m_validSnapshot.remove(file); m_newestSnapshot.remove(file); } } namespace { bool pInfoLessThanActive(const ModelManagerInterface::ProjectInfo &p1, const ModelManagerInterface::ProjectInfo &p2) { QStringList s1 = p1.activeResourceFiles; QStringList s2 = p2.activeResourceFiles; if (s1.size() < s2.size()) return true; if (s1.size() > s2.size()) return false; for (int i = 0; i < s1.size(); ++i) { if (s1.at(i) < s2.at(i)) return true; else if (s1.at(i) > s2.at(i)) return false; } return false; } bool pInfoLessThanAll(const ModelManagerInterface::ProjectInfo &p1, const ModelManagerInterface::ProjectInfo &p2) { QStringList s1 = p1.allResourceFiles; QStringList s2 = p2.allResourceFiles; if (s1.size() < s2.size()) return true; if (s1.size() > s2.size()) return false; for (int i = 0; i < s1.size(); ++i) { if (s1.at(i) < s2.at(i)) return true; else if (s1.at(i) > s2.at(i)) return false; } return false; } bool pInfoLessThanImports(const ModelManagerInterface::ProjectInfo &p1, const ModelManagerInterface::ProjectInfo &p2) { if (p1.qtQmlPath < p2.qtQmlPath) return true; if (p1.qtQmlPath > p2.qtQmlPath) return false; if (p1.qtImportsPath < p2.qtImportsPath) return true; if (p1.qtImportsPath > p2.qtImportsPath) return false; const PathsAndLanguages &s1 = p1.importPaths; const PathsAndLanguages &s2 = p2.importPaths; if (s1.size() < s2.size()) return true; if (s1.size() > s2.size()) return false; for (int i = 0; i < s1.size(); ++i) { if (s1.at(i) < s2.at(i)) return true; else if (s2.at(i) < s1.at(i)) return false; } return false; } } void ModelManagerInterface::iterateQrcFiles(ProjectExplorer::Project *project, QrcResourceSelector resources, std::function callback) { QList pInfos; if (project) { pInfos.append(projectInfo(project)); } else { pInfos = projectInfos(); if (resources == ActiveQrcResources) // make the result predictable Utils::sort(pInfos, &pInfoLessThanActive); else Utils::sort(pInfos, &pInfoLessThanAll); } QSet pathsChecked; foreach (const ModelManagerInterface::ProjectInfo &pInfo, pInfos) { QStringList qrcFilePaths; if (resources == ActiveQrcResources) qrcFilePaths = pInfo.activeResourceFiles; else qrcFilePaths = pInfo.allResourceFiles; foreach (const QString &qrcFilePath, qrcFilePaths) { if (pathsChecked.contains(qrcFilePath)) continue; pathsChecked.insert(qrcFilePath); QrcParser::ConstPtr qrcFile = m_qrcCache.parsedPath(qrcFilePath); if (qrcFile.isNull()) continue; callback(qrcFile); } } } QStringList ModelManagerInterface::qrcPathsForFile(const QString &file, const QLocale *locale, ProjectExplorer::Project *project, QrcResourceSelector resources) { QStringList res; iterateQrcFiles(project, resources, [&](QrcParser::ConstPtr qrcFile) { qrcFile->collectResourceFilesForSourceFile(file, &res, locale); }); return res; } QStringList ModelManagerInterface::filesAtQrcPath(const QString &path, const QLocale *locale, ProjectExplorer::Project *project, QrcResourceSelector resources) { QString normPath = QrcParser::normalizedQrcFilePath(path); QStringList res; iterateQrcFiles(project, resources, [&](QrcParser::ConstPtr qrcFile) { qrcFile->collectFilesAtPath(normPath, &res, locale); }); return res; } QMap ModelManagerInterface::filesInQrcPath(const QString &path, const QLocale *locale, ProjectExplorer::Project *project, bool addDirs, QrcResourceSelector resources) { QString normPath = QrcParser::normalizedQrcDirectoryPath(path); QMap res; iterateQrcFiles(project, resources, [&](QrcParser::ConstPtr qrcFile) { qrcFile->collectFilesInPath(normPath, &res, addDirs, locale); }); return res; } QList ModelManagerInterface::projectInfos() const { QMutexLocker locker(&m_mutex); return m_projects.values(); } ModelManagerInterface::ProjectInfo ModelManagerInterface::projectInfo( ProjectExplorer::Project *project, const ModelManagerInterface::ProjectInfo &defaultValue) const { QMutexLocker locker(&m_mutex); return m_projects.value(project, defaultValue); } void ModelManagerInterface::updateProjectInfo(const ProjectInfo &pinfo, ProjectExplorer::Project *p) { if (! pinfo.isValid() || !p || !m_indexerEnabled) return; Snapshot snapshot; ProjectInfo oldInfo; { QMutexLocker locker(&m_mutex); oldInfo = m_projects.value(p); m_projects.insert(p, pinfo); if (p == m_defaultProject) m_defaultProjectInfo = pinfo; snapshot = m_validSnapshot; } if (oldInfo.qmlDumpPath != pinfo.qmlDumpPath || oldInfo.qmlDumpEnvironment != pinfo.qmlDumpEnvironment) { m_pluginDumper->scheduleRedumpPlugins(); m_pluginDumper->scheduleMaybeRedumpBuiltins(pinfo); } updateImportPaths(); // remove files that are no longer in the project and have been deleted QStringList deletedFiles; foreach (const QString &oldFile, oldInfo.sourceFiles) { if (snapshot.document(oldFile) && !pinfo.sourceFiles.contains(oldFile) && !QFile::exists(oldFile)) { deletedFiles += oldFile; } } removeFiles(deletedFiles); foreach (const QString &oldFile, deletedFiles) m_fileToProject.remove(oldFile, p); // parse any files not yet in the snapshot QStringList newFiles; foreach (const QString &file, pinfo.sourceFiles) { if (!snapshot.document(file)) newFiles += file; } foreach (const QString &newFile, newFiles) m_fileToProject.insert(newFile, p); updateSourceFiles(newFiles, false); // update qrc cache m_qrcContents = pinfo.resourceFileContents; foreach (const QString &newQrc, pinfo.allResourceFiles) m_qrcCache.addPath(newQrc, m_qrcContents.value(newQrc)); foreach (const QString &oldQrc, oldInfo.allResourceFiles) m_qrcCache.removePath(oldQrc); int majorVersion, minorVersion, patchVersion; // dump builtin types if the shipped definitions are probably outdated and the // Qt version ships qmlplugindump if (::sscanf(pinfo.qtVersionString.toLatin1().constData(), "%d.%d.%d", &majorVersion, &minorVersion, &patchVersion) != 3) majorVersion = minorVersion = patchVersion = -1; if (majorVersion > 4 || (majorVersion == 4 && (minorVersion > 8 || (minorVersion == 8 && patchVersion >= 5)))) { m_pluginDumper->loadBuiltinTypes(pinfo); } emit projectInfoUpdated(pinfo); } void ModelManagerInterface::removeProjectInfo(ProjectExplorer::Project *project) { ProjectInfo info; info.sourceFiles.clear(); // update with an empty project info to clear data updateProjectInfo(info, project); { QMutexLocker locker(&m_mutex); m_projects.remove(project); } } /*! Returns project info with summarized info for \a path \note Project pointer will be empty */ ModelManagerInterface::ProjectInfo ModelManagerInterface::projectInfoForPath(const QString &path) const { QList infos = allProjectInfosForPath(path); ProjectInfo res; foreach (const ProjectInfo &pInfo, infos) { if (res.qtImportsPath.isEmpty()) res.qtImportsPath = pInfo.qtImportsPath; if (res.qtQmlPath.isEmpty()) res.qtQmlPath = pInfo.qtQmlPath; for (int i = 0; i < pInfo.importPaths.size(); ++i) res.importPaths.maybeInsert(pInfo.importPaths.at(i)); } return res; } /*! Returns list of project infos for \a path */ QList ModelManagerInterface::allProjectInfosForPath(const QString &path) const { QList projects; { QMutexLocker locker(&m_mutex); projects = m_fileToProject.values(path); if (projects.isEmpty()) { QFileInfo fInfo(path); projects = m_fileToProject.values(fInfo.canonicalFilePath()); } } QList infos; foreach (ProjectExplorer::Project *project, projects) { ProjectInfo info = projectInfo(project); if (info.isValid()) infos.append(info); } std::sort(infos.begin(), infos.end(), &pInfoLessThanImports); infos.append(m_defaultProjectInfo); return infos; } bool ModelManagerInterface::isIdle() const { return m_futures.isEmpty(); } void ModelManagerInterface::emitDocumentChangedOnDisk(Document::Ptr doc) { emit documentChangedOnDisk(doc); } void ModelManagerInterface::updateQrcFile(const QString &path) { m_qrcCache.updatePath(path, m_qrcContents.value(path)); } void ModelManagerInterface::updateDocument(Document::Ptr doc) { { QMutexLocker locker(&m_mutex); m_validSnapshot.insert(doc); m_newestSnapshot.insert(doc, true); } emit documentUpdated(doc); } void ModelManagerInterface::updateLibraryInfo(const QString &path, const LibraryInfo &info) { if (!info.pluginTypeInfoError().isEmpty()) qCDebug(qmljsLog) << "Dumping errors for " << path << ":" << info.pluginTypeInfoError(); { QMutexLocker locker(&m_mutex); m_validSnapshot.insertLibraryInfo(path, info); m_newestSnapshot.insertLibraryInfo(path, info); } // only emit if we got new useful information if (info.isValid()) emit libraryInfoUpdated(path, info); } static QStringList filesInDirectoryForLanguages(const QString &path, QList languages) { const QStringList pattern = ModelManagerInterface::globPatternsForLanguages(languages); QStringList files; const QDir dir(path); foreach (const QFileInfo &fi, dir.entryInfoList(pattern, QDir::Files)) files += fi.absoluteFilePath(); return files; } static void findNewImplicitImports(const Document::Ptr &doc, const Snapshot &snapshot, QStringList *importedFiles, QSet *scannedPaths) { // scan files that could be implicitly imported // it's important we also do this for JS files, otherwise the isEmpty check will fail if (snapshot.documentsInDirectory(doc->path()).isEmpty()) { if (! scannedPaths->contains(doc->path())) { *importedFiles += filesInDirectoryForLanguages(doc->path(), doc->language().companionLanguages()); scannedPaths->insert(doc->path()); } } } static void findNewFileImports(const Document::Ptr &doc, const Snapshot &snapshot, QStringList *importedFiles, QSet *scannedPaths) { // scan files and directories that are explicitly imported foreach (const ImportInfo &import, doc->bind()->imports()) { const QString &importName = import.path(); if (import.type() == ImportType::File) { if (! snapshot.document(importName)) *importedFiles += importName; } else if (import.type() == ImportType::Directory) { if (snapshot.documentsInDirectory(importName).isEmpty()) { if (! scannedPaths->contains(importName)) { *importedFiles += filesInDirectoryForLanguages(importName, doc->language().companionLanguages()); scannedPaths->insert(importName); } } } else if (import.type() == ImportType::QrcFile) { QStringList importPaths = ModelManagerInterface::instance()->filesAtQrcPath(importName); foreach (const QString &importPath, importPaths) { if (! snapshot.document(importPath)) *importedFiles += importPath; } } else if (import.type() == ImportType::QrcDirectory) { QMapIterator dirContents(ModelManagerInterface::instance()->filesInQrcPath(importName)); while (dirContents.hasNext()) { dirContents.next(); if (ModelManagerInterface::guessLanguageOfFile(dirContents.key()).isQmlLikeOrJsLanguage()) { foreach (const QString &filePath, dirContents.value()) { if (! snapshot.document(filePath)) *importedFiles += filePath; } } } } } } static bool findNewQmlLibraryInPath(const QString &path, const Snapshot &snapshot, ModelManagerInterface *modelManager, QStringList *importedFiles, QSet *scannedPaths, QSet *newLibraries, bool ignoreMissing) { // if we know there is a library, done const LibraryInfo &existingInfo = snapshot.libraryInfo(path); if (existingInfo.isValid()) return true; if (newLibraries->contains(path)) return true; // if we looked at the path before, done if (existingInfo.wasScanned()) return false; const QDir dir(path); QFile qmldirFile(dir.filePath(QLatin1String("qmldir"))); if (!qmldirFile.exists()) { if (!ignoreMissing) { LibraryInfo libraryInfo(LibraryInfo::NotFound); modelManager->updateLibraryInfo(path, libraryInfo); } return false; } if (Utils::HostOsInfo::isWindowsHost()) { // QTCREATORBUG-3402 - be case sensitive even here? } // found a new library! if (!qmldirFile.open(QFile::ReadOnly)) return false; QString qmldirData = QString::fromUtf8(qmldirFile.readAll()); QmlDirParser qmldirParser; qmldirParser.parse(qmldirData); const QString libraryPath = QFileInfo(qmldirFile).absolutePath(); newLibraries->insert(libraryPath); modelManager->updateLibraryInfo(libraryPath, LibraryInfo(qmldirParser)); modelManager->loadPluginTypes(QFileInfo(libraryPath).canonicalFilePath(), libraryPath, QString(), QString()); // scan the qml files in the library foreach (const QmlDirParser::Component &component, qmldirParser.components()) { if (! component.fileName.isEmpty()) { const QFileInfo componentFileInfo(dir.filePath(component.fileName)); const QString path = QDir::cleanPath(componentFileInfo.absolutePath()); if (! scannedPaths->contains(path)) { *importedFiles += filesInDirectoryForLanguages(path, Dialect(Dialect::AnyLanguage).companionLanguages()); scannedPaths->insert(path); } } } return true; } static QString modulePath(const ImportInfo &import, const QStringList &paths) { if (!import.version().isValid()) return QString(); return modulePath(import.name(), import.version().toString(), paths); } static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snapshot, ModelManagerInterface *modelManager, QStringList *importedFiles, QSet *scannedPaths, QSet *newLibraries) { // scan current dir findNewQmlLibraryInPath(doc->path(), snapshot, modelManager, importedFiles, scannedPaths, newLibraries, false); // scan dir and lib imports const QStringList importPaths = modelManager->importPathsNames(); foreach (const ImportInfo &import, doc->bind()->imports()) { if (import.type() == ImportType::Directory) { const QString targetPath = import.path(); findNewQmlLibraryInPath(targetPath, snapshot, modelManager, importedFiles, scannedPaths, newLibraries, false); } if (import.type() == ImportType::Library) { const QString libraryPath = modulePath(import, importPaths); if (libraryPath.isEmpty()) continue; findNewQmlLibraryInPath(libraryPath, snapshot, modelManager, importedFiles, scannedPaths, newLibraries, false); } } } void ModelManagerInterface::parseLoop(QSet &scannedPaths, QSet &newLibraries, WorkingCopy workingCopy, QStringList files, ModelManagerInterface *modelManager, Dialect mainLanguage, bool emitDocChangedOnDisk, std::function reportProgress) { for (int i = 0; i < files.size(); ++i) { if (!reportProgress(qreal(i) / files.size())) return; const QString fileName = files.at(i); Dialect language = guessLanguageOfFile(fileName); if (language == Dialect::NoLanguage) { if (fileName.endsWith(QLatin1String(".qrc"))) modelManager->updateQrcFile(fileName); continue; } if (language == Dialect::Qml && (mainLanguage == Dialect::QmlQtQuick2)) language = mainLanguage; if (language == Dialect::Qml && mainLanguage == Dialect::QmlQtQuick2Ui) language = Dialect::QmlQtQuick2; if (language == Dialect::QmlTypeInfo || language == Dialect::QmlProject) continue; QString contents; int documentRevision = 0; if (workingCopy.contains(fileName)) { QPair entry = workingCopy.get(fileName); contents = entry.first; documentRevision = entry.second; } else { QFile inFile(fileName); if (inFile.open(QIODevice::ReadOnly)) { QTextStream ins(&inFile); contents = ins.readAll(); inFile.close(); } else { continue; } } Document::MutablePtr doc = Document::create(fileName, language); doc->setEditorRevision(documentRevision); doc->setSource(contents); doc->parse(); // update snapshot. requires synchronization, but significantly reduces amount of file // system queries for library imports because queries are cached in libraryInfo const Snapshot snapshot = modelManager->snapshot(); // get list of referenced files not yet in snapshot or in directories already scanned QStringList importedFiles; findNewImplicitImports(doc, snapshot, &importedFiles, &scannedPaths); findNewFileImports(doc, snapshot, &importedFiles, &scannedPaths); findNewLibraryImports(doc, snapshot, modelManager, &importedFiles, &scannedPaths, &newLibraries); // add new files to parse list foreach (const QString &file, importedFiles) { if (! files.contains(file)) files.append(file); } modelManager->updateDocument(doc); if (emitDocChangedOnDisk) modelManager->emitDocumentChangedOnDisk(doc); } } class FutureReporter { public: FutureReporter(QFutureInterface &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 &future; int multiplier; int base; }; void ModelManagerInterface::parse(QFutureInterface &future, WorkingCopy workingCopy, QStringList files, ModelManagerInterface *modelManager, Dialect mainLanguage, bool emitDocChangedOnDisk) { FutureReporter reporter(future); future.setProgressRange(0, 100); // paths we have scanned for files and added to the files list QSet scannedPaths; // libraries we've found while scanning imports QSet newLibraries; parseLoop(scannedPaths, newLibraries, workingCopy, files, modelManager, mainLanguage, emitDocChangedOnDisk, reporter); future.setProgressValue(100); } struct ScanItem { QString path; int depth; Dialect language; ScanItem(QString path = QString(), int depth = 0, Dialect language = Dialect::AnyLanguage) : path(path), depth(depth), language(language) { } }; void ModelManagerInterface::importScan(QFutureInterface &future, ModelManagerInterface::WorkingCopy workingCopy, PathsAndLanguages paths, ModelManagerInterface *modelManager, bool emitDocChangedOnDisk, bool libOnly, bool forceRescan) { // paths we have scanned for files and added to the files list QSet scannedPaths; { QMutexLocker l(&modelManager->m_mutex); scannedPaths = modelManager->m_scannedPaths; } // libraries we've found while scanning imports QSet newLibraries; QVector pathsToScan; pathsToScan.reserve(paths.size()); { QMutexLocker l(&modelManager->m_mutex); for (int i = 0; i < paths.size(); ++i) { PathAndLanguage pAndL = paths.at(i); QString cPath = QDir::cleanPath(pAndL.path().toString()); if (!forceRescan && modelManager->m_scannedPaths.contains(cPath)) continue; pathsToScan.append(ScanItem(cPath, 0, pAndL.language())); modelManager->m_scannedPaths.insert(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 Snapshot snapshot = modelManager->snapshot(); bool isCanceled = future.isCanceled(); while (!pathsToScan.isEmpty() && !isCanceled) { ScanItem toScan = pathsToScan.last(); pathsToScan.pop_back(); int pathBudget = (1 << (maxScanDepth + 2 - toScan.depth)); if (forceRescan || !scannedPaths.contains(toScan.path)) { QStringList importedFiles; if (forceRescan || (!findNewQmlLibraryInPath(toScan.path, snapshot, modelManager, &importedFiles, &scannedPaths, &newLibraries, true) && !libOnly && snapshot.documentsInDirectory(toScan.path).isEmpty())) { importedFiles += filesInDirectoryForLanguages(toScan.path, toScan.language.companionLanguages()); } 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, toScan.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 | QDir::NoDotAndDotDot)); 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, toScan.language)); } else { workDone += pathBudget * 3 / 4; } future.setProgressValue(progressRange * workDone / totalWork); isCanceled = future.isCanceled(); } future.setProgressValue(progressRange); if (isCanceled) { // assume no work has been done QMutexLocker l(&modelManager->m_mutex); for (int i = 0; i < paths.size(); ++i) modelManager->m_scannedPaths.remove(paths.at(i).path().toString()); } } QStringList ModelManagerInterface::importPathsNames() const { QStringList names; QMutexLocker l(&m_mutex); names.reserve(m_allImportPaths.size()); for (const PathAndLanguage &x: m_allImportPaths) names << x.path().toString(); return names; } QmlLanguageBundles ModelManagerInterface::activeBundles() const { QMutexLocker l(&m_mutex); return m_activeBundles; } QmlLanguageBundles ModelManagerInterface::extendedBundles() const { QMutexLocker l(&m_mutex); return m_extendedBundles; } void ModelManagerInterface::maybeScan(const PathsAndLanguages &importPaths) { if (!m_indexerEnabled) return; PathsAndLanguages pathToScan; { QMutexLocker l(&m_mutex); foreach (const PathAndLanguage &importPath, importPaths) if (!m_scannedPaths.contains(importPath.path().toString())) pathToScan.maybeInsert(importPath); } if (pathToScan.length() > 1) { QFuture result = Utils::runAsync(&ModelManagerInterface::importScan, workingCopyInternal(), pathToScan, this, true, true, false); cleanupFutures(); m_futures.append(result); addTaskInternal(result, tr("Scanning QML Imports"), Constants::TASK_IMPORT_SCAN); } } void ModelManagerInterface::updateImportPaths() { if (!m_indexerEnabled) return; PathsAndLanguages allImportPaths; QmlLanguageBundles activeBundles; QmlLanguageBundles extendedBundles; QMapIterator pInfoIter(m_projects); QHashIterator vCtxsIter = m_defaultVContexts; while (pInfoIter.hasNext()) { pInfoIter.next(); const PathsAndLanguages &iPaths = pInfoIter.value().importPaths; for (int i = 0; i < iPaths.size(); ++i) { PathAndLanguage pAndL = iPaths.at(i); const QString canonicalPath = pAndL.path().toFileInfo().canonicalFilePath(); if (!canonicalPath.isEmpty()) allImportPaths.maybeInsert(Utils::FileName::fromString(canonicalPath), pAndL.language()); } } while (vCtxsIter.hasNext()) { vCtxsIter.next(); foreach (const QString &path, vCtxsIter.value().paths) allImportPaths.maybeInsert(Utils::FileName::fromString(path), vCtxsIter.value().language); } pInfoIter.toFront(); while (pInfoIter.hasNext()) { pInfoIter.next(); activeBundles.mergeLanguageBundles(pInfoIter.value().activeBundle); foreach (Dialect l, pInfoIter.value().activeBundle.languages()) { foreach (const QString &path, pInfoIter.value().activeBundle.bundleForLanguage(l) .searchPaths().stringList()) { const QString canonicalPath = QFileInfo(path).canonicalFilePath(); if (!canonicalPath.isEmpty()) allImportPaths.maybeInsert(Utils::FileName::fromString(canonicalPath), l); } } } pInfoIter.toFront(); while (pInfoIter.hasNext()) { pInfoIter.next(); QString pathAtt = pInfoIter.value().qtQmlPath; if (!pathAtt.isEmpty()) allImportPaths.maybeInsert(Utils::FileName::fromString(pathAtt), Dialect::QmlQtQuick2); } { QString pathAtt = defaultProjectInfo().qtQmlPath; if (!pathAtt.isEmpty()) allImportPaths.maybeInsert(Utils::FileName::fromString(pathAtt), Dialect::QmlQtQuick2); } foreach (const QString &path, m_defaultImportPaths) allImportPaths.maybeInsert(Utils::FileName::fromString(path), Dialect::Qml); allImportPaths.compact(); { QMutexLocker l(&m_mutex); m_allImportPaths = allImportPaths; m_activeBundles = activeBundles; m_extendedBundles = extendedBundles; } // check if any file in the snapshot imports something new in the new paths Snapshot snapshot = m_validSnapshot; QStringList importedFiles; QSet scannedPaths; QSet newLibraries; foreach (const Document::Ptr &doc, snapshot) findNewLibraryImports(doc, snapshot, this, &importedFiles, &scannedPaths, &newLibraries); updateSourceFiles(importedFiles, true); if (!m_shouldScanImports) return; maybeScan(allImportPaths); } void ModelManagerInterface::loadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri, const QString &importVersion) { m_pluginDumper->loadPluginTypes(libraryPath, importPath, importUri, importVersion); } +#if 0 // is called *inside a c++ parsing thread*, to allow hanging on to source and ast void ModelManagerInterface::maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc) { // avoid scanning documents without source code available doc->keepSourceAndAST(); if (doc->utf8Source().isEmpty()) { doc->releaseSourceAndAST(); return; } // keep source and AST alive if we want to scan for register calls const bool scan = FindExportedCppTypes::maybeExportsTypes(doc); if (!scan) doc->releaseSourceAndAST(); // delegate actual queuing to the gui thread QMetaObject::invokeMethod(this, "queueCppQmlTypeUpdate", Q_ARG(CPlusPlus::Document::Ptr, doc), Q_ARG(bool, scan)); } void ModelManagerInterface::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan) { QPair prev = m_queuedCppDocuments.value(doc->fileName()); if (prev.first && prev.second) prev.first->releaseSourceAndAST(); m_queuedCppDocuments.insert(doc->fileName(), {doc, scan}); m_updateCppQmlTypesTimer->start(); } void ModelManagerInterface::startCppQmlTypeUpdate() { // if a future is still running, delay if (m_cppQmlTypesUpdater.isRunning()) { m_updateCppQmlTypesTimer->start(); return; } CPlusPlus::CppModelManagerBase *cppModelManager = CPlusPlus::CppModelManagerBase::instance(); if (!cppModelManager) return; m_cppQmlTypesUpdater = Utils::runAsync(&ModelManagerInterface::updateCppQmlTypes, this, cppModelManager->snapshot(), m_queuedCppDocuments); m_queuedCppDocuments.clear(); } +#endif QMutex *ModelManagerInterface::mutex() const { return &m_mutex; } void ModelManagerInterface::asyncReset() { m_asyncResetTimer->start(); } +#if 0 bool rescanExports(const QString &fileName, FindExportedCppTypes &finder, ModelManagerInterface::CppDataHash &newData) { bool hasNewInfo = false; QList exported = finder.exportedTypes(); QHash contextProperties = finder.contextProperties(); if (exported.isEmpty() && contextProperties.isEmpty()) { hasNewInfo = hasNewInfo || newData.remove(fileName) > 0; } else { ModelManagerInterface::CppData &data = newData[fileName]; if (!hasNewInfo && (data.exportedTypes.size() != exported.size() || data.contextProperties != contextProperties)) hasNewInfo = true; if (!hasNewInfo) { QHash newFingerprints; foreach (LanguageUtils::FakeMetaObject::ConstPtr newType, exported) newFingerprints[newType->className()]=newType->fingerprint(); foreach (LanguageUtils::FakeMetaObject::ConstPtr oldType, data.exportedTypes) { if (newFingerprints.value(oldType->className()) != oldType->fingerprint()) { hasNewInfo = true; break; } } } data.exportedTypes = exported; data.contextProperties = contextProperties; } return hasNewInfo; } void ModelManagerInterface::updateCppQmlTypes(QFutureInterface &futureInterface, ModelManagerInterface *qmlModelManager, CPlusPlus::Snapshot snapshot, QHash > documents) { futureInterface.setProgressRange(0, documents.size()); futureInterface.setProgressValue(0); CppDataHash newData; QHash > newDeclarations; { QMutexLocker locker(&qmlModelManager->m_cppDataMutex); newData = qmlModelManager->m_cppDataHash; newDeclarations = qmlModelManager->m_cppDeclarationFiles; } FindExportedCppTypes finder(snapshot); bool hasNewInfo = false; typedef QPair DocScanPair; foreach (const DocScanPair &pair, documents) { if (futureInterface.isCanceled()) return; futureInterface.setProgressValue(futureInterface.progressValue() + 1); CPlusPlus::Document::Ptr doc = pair.first; const bool scan = pair.second; const QString fileName = doc->fileName(); if (!scan) { hasNewInfo = newData.remove(fileName) > 0 || hasNewInfo; foreach (const CPlusPlus::Document::Ptr &savedDoc, newDeclarations.value(fileName)) { finder(savedDoc); hasNewInfo = rescanExports(savedDoc->fileName(), finder, newData) || hasNewInfo; } continue; } for (auto it = newDeclarations.begin(), end = newDeclarations.end(); it != end;) { for (auto docIt = it->begin(), endDocIt = it->end(); docIt != endDocIt;) { CPlusPlus::Document::Ptr &savedDoc = *docIt; if (savedDoc->fileName() == fileName) { savedDoc->releaseSourceAndAST(); it->erase(docIt); break; } else { ++docIt; } } if (it->isEmpty()) it = newDeclarations.erase(it); else ++it; } foreach (const QString &declarationFile, finder(doc)) { newDeclarations[declarationFile].append(doc); doc->keepSourceAndAST(); // keep for later reparsing when dependent doc changes } hasNewInfo = rescanExports(fileName, finder, newData) || hasNewInfo; doc->releaseSourceAndAST(); } QMutexLocker locker(&qmlModelManager->m_cppDataMutex); qmlModelManager->m_cppDataHash = newData; qmlModelManager->m_cppDeclarationFiles = newDeclarations; if (hasNewInfo) // one could get away with re-linking the cpp types... QMetaObject::invokeMethod(qmlModelManager, "asyncReset"); } +#endif ModelManagerInterface::CppDataHash ModelManagerInterface::cppData() const { QMutexLocker locker(&m_cppDataMutex); return m_cppDataHash; } LibraryInfo ModelManagerInterface::builtins(const Document::Ptr &doc) const { ProjectInfo info = projectInfoForPath(doc->fileName()); if (!info.isValid()) return LibraryInfo(); if (!info.qtQmlPath.isEmpty()) return m_validSnapshot.libraryInfo(info.qtQmlPath); return m_validSnapshot.libraryInfo(info.qtImportsPath); } ViewerContext ModelManagerInterface::completeVContext(const ViewerContext &vCtx, const Document::Ptr &doc) const { ViewerContext res = vCtx; if (!doc.isNull() && ((vCtx.language == Dialect::AnyLanguage && doc->language() != Dialect::NoLanguage) || (vCtx.language == Dialect::Qml && (doc->language() == Dialect::QmlQtQuick2 || doc->language() == Dialect::QmlQtQuick2Ui)))) res.language = doc->language(); ProjectInfo info; if (!doc.isNull()) info = projectInfoForPath(doc->fileName()); ViewerContext defaultVCtx = defaultVContext(res.language, Document::Ptr(0), false); ProjectInfo defaultInfo = defaultProjectInfo(); if (info.qtImportsPath.isEmpty()) info.qtImportsPath = defaultInfo.qtImportsPath; if (info.qtQmlPath.isEmpty()) info.qtQmlPath = defaultInfo.qtQmlPath; switch (res.flags) { case ViewerContext::Complete: break; case ViewerContext::AddAllPathsAndDefaultSelectors: res.selectors.append(defaultVCtx.selectors); Q_FALLTHROUGH(); case ViewerContext::AddAllPaths: { foreach (const QString &path, defaultVCtx.paths) res.maybeAddPath(path); switch (res.language.dialect()) { case Dialect::AnyLanguage: case Dialect::Qml: res.maybeAddPath(info.qtQmlPath); res.maybeAddPath(info.qtImportsPath); Q_FALLTHROUGH(); case Dialect::QmlQtQuick2: case Dialect::QmlQtQuick2Ui: { if (res.language == Dialect::QmlQtQuick2 || res.language == Dialect::QmlQtQuick2Ui) res.maybeAddPath(info.qtQmlPath); QList allProjects; { QMutexLocker locker(&m_mutex); allProjects = m_projects.values(); } std::sort(allProjects.begin(), allProjects.end(), &pInfoLessThanImports); QList languages = res.language.companionLanguages(); foreach (const ProjectInfo &pInfo, allProjects) { for (int i = 0; i< pInfo.importPaths.size(); ++i) { PathAndLanguage pAndL = pInfo.importPaths.at(i); if (languages.contains(pAndL.language()) || pAndL.language().companionLanguages().contains(res.language)) res.maybeAddPath(pAndL.path().toString()); } } foreach (const QString &path, environmentImportPaths()) res.maybeAddPath(path); break; } case Dialect::NoLanguage: case Dialect::JavaScript: case Dialect::QmlTypeInfo: case Dialect::Json: case Dialect::QmlQbs: case Dialect::QmlProject: break; } break; } case ViewerContext::AddDefaultPathsAndSelectors: res.selectors.append(defaultVCtx.selectors); Q_FALLTHROUGH(); case ViewerContext::AddDefaultPaths: foreach (const QString &path, defaultVCtx.paths) res.maybeAddPath(path); if (res.language == Dialect::AnyLanguage || res.language == Dialect::Qml || res.language == Dialect::QmlQtQuick2 || res.language == Dialect::QmlQtQuick2Ui) res.maybeAddPath(info.qtImportsPath); if (res.language == Dialect::AnyLanguage || res.language == Dialect::Qml) res.maybeAddPath(info.qtQmlPath); if (res.language == Dialect::AnyLanguage || res.language == Dialect::Qml || res.language == Dialect::QmlQtQuick2 || res.language == Dialect::QmlQtQuick2Ui) { foreach (const QString &path, environmentImportPaths()) res.maybeAddPath(path); } break; } res.flags = ViewerContext::Complete; return res; } ViewerContext ModelManagerInterface::defaultVContext(Dialect language, const Document::Ptr &doc, bool autoComplete) const { if (!doc.isNull()) { if (language == Dialect::AnyLanguage && doc->language() != Dialect::NoLanguage) language = doc->language(); else if (language == Dialect::Qml && (doc->language() == Dialect::QmlQtQuick2 || doc->language() == Dialect::QmlQtQuick2Ui)) language = doc->language(); } ViewerContext defaultCtx; { QMutexLocker locker(&m_mutex); defaultCtx = m_defaultVContexts.value(language); } defaultCtx.language = language; if (autoComplete) return completeVContext(defaultCtx, doc); else return defaultCtx; } ModelManagerInterface::ProjectInfo ModelManagerInterface::defaultProjectInfo() const { QMutexLocker l(mutex()); return m_defaultProjectInfo; } ModelManagerInterface::ProjectInfo ModelManagerInterface::defaultProjectInfoForProject( ProjectExplorer::Project *) const { return ModelManagerInterface::ProjectInfo(); } void ModelManagerInterface::setDefaultVContext(const ViewerContext &vContext) { QMutexLocker locker(&m_mutex); m_defaultVContexts[vContext.language] = vContext; } void ModelManagerInterface::joinAllThreads() { foreach (QFuture future, m_futures) future.waitForFinished(); m_futures.clear(); } Document::Ptr ModelManagerInterface::ensuredGetDocumentForPath(const QString &filePath) { QmlJS::Document::Ptr document = newestSnapshot().document(filePath); if (!document) { document = QmlJS::Document::create(filePath, QmlJS::Dialect::Qml); QMutexLocker lock(&m_mutex); m_newestSnapshot.insert(document); } return document; } void ModelManagerInterface::resetCodeModel() { QStringList documents; { QMutexLocker locker(&m_mutex); // find all documents currently in the code model foreach (Document::Ptr doc, m_validSnapshot) documents.append(doc->fileName()); // reset the snapshot m_validSnapshot = Snapshot(); m_newestSnapshot = Snapshot(); } // start a reparse thread updateSourceFiles(documents, false); } } // namespace QmlJS diff --git a/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.h b/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.h index 430f4b880e..080f131f35 100644 --- a/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.h +++ b/plugins/qmljs/3rdparty/qtcreator-libs/qmljs/qmljsmodelmanagerinterface.h @@ -1,295 +1,295 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** 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 The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #pragma once #include "qmljs_global.h" #include "qmljsbundle.h" #include "qmljsdocument.h" #include "qmljsqrcparser.h" #include "qmljsdialect.h" -#include +// #include #include #include #include #include #include #include QT_FORWARD_DECLARE_CLASS(QTimer) namespace ProjectExplorer { class Project; } namespace QmlJS { class Snapshot; class PluginDumper; class QMLJS_EXPORT ModelManagerInterface: public QObject { Q_OBJECT public: enum QrcResourceSelector { ActiveQrcResources, AllQrcResources }; class ProjectInfo { public: ProjectInfo() : tryQmlDump(false), qmlDumpHasRelocatableFlag(true) { } ProjectInfo(QPointer project) : project(project) , tryQmlDump(false), qmlDumpHasRelocatableFlag(true) { } explicit operator bool() const { return ! project.isNull(); } bool isValid() const { return ! project.isNull(); } bool isNull() const { return project.isNull(); } public: // attributes QPointer project; QStringList sourceFiles; PathsAndLanguages importPaths; QStringList activeResourceFiles; QStringList allResourceFiles; QHash resourceFileContents; // whether trying to run qmldump makes sense bool tryQmlDump; bool qmlDumpHasRelocatableFlag; QString qmlDumpPath; ::Utils::Environment qmlDumpEnvironment; QString qtImportsPath; QString qtQmlPath; QString qtVersionString; QmlJS::QmlLanguageBundles activeBundle; QmlJS::QmlLanguageBundles extendedBundle; }; class WorkingCopy { public: typedef QHash > Table; void insert(const QString &fileName, const QString &source, int revision = 0) { _elements.insert(fileName, {source, revision}); } bool contains(const QString &fileName) const { return _elements.contains(fileName); } QString source(const QString &fileName) const { return _elements.value(fileName).first; } QPair get(const QString &fileName) const { return _elements.value(fileName); } Table all() const { return _elements; } private: Table _elements; }; class CppData { public: QList exportedTypes; QHash contextProperties; }; typedef QHash CppDataHash; typedef QHashIterator CppDataHashIterator; public: ModelManagerInterface(QObject *parent = 0); ~ModelManagerInterface() override; static Dialect guessLanguageOfFile(const QString &fileName); static QStringList globPatternsForLanguages(const QList languages); static ModelManagerInterface *instance(); static void writeWarning(const QString &msg); static WorkingCopy workingCopy(); QmlJS::Snapshot snapshot() const; QmlJS::Snapshot newestSnapshot() const; void activateScan(); void updateSourceFiles(const QStringList &files, bool emitDocumentOnDiskChanged); void fileChangedOnDisk(const QString &path); void removeFiles(const QStringList &files); QStringList qrcPathsForFile(const QString &file, const QLocale *locale = 0, ProjectExplorer::Project *project = 0, QrcResourceSelector resources = AllQrcResources); QStringList filesAtQrcPath(const QString &path, const QLocale *locale = 0, ProjectExplorer::Project *project = 0, QrcResourceSelector resources = AllQrcResources); QMap filesInQrcPath(const QString &path, const QLocale *locale = 0, ProjectExplorer::Project *project = 0, bool addDirs = false, QrcResourceSelector resources = AllQrcResources); QList projectInfos() const; ProjectInfo projectInfo(ProjectExplorer::Project *project, const ModelManagerInterface::ProjectInfo &defaultValue = ProjectInfo()) const; void updateProjectInfo(const ProjectInfo &pinfo, ProjectExplorer::Project *p); void updateDocument(QmlJS::Document::Ptr doc); void updateLibraryInfo(const QString &path, const QmlJS::LibraryInfo &info); void emitDocumentChangedOnDisk(QmlJS::Document::Ptr doc); void updateQrcFile(const QString &path); ProjectInfo projectInfoForPath(const QString &path) const; QList allProjectInfosForPath(const QString &path) const; bool isIdle() const ; QStringList importPathsNames() const; QmlJS::QmlLanguageBundles activeBundles() const; QmlJS::QmlLanguageBundles extendedBundles() const; void loadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri, const QString &importVersion); CppDataHash cppData() const; LibraryInfo builtins(const Document::Ptr &doc) const; ViewerContext completeVContext(const ViewerContext &vCtx, const Document::Ptr &doc = Document::Ptr(0)) const; ViewerContext defaultVContext(Dialect language = Dialect::Qml, const Document::Ptr &doc = Document::Ptr(0), bool autoComplete = true) const; void setDefaultVContext(const ViewerContext &vContext); virtual ProjectInfo defaultProjectInfo() const; virtual ProjectInfo defaultProjectInfoForProject(ProjectExplorer::Project *project) const; // Blocks until all parsing threads are done. Used for testing. void joinAllThreads(); QmlJS::Document::Ptr ensuredGetDocumentForPath(const QString &filePath); static void importScan(QFutureInterface &future, WorkingCopy workingCopyInternal, PathsAndLanguages paths, ModelManagerInterface *modelManager, bool emitDocChangedOnDisk, bool libOnly = true, bool forceRescan = false); virtual void resetCodeModel(); void removeProjectInfo(ProjectExplorer::Project *project); - void maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc); +// void maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc); -signals: +Q_SIGNALS: void documentUpdated(QmlJS::Document::Ptr doc); void documentChangedOnDisk(QmlJS::Document::Ptr doc); void aboutToRemoveFiles(const QStringList &files); void libraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info); void projectInfoUpdated(const ProjectInfo &pinfo); void projectPathChanged(const QString &projectPath); protected: - Q_INVOKABLE void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan); +// Q_INVOKABLE void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan); Q_INVOKABLE void asyncReset(); - virtual void startCppQmlTypeUpdate(); +// virtual void startCppQmlTypeUpdate(); QMutex *mutex() const; virtual QHash languageForSuffix() const; virtual void writeMessageInternal(const QString &msg) const; virtual WorkingCopy workingCopyInternal() const; virtual void addTaskInternal(QFuture result, const QString &msg, const char *taskId) const; QFuture refreshSourceFiles(const QStringList &sourceFiles, bool emitDocumentOnDiskChanged); static void parseLoop(QSet &scannedPaths, QSet &newLibraries, WorkingCopy workingCopyInternal, QStringList files, ModelManagerInterface *modelManager, QmlJS::Dialect mainLanguage, bool emitDocChangedOnDisk, std::function reportProgress); static void parse(QFutureInterface &future, WorkingCopy workingCopyInternal, QStringList files, ModelManagerInterface *modelManager, QmlJS::Dialect mainLanguage, bool emitDocChangedOnDisk); - static void updateCppQmlTypes(QFutureInterface &futureInterface, - ModelManagerInterface *qmlModelManager, - CPlusPlus::Snapshot snapshot, - QHash > documents); +// static void updateCppQmlTypes(QFutureInterface &futureInterface, +// ModelManagerInterface *qmlModelManager, +// CPlusPlus::Snapshot snapshot, +// QHash > documents); void maybeScan(const PathsAndLanguages &importPaths); void updateImportPaths(); void loadQmlTypeDescriptionsInternal(const QString &path); void setDefaultProject(const ProjectInfo &pInfo, ProjectExplorer::Project *p); private: void cleanupFutures(); void iterateQrcFiles(ProjectExplorer::Project *project, QrcResourceSelector resources, std::function callback); mutable QMutex m_mutex; QmlJS::Snapshot m_validSnapshot; QmlJS::Snapshot m_newestSnapshot; PathsAndLanguages m_allImportPaths; QStringList m_defaultImportPaths; QmlJS::QmlLanguageBundles m_activeBundles; QmlJS::QmlLanguageBundles m_extendedBundles; QHash m_defaultVContexts; bool m_shouldScanImports; QSet m_scannedPaths; QTimer *m_updateCppQmlTypesTimer; QTimer *m_asyncResetTimer; - QHash > m_queuedCppDocuments; +// QHash > m_queuedCppDocuments; QFuture m_cppQmlTypesUpdater; QrcCache m_qrcCache; QHash m_qrcContents; CppDataHash m_cppDataHash; - QHash > m_cppDeclarationFiles; +// QHash > m_cppDeclarationFiles; mutable QMutex m_cppDataMutex; // project integration QMap m_projects; ProjectInfo m_defaultProjectInfo; ProjectExplorer::Project *m_defaultProject; QMultiHash m_fileToProject; PluginDumper *m_pluginDumper; QList> m_futures; bool m_indexerEnabled; }; } // namespace QmlJS diff --git a/plugins/qmljs/3rdparty/qtcreator-libs/utils/filesystemwatcher.h b/plugins/qmljs/3rdparty/qtcreator-libs/utils/filesystemwatcher.h index 1f029eae4d..0e13459b69 100644 --- a/plugins/qmljs/3rdparty/qtcreator-libs/utils/filesystemwatcher.h +++ b/plugins/qmljs/3rdparty/qtcreator-libs/utils/filesystemwatcher.h @@ -1,81 +1,81 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** 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 The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #pragma once #include "utils_global.h" #include namespace Utils { class FileSystemWatcherPrivate; // Documentation inside. class QTCREATOR_UTILS_EXPORT FileSystemWatcher : public QObject { Q_OBJECT public: enum WatchMode { WatchModifiedDate, WatchAllChanges }; explicit FileSystemWatcher(QObject *parent = nullptr); explicit FileSystemWatcher(int id, QObject *parent = nullptr); ~FileSystemWatcher() override; void addFile(const QString &file, WatchMode wm); void addFiles(const QStringList &files, WatchMode wm); void removeFile(const QString &file); void removeFiles(const QStringList &files); bool watchesFile(const QString &file) const; QStringList files() const; void addDirectory(const QString &file, WatchMode wm); void addDirectories(const QStringList &files, WatchMode wm); void removeDirectory(const QString &file); void removeDirectories(const QStringList &files); bool watchesDirectory(const QString &file) const; QStringList directories() const; -signals: +Q_SIGNALS: void fileChanged(const QString &path); void directoryChanged(const QString &path); private: void init(); void slotFileChanged(const QString &path); void slotDirectoryChanged(const QString &path); FileSystemWatcherPrivate *d; }; } // namespace Utils diff --git a/plugins/qmljs/3rdparty/qtcreator-libs/utils/osspecificaspects.h b/plugins/qmljs/3rdparty/qtcreator-libs/utils/osspecificaspects.h index 58a4234dba..662a7895ac 100644 --- a/plugins/qmljs/3rdparty/qtcreator-libs/utils/osspecificaspects.h +++ b/plugins/qmljs/3rdparty/qtcreator-libs/utils/osspecificaspects.h @@ -1,80 +1,80 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** 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 The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #pragma once #include "utils_global.h" #include #include #define QTC_WIN_EXE_SUFFIX ".exe" namespace Utils { // Add more as needed. enum OsType { OsTypeWindows, OsTypeLinux, OsTypeMac, OsTypeOtherUnix, OsTypeOther }; namespace OsSpecificAspects { QTCREATOR_UTILS_EXPORT inline QString withExecutableSuffix(OsType osType, const QString &executable) { QString finalName = executable; if (osType == OsTypeWindows) finalName += QLatin1String(QTC_WIN_EXE_SUFFIX); return finalName; } QTCREATOR_UTILS_EXPORT inline Qt::CaseSensitivity fileNameCaseSensitivity(OsType osType) { return osType == OsTypeWindows || osType == OsTypeMac ? Qt::CaseInsensitive : Qt::CaseSensitive; } QTCREATOR_UTILS_EXPORT inline QChar pathListSeparator(OsType osType) { return QLatin1Char(osType == OsTypeWindows ? ';' : ':'); } QTCREATOR_UTILS_EXPORT inline Qt::KeyboardModifier controlModifier(OsType osType) { return osType == OsTypeMac ? Qt::MetaModifier : Qt::ControlModifier; } QTCREATOR_UTILS_EXPORT inline QString pathWithNativeSeparators(OsType osType, const QString &pathName) { if (osType == OsTypeWindows) { - const int pos = pathName.indexOf('/'); + const int pos = pathName.indexOf(QLatin1Char('/')); if (pos >= 0) { QString n = pathName; - std::replace(std::begin(n) + pos, std::end(n), '/', '\\'); + std::replace(std::begin(n) + pos, std::end(n), QLatin1Char('/'), QLatin1Char('\\')); return n; } } return pathName; } } // namespace OsSpecificAspects } // namespace Utils