diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 2c9e40114f3..70403625430 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -86,14 +86,16 @@ bool operator==(const ImportCacheKey &i1, const ImportCacheKey &i2) class QmlJS::LinkPrivate { public: - Document::Ptr doc; Snapshot snapshot; Interpreter::Context *context; QStringList importPaths; QHash importCache; - QList diagnosticMessages; + Document::Ptr doc; + QList *diagnosticMessages; + + QHash > *allDiagnosticMessages; }; /*! @@ -109,16 +111,42 @@ public: \l{Context} with \l{Link}. */ -Link::Link(Context *context, const Document::Ptr &doc, const Snapshot &snapshot, - const QStringList &importPaths) +Link::Link(Context *context, const Snapshot &snapshot, const QStringList &importPaths, + QHash > *messages) : d_ptr(new LinkPrivate) { Q_D(Link); d->context = context; - d->doc = doc; d->snapshot = snapshot; d->importPaths = importPaths; + d->diagnosticMessages = 0; + d->allDiagnosticMessages = messages; + + // populate engine with types from C++ + ModelManagerInterface *modelManager = ModelManagerInterface::instance(); + if (modelManager) { + foreach (const QList &cppTypes, modelManager->cppQmlTypes()) { + engine()->cppQmlTypes().load(engine(), cppTypes); + } + } + + linkImports(); +} + +Link::Link(Context *context, const Snapshot &snapshot, const QStringList &importPaths, + const Document::Ptr &doc, QList *messages) + : d_ptr(new LinkPrivate) +{ + Q_D(Link); + d->context = context; + d->snapshot = snapshot; + d->importPaths = importPaths; + + d->doc = doc; + d->diagnosticMessages = messages; + d->allDiagnosticMessages = 0; + // populate engine with types from C++ ModelManagerInterface *modelManager = ModelManagerInterface::instance(); if (modelManager) { @@ -140,20 +168,16 @@ Interpreter::Engine *Link::engine() return d->context->engine(); } -QList Link::diagnosticMessages() const -{ - Q_D(const Link); - return d->diagnosticMessages; -} - void Link::linkImports() { Q_D(Link); - // do it on d->doc first, to make sure import errors are shown - TypeEnvironment *typeEnv = new TypeEnvironment(engine()); - populateImportedTypes(typeEnv, d->doc); - d->context->setTypeEnvironment(d->doc.data(), typeEnv); + if (d->doc) { + // do it on d->doc first, to make sure import errors are shown + TypeEnvironment *typeEnv = new TypeEnvironment(engine()); + populateImportedTypes(typeEnv, d->doc); + d->context->setTypeEnvironment(d->doc.data(), typeEnv); + } foreach (Document::Ptr doc, d->snapshot) { if (doc == d->doc) @@ -364,18 +388,22 @@ UiQualifiedId *Link::qualifiedTypeNameId(Node *node) void Link::error(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message) { - Q_D(Link); - - if (doc->fileName() == d->doc->fileName()) - d->diagnosticMessages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message)); + appendDiagnostic(doc, DiagnosticMessage(DiagnosticMessage::Error, loc, message)); } void Link::warning(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message) +{ + appendDiagnostic(doc, DiagnosticMessage(DiagnosticMessage::Warning, loc, message)); +} + +void Link::appendDiagnostic(const Document::Ptr &doc, const DiagnosticMessage &message) { Q_D(Link); - if (doc->fileName() == d->doc->fileName()) - d->diagnosticMessages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message)); + if (d->diagnosticMessages && doc->fileName() == d->doc->fileName()) + d->diagnosticMessages->append(message); + if (d->allDiagnosticMessages) + (*d->allDiagnosticMessages)[doc->fileName()].append(message); } void Link::loadQmldirComponents(Interpreter::ObjectValue *import, ComponentVersion version, diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index 1938a35c7df..2fb421dda41 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -54,12 +54,16 @@ class QMLJS_EXPORT Link Q_DECLARE_TR_FUNCTIONS(QmlJS::Link) public: - // Link all documents in snapshot - Link(Interpreter::Context *context, const Document::Ptr &doc, const Snapshot &snapshot, - const QStringList &importPaths); - ~Link(); + // Link all documents in snapshot, collecting all diagnostic messages + Link(Interpreter::Context *context, const Snapshot &snapshot, + const QStringList &importPaths, QHash > *messages = 0); - QList diagnosticMessages() const; + // Link all documents in snapshot, appending the diagnostic messages + // for 'doc' in 'messages' + Link(Interpreter::Context *context, const Snapshot &snapshot, + const QStringList &importPaths, const Document::Ptr &doc, QList *messages); + + ~Link(); private: Interpreter::Engine *engine(); @@ -87,6 +91,7 @@ private: void error(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message); void warning(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message); + void appendDiagnostic(const Document::Ptr &doc, const DiagnosticMessage &message); private: QScopedPointer d_ptr; diff --git a/src/libs/qmljs/qmljslookupcontext.cpp b/src/libs/qmljs/qmljslookupcontext.cpp index 0cd19d894fc..26a5ce968ac 100644 --- a/src/libs/qmljs/qmljslookupcontext.cpp +++ b/src/libs/qmljs/qmljslookupcontext.cpp @@ -47,7 +47,7 @@ public: snapshot(snapshot) { // since we keep the document and snapshot around, we don't need to keep the Link instance - Link link(&context, doc, snapshot, ModelManagerInterface::instance()->importPaths()); + Link link(&context, snapshot, ModelManagerInterface::instance()->importPaths()); ScopeBuilder scopeBuilder(&context, doc, snapshot); scopeBuilder.push(path); diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h index 67f588f83b1..5058d8358ff 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.h +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h @@ -142,6 +142,7 @@ signals: 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); }; } // namespace QmlJS diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 6e3f387da5d..53e4d6d970b 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -283,7 +283,7 @@ public: : m_snapshot(snapshot) , m_doc(doc) , m_context(new Interpreter::Context) - , m_link(m_context, doc, snapshot, importPaths) + , m_link(m_context, snapshot, importPaths, doc, &m_diagnosticLinkMessages) , m_lookupContext(LookupContext::create(doc, snapshot, *m_context, QList())) , m_scopeBuilder(m_context, doc, snapshot) { @@ -542,13 +542,14 @@ public: { return m_lookupContext; } QList diagnosticLinkMessages() const - { return m_link.diagnosticMessages(); } + { return m_diagnosticLinkMessages; } private: Snapshot m_snapshot; Document::Ptr m_doc; Interpreter::Context *m_context; Link m_link; + QList m_diagnosticLinkMessages; LookupContext::Ptr m_lookupContext; ScopeBuilder m_scopeBuilder; }; diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index f6683ec13dd..532139d5373 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -237,7 +237,13 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e addAutoReleasedObject(m_qmlTaskManager); connect(m_modelManager, SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)), - m_qmlTaskManager, SLOT(documentChangedOnDisk(QmlJS::Document::Ptr))); + m_qmlTaskManager, SLOT(updateMessages())); + // recompute messages when information about libraries changes + connect(m_modelManager, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)), + m_qmlTaskManager, SLOT(updateMessages())); + // recompute messages when project data changes (files added or removed) + connect(m_modelManager, SIGNAL(projectInfoUpdated(ProjectInfo)), + m_qmlTaskManager, SLOT(updateMessages())); connect(m_modelManager, SIGNAL(aboutToRemoveFiles(QStringList)), m_qmlTaskManager, SLOT(documentsRemoved(QStringList))); diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 437aa726766..4ed053d82d8 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -626,7 +626,7 @@ static void find_helper(QFutureInterface &future, if (!doc) return; - Link link(&context, doc, snapshot, ModelManagerInterface::instance()->importPaths()); + Link link(&context, snapshot, ModelManagerInterface::instance()->importPaths()); ScopeBuilder builder(&context, doc, snapshot); ScopeAstPath astPath(doc); builder.push(astPath(offset)); diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp index 323b46587ee..bdf8f06cd7a 100644 --- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp +++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp @@ -133,13 +133,9 @@ SemanticInfo SemanticHighlighter::semanticInfo(const SemanticHighlighterSource & semanticInfo.document = doc; QmlJS::Interpreter::Context *ctx = new QmlJS::Interpreter::Context; - QmlJS::Link link(ctx, doc, snapshot, QmlJS::ModelManagerInterface::instance()->importPaths()); + QmlJS::Link link(ctx, snapshot, QmlJS::ModelManagerInterface::instance()->importPaths(), doc, &semanticInfo.semanticMessages); semanticInfo.m_context = QSharedPointer(ctx); - semanticInfo.semanticMessages = link.diagnosticMessages(); - QStringList importPaths; - if (m_modelManager) - importPaths = m_modelManager->importPaths(); QmlJS::Check checker(doc, snapshot, ctx); semanticInfo.semanticMessages.append(checker()); diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp index a998f1d046c..4f582d07576 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.cpp +++ b/src/plugins/qmljseditor/qmltaskmanager.cpp @@ -33,11 +33,21 @@ #include "qmltaskmanager.h" #include "qmljseditorconstants.h" +#include #include #include #include +#include +#include +#include +#include +#include #include +#include +#include + +using namespace QmlJS; namespace QmlJSEditor { namespace Internal { @@ -47,35 +57,103 @@ QmlTaskManager::QmlTaskManager(QObject *parent) : m_taskHub(0) { m_taskHub = ExtensionSystem::PluginManager::instance()->getObject(); + // displaying results incrementally leads to flickering +// connect(&m_messageCollector, SIGNAL(resultsReadyAt(int,int)), +// SLOT(displayResults(int,int))); + connect(&m_messageCollector, SIGNAL(finished()), + SLOT(displayAllResults())); + + m_updateDelay.setInterval(100); + m_updateDelay.setSingleShot(true); + connect(&m_updateDelay, SIGNAL(timeout()), + SLOT(updateMessagesNow())); } -void QmlTaskManager::documentChangedOnDisk(QmlJS::Document::Ptr doc) +void QmlTaskManager::collectMessages(QFutureInterface &future, + Snapshot snapshot, QStringList files, QStringList importPaths) { - const QString fileName = doc->fileName(); - removeTasksForFile(fileName); + Interpreter::Context ctx; + QHash > linkMessages; + Link link(&ctx, snapshot, importPaths, &linkMessages); - foreach (const QmlJS::DiagnosticMessage &msg, doc->diagnosticMessages()) { - ProjectExplorer::Task::TaskType type - = msg.isError() ? ProjectExplorer::Task::Error - : ProjectExplorer::Task::Warning; + foreach (const QString &fileName, files) { + Document::Ptr document = snapshot.document(fileName); + if (!document) + continue; - ProjectExplorer::Task task(type, msg.message, fileName, msg.loc.startLine, - Constants::TASK_CATEGORY_QML); - insertTask(fileName, task); + FileErrorMessages result; + result.fileName = fileName; + result.messages = document->diagnosticMessages(); + result.messages += linkMessages.value(fileName); + + Check checker(document, snapshot, &ctx); + result.messages.append(checker()); + + future.reportResult(result); + if (future.isCanceled()) + break; } } +void QmlTaskManager::updateMessages() +{ + m_updateDelay.start(); +} + +void QmlTaskManager::updateMessagesNow() +{ + // abort any update that's going on already + m_messageCollector.cancel(); + removeAllTasks(); + + // collect all the source files in open projects + ModelManagerInterface *modelManager = ModelManagerInterface::instance(); + QStringList sourceFiles; + foreach (const ModelManagerInterface::ProjectInfo &info, modelManager->projectInfos()) { + sourceFiles += info.sourceFiles; + } + + // process them + QFuture future = + QtConcurrent::run( + &collectMessages, modelManager->snapshot(), sourceFiles, + modelManager->importPaths()); + m_messageCollector.setFuture(future); +} + void QmlTaskManager::documentsRemoved(const QStringList path) { foreach (const QString &item, path) removeTasksForFile(item); } -void QmlTaskManager::insertTask(const QString &fileName, const ProjectExplorer::Task &task) +void QmlTaskManager::displayResults(int begin, int end) { - QList tasks = m_docsWithTasks.value(fileName); + for (int i = begin; i < end; ++i) { + FileErrorMessages result = m_messageCollector.resultAt(i); + foreach (const DiagnosticMessage &msg, result.messages) { + ProjectExplorer::Task::TaskType type + = msg.isError() ? ProjectExplorer::Task::Error + : ProjectExplorer::Task::Warning; + + ProjectExplorer::Task task(type, msg.message, result.fileName, msg.loc.startLine, + Constants::TASK_CATEGORY_QML); + + insertTask(task); + } + } +} + +void QmlTaskManager::displayAllResults() +{ + displayResults(0, m_messageCollector.future().resultCount()); +} + +void QmlTaskManager::insertTask(const ProjectExplorer::Task &task) +{ + QList tasks = m_docsWithTasks.value(task.file); tasks.append(task); - m_docsWithTasks.insert(fileName, tasks); + m_docsWithTasks.insert(task.file, tasks); m_taskHub->addTask(task); } @@ -89,5 +167,16 @@ void QmlTaskManager::removeTasksForFile(const QString &fileName) } } +void QmlTaskManager::removeAllTasks() +{ + QMapIterator > it(m_docsWithTasks); + while (it.hasNext()) { + it.next(); + foreach (const ProjectExplorer::Task &task, it.value()) + m_taskHub->removeTask(task); + } + m_docsWithTasks.clear(); +} + } // Internal } // QmlProjectManager diff --git a/src/plugins/qmljseditor/qmltaskmanager.h b/src/plugins/qmljseditor/qmltaskmanager.h index aa6dbdbe59e..a4b6f80c1ce 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.h +++ b/src/plugins/qmljseditor/qmltaskmanager.h @@ -40,6 +40,12 @@ #include #include #include +#include +#include + +namespace QmlJSEditor { +class QmlJSTextEditorWidget; +} namespace ProjectExplorer { class TaskHub; @@ -57,16 +63,34 @@ public: void extensionsInitialized(); public slots: - void documentChangedOnDisk(QmlJS::Document::Ptr doc); + void updateMessages(); void documentsRemoved(const QStringList path); +private slots: + void displayResults(int begin, int end); + void displayAllResults(); + void updateMessagesNow(); + private: - void insertTask(const QString &fileName, const ProjectExplorer::Task &task); + void insertTask(const ProjectExplorer::Task &task); void removeTasksForFile(const QString &fileName); + void removeAllTasks(); + +private: + class FileErrorMessages + { + public: + QString fileName; + QList messages; + }; + static void collectMessages(QFutureInterface &future, + QmlJS::Snapshot snapshot, QStringList files, QStringList importPaths); private: ProjectExplorer::TaskHub *m_taskHub; QMap > m_docsWithTasks; + QFutureWatcher m_messageCollector; + QTimer m_updateDelay; }; } // Internal diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp index 0b1ba21c26c..eecc55d24f8 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.cpp +++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp @@ -265,6 +265,8 @@ void ModelManager::updateProjectInfo(const ProjectInfo &pinfo) newFiles += file; } updateSourceFiles(newFiles, false); + + emit projectInfoUpdated(pinfo); } void ModelManager::emitDocumentChangedOnDisk(Document::Ptr doc)