From 6eadfa3ac0bea0e91e3c7386b56047b59623adaf Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Thu, 18 Mar 2010 15:43:33 +0100
Subject: [PATCH] Read qmldir files in a thread and cache them in Snapshot.

Reviewed-by: Erik Verbruggen
---
 src/libs/qmljs/parser/cmd.sed                 |  8 ++++
 src/libs/qmljs/parser/gen-parser.sh           |  2 +
 src/libs/qmljs/parser/qmldirparser_p.h        |  4 +-
 src/libs/qmljs/qmljsdocument.cpp              | 21 ++++++++++
 src/libs/qmljs/qmljsdocument.h                | 27 +++++++++++++
 src/libs/qmljs/qmljslink.cpp                  | 14 ++-----
 src/plugins/qmljseditor/qmljsmodelmanager.cpp | 38 ++++++++++++++++++-
 src/plugins/qmljseditor/qmljsmodelmanager.h   |  3 ++
 8 files changed, 104 insertions(+), 13 deletions(-)

diff --git a/src/libs/qmljs/parser/cmd.sed b/src/libs/qmljs/parser/cmd.sed
index fa2ef28f2f5..740da4e3d1a 100644
--- a/src/libs/qmljs/parser/cmd.sed
+++ b/src/libs/qmljs/parser/cmd.sed
@@ -1,3 +1,11 @@
 s/qdeclarative/qml/g
 s/QDECLARATIVE/QML/g
 s/QDeclarative/Qml/g
+
+# adjust pri file
+s/    \$\$PWD\/qmljsglobal_p.h/    $$PWD\/qmljsglobal_p.h \\\
+    $$PWD\/qmldirparser_p.h \\\
+    $$PWD\/qmlerror.h/
+s/    \$\$PWD\/qmljsparser.cpp/    $$PWD\/qmljsparser.cpp \\\
+    $$PWD\/qmldirparser.cpp \\\
+    $$PWD\/qmlerror.cpp/
diff --git a/src/libs/qmljs/parser/gen-parser.sh b/src/libs/qmljs/parser/gen-parser.sh
index 1925432977a..5b999e27f15 100755
--- a/src/libs/qmljs/parser/gen-parser.sh
+++ b/src/libs/qmljs/parser/gen-parser.sh
@@ -10,3 +10,5 @@ for i in $QTDIR/src/declarative/qml/qdeclarative{error.{h,cpp},dirparser{_p.h,.c
     sed -f $me/cmd.sed $i > $me/$(echo $(basename $i) | sed s/qdeclarative/qml/)
 done
 
+# export QmlDirParser
+perl -p -0777 -i -e 's/QT_BEGIN_NAMESPACE\n\nclass QmlError;\n\nclass QmlDirParser/#include "qmljsglobal_p.h"\n\nQT_BEGIN_NAMESPACE\n\nclass QmlError;\n\nclass QML_PARSER_EXPORT QmlDirParser/' qmldirparser_p.h
diff --git a/src/libs/qmljs/parser/qmldirparser_p.h b/src/libs/qmljs/parser/qmldirparser_p.h
index c58c03fb8c0..8ab6095c382 100644
--- a/src/libs/qmljs/parser/qmldirparser_p.h
+++ b/src/libs/qmljs/parser/qmldirparser_p.h
@@ -56,11 +56,13 @@
 #include <QtCore/QUrl>
 #include <QtCore/QHash>
 
+#include "qmljsglobal_p.h"
+
 QT_BEGIN_NAMESPACE
 
 class QmlError;
 
-class QmlDirParser
+class QML_PARSER_EXPORT QmlDirParser
 {
     Q_DISABLE_COPY(QmlDirParser)
 
diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp
index 68693c4772d..6b4f3ed96ba 100644
--- a/src/libs/qmljs/qmljsdocument.cpp
+++ b/src/libs/qmljs/qmljsdocument.cpp
@@ -202,6 +202,22 @@ Bind *Document::bind() const
     return _bind;
 }
 
+LibraryInfo::LibraryInfo()
+    : _valid(false)
+{
+}
+
+LibraryInfo::LibraryInfo(const QmlDirParser &parser)
+    : _valid(true)
+    , _components(parser.components())
+    , _plugins(parser.plugins())
+{
+}
+
+LibraryInfo::~LibraryInfo()
+{
+}
+
 Snapshot::Snapshot()
 {
 }
@@ -216,6 +232,11 @@ void Snapshot::insert(const Document::Ptr &document)
         _documents.insert(document->fileName(), document);
 }
 
+void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
+{
+    _libraries.insert(path, info);
+}
+
 Document::Ptr Snapshot::documentFromSource(const QString &code,
                                            const QString &fileName) const
 {
diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h
index 126f364e620..fd3742e8b86 100644
--- a/src/libs/qmljs/qmljsdocument.h
+++ b/src/libs/qmljs/qmljsdocument.h
@@ -35,6 +35,7 @@
 #include <QtCore/QSharedPointer>
 #include <QtCore/QString>
 
+#include "parser/qmldirparser_p.h"
 #include "parser/qmljsengine_p.h"
 #include "qmljs_global.h"
 
@@ -102,10 +103,32 @@ private:
     friend class Snapshot;
 };
 
+class QMLJS_EXPORT LibraryInfo
+{
+    bool _valid;
+    QList<QmlDirParser::Component> _components;
+    QList<QmlDirParser::Plugin> _plugins;
+
+public:
+    LibraryInfo();
+    LibraryInfo(const QmlDirParser &parser);
+    ~LibraryInfo();
+
+    QList<QmlDirParser::Component> components() const
+    { return _components; }
+
+    QList<QmlDirParser::Plugin> plugins() const
+    { return _plugins; }
+
+    bool isValid() const
+    { return _valid; }
+};
+
 class QMLJS_EXPORT Snapshot
 {
     typedef QMap<QString, Document::Ptr> _Base;
     QMap<QString, Document::Ptr> _documents;
+    QMap<QString, LibraryInfo> _libraries;
 
 public:
     Snapshot();
@@ -118,10 +141,14 @@ public:
     const_iterator end() const { return _documents.end(); }
 
     void insert(const Document::Ptr &document);
+    void insertLibraryInfo(const QString &path, const LibraryInfo &info);
 
     Document::Ptr document(const QString &fileName) const
     { return _documents.value(fileName); }
 
+    LibraryInfo libraryInfo(const QString &path) const
+    { return _libraries.value(path); }
+
     Document::Ptr documentFromSource(const QString &code,
                                      const QString &fileName) const;
 
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index f8b184b9c5c..24d417baa87 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -1,7 +1,6 @@
 #include "qmljslink.h"
 
 #include "parser/qmljsast_p.h"
-#include "parser/qmldirparser_p.h"
 #include "qmljsdocument.h"
 #include "qmljsbind.h"
 #include "qmljsscopebuilder.h"
@@ -289,18 +288,13 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A
             QDir dir(importPath);
             if (!dir.cd(package))
                 continue;
-            if (!dir.exists("qmldir"))
-                continue;
 
-            QFile qmldirFile(dir.filePath("qmldir"));
-            qmldirFile.open(QFile::ReadOnly);
-            QString qmldirData = QString::fromUtf8(qmldirFile.readAll());
-            QmlDirParser qmldirParser;
-            qmldirParser.setSource(qmldirData);
-            qmldirParser.parse();
+            const LibraryInfo libraryInfo = _snapshot.libraryInfo(dir.path());
+            if (!libraryInfo.isValid())
+                continue;
 
             QSet<QString> importedTypes;
-            foreach (const QmlDirParser::Component &component, qmldirParser.components()) {
+            foreach (const QmlDirParser::Component &component, libraryInfo.components()) {
                 if (importedTypes.contains(component.typeName))
                     continue;
 
diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.cpp b/src/plugins/qmljseditor/qmljsmodelmanager.cpp
index e765b5fa377..52a781efdd8 100644
--- a/src/plugins/qmljseditor/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljseditor/qmljsmodelmanager.cpp
@@ -36,6 +36,7 @@
 #include <coreplugin/progressmanager/progressmanager.h>
 #include <coreplugin/mimedatabase.h>
 #include <qmljs/qmljsinterpreter.h>
+#include <qmljs/parser/qmldirparser_p.h>
 #include <texteditor/itexteditor.h>
 
 #include <QDir>
@@ -62,9 +63,12 @@ ModelManager::ModelManager(QObject *parent):
     m_synchronizer.setCancelOnWait(true);
 
     qRegisterMetaType<QmlJS::Document::Ptr>("QmlJS::Document::Ptr");
+    qRegisterMetaType<QmlJS::LibraryInfo>("QmlJS::LibraryInfo");
 
     connect(this, SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
             this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr)));
+    connect(this, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)),
+            this, SLOT(onLibraryInfoUpdated(QString,QmlJS::LibraryInfo)));
 
     loadQmlTypeDescriptions();
 
@@ -197,6 +201,16 @@ void ModelManager::onDocumentUpdated(Document::Ptr doc)
     _snapshot.insert(doc);
 }
 
+void ModelManager::emitLibraryInfoUpdated(const QString &path, const LibraryInfo &info)
+{ emit libraryInfoUpdated(path, info); }
+
+void ModelManager::onLibraryInfoUpdated(const QString &path, const LibraryInfo &info)
+{
+    QMutexLocker locker(&m_mutex);
+
+    _snapshot.insertLibraryInfo(path, info);
+}
+
 void ModelManager::parse(QFutureInterface<void> &future,
                             QMap<QString, WorkingCopy> workingCopy,
                             QStringList files,
@@ -270,13 +284,33 @@ void ModelManager::parseDirectories(QFutureInterface<void> &future,
         pattern << glob.pattern();
     foreach (const QRegExp &glob, qmlSourceTy.globPatterns())
         pattern << glob.pattern();
+    pattern << QLatin1String("qmldir");
 
     QStringList importedFiles;
+    QStringList qmldirFiles;
     foreach (const QString &path, directories) {
         QDirIterator fileIterator(path, pattern, QDir::Files,
                                   QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
-        while (fileIterator.hasNext())
-            importedFiles << fileIterator.next();
+        while (fileIterator.hasNext()) {
+            fileIterator.next();
+            if (fileIterator.fileName() == QLatin1String("qmldir"))
+                qmldirFiles << fileIterator.filePath();
+            else
+                importedFiles << fileIterator.filePath();
+        }
+    }
+
+    foreach (const QString &qmldirFilePath, qmldirFiles) {
+        QFile qmldirFile(qmldirFilePath);
+        qmldirFile.open(QFile::ReadOnly);
+        QString qmldirData = QString::fromUtf8(qmldirFile.readAll());
+
+        QmlDirParser qmldirParser;
+        qmldirParser.setSource(qmldirData);
+        qmldirParser.parse();
+
+        modelManager->emitLibraryInfoUpdated(QFileInfo(qmldirFilePath).path(),
+                                             LibraryInfo(qmldirParser));
     }
 
     parse(future, workingCopy, importedFiles, modelManager);
diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.h b/src/plugins/qmljseditor/qmljsmodelmanager.h
index aafdc2a89f5..8ca17240196 100644
--- a/src/plugins/qmljseditor/qmljsmodelmanager.h
+++ b/src/plugins/qmljseditor/qmljsmodelmanager.h
@@ -58,6 +58,7 @@ public:
     virtual void updateSourceDirectories(const QStringList &directories);
 
     void emitDocumentUpdated(QmlJS::Document::Ptr doc);
+    void emitLibraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info);
 
     virtual void setProjectImportPaths(const QStringList &importPaths);
     virtual QStringList importPaths() const;
@@ -65,10 +66,12 @@ public:
 Q_SIGNALS:
     void projectPathChanged(const QString &projectPath);
     void aboutToRemoveFiles(const QStringList &files);
+    void libraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info);
 
 private Q_SLOTS:
     // this should be executed in the GUI thread.
     void onDocumentUpdated(QmlJS::Document::Ptr doc);
+    void onLibraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info);
 
 protected:
     struct WorkingCopy
-- 
GitLab