From 3f505e9982e2a07ce41e650d9ae547c436f12a43 Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Fri, 24 Sep 2010 14:05:34 +0200
Subject: [PATCH] QmlJS: Add initial 'Find Usages' support.

---
 src/libs/qmljs/qmljscheck.cpp                 |   2 +-
 src/libs/qmljs/qmljsinterpreter.cpp           |   6 +-
 src/libs/qmljs/qmljsinterpreter.h             |   2 +-
 src/libs/qmljs/qmljslink.cpp                  |  81 ---
 src/libs/qmljs/qmljslink.h                    |   8 -
 src/libs/qmljs/qmljslookupcontext.cpp         |   4 +-
 src/libs/qmljs/qmljsmodelmanagerinterface.h   |  26 +
 src/libs/qmljs/qmljsscopebuilder.cpp          |  83 ++-
 src/libs/qmljs/qmljsscopebuilder.h            |  14 +-
 .../designercore/model/texttomodelmerger.cpp  |   2 +-
 src/plugins/qmljseditor/qmljseditor.cpp       |   9 +-
 src/plugins/qmljseditor/qmljseditor.h         |   4 +
 src/plugins/qmljseditor/qmljseditor.pro       |   6 +-
 .../qmljseditor/qmljseditorconstants.h        |   2 +
 src/plugins/qmljseditor/qmljseditorplugin.cpp |  14 +
 src/plugins/qmljseditor/qmljseditorplugin.h   |   1 +
 .../qmljseditor/qmljsfindreferences.cpp       | 685 ++++++++++++++++++
 src/plugins/qmljseditor/qmljsfindreferences.h |  97 +++
 src/plugins/qmljseditor/qmljsmodelmanager.cpp |  51 +-
 src/plugins/qmljseditor/qmljsmodelmanager.h   |  12 +-
 20 files changed, 967 insertions(+), 142 deletions(-)
 create mode 100644 src/plugins/qmljseditor/qmljsfindreferences.cpp
 create mode 100644 src/plugins/qmljseditor/qmljsfindreferences.h

diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index bcab73dcb8e..2ab09a637cb 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -172,7 +172,7 @@ Check::Check(Document::Ptr doc, const Snapshot &snapshot, const Context *linkedC
     : _doc(doc)
     , _snapshot(snapshot)
     , _context(*linkedContextNoScope)
-    , _scopeBuilder(doc, &_context)
+    , _scopeBuilder(&_context, doc, snapshot)
     , _ignoreTypeErrors(false)
 {
 }
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index fa9e2e59e94..302a6df599b 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -1424,17 +1424,21 @@ void Context::setTypeEnvironment(const QmlJS::Document *doc, const TypeEnvironme
     _typeEnvironments[doc->fileName()] = typeEnvironment;
 }
 
-const Value *Context::lookup(const QString &name) const
+const Value *Context::lookup(const QString &name, const ObjectValue **foundInScope) const
 {
     QList<const ObjectValue *> scopes = _scopeChain.all();
     for (int index = scopes.size() - 1; index != -1; --index) {
         const ObjectValue *scope = scopes.at(index);
 
         if (const Value *member = scope->lookupMember(name, this)) {
+            if (foundInScope)
+                *foundInScope = scope;
             return member;
         }
     }
 
+    if (foundInScope)
+        *foundInScope = 0;
     return _engine->undefinedValue();
 }
 
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index f872a7a2238..4474dd43002 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -285,7 +285,7 @@ public:
     const TypeEnvironment *typeEnvironment(const Document *doc) const;
     void setTypeEnvironment(const Document *doc, const TypeEnvironment *typeEnvironment);
 
-    const Value *lookup(const QString &name) const;
+    const Value *lookup(const QString &name, const ObjectValue **foundInScope = 0) const;
     const ObjectValue *lookupType(const Document *doc, AST::UiQualifiedId *qmlTypeName) const;
     const ObjectValue *lookupType(const Document *doc, const QStringList &qmlTypeName) const;
     const Value *lookupReference(const Reference *reference) const;
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 9539cfc581c..55e7a425224 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -114,7 +114,6 @@ Link::Link(Context *context, const Document::Ptr &doc, const Snapshot &snapshot,
     d->importPaths = importPaths;
 
     linkImports();
-    initializeScopeChain();
 }
 
 Link::~Link()
@@ -133,86 +132,6 @@ QList<DiagnosticMessage> Link::diagnosticMessages() const
     return d->diagnosticMessages;
 }
 
-void Link::initializeScopeChain()
-{
-    Q_D(Link);
-
-    ScopeChain &scopeChain = d->context->scopeChain();
-
-    // ### TODO: This object ought to contain the global namespace additions by QML.
-    scopeChain.globalScope = engine()->globalObject();
-
-    if (! d->doc) {
-        scopeChain.update();
-        return;
-    }
-
-    Bind *bind = d->doc->bind();
-    QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes;
-
-    ScopeChain::QmlComponentChain *chain = new ScopeChain::QmlComponentChain;
-    scopeChain.qmlComponentScope = QSharedPointer<const ScopeChain::QmlComponentChain>(chain);
-    if (d->doc->qmlProgram()) {
-        componentScopes.insert(d->doc.data(), chain);
-        makeComponentChain(d->doc, chain, &componentScopes);
-
-        if (const TypeEnvironment *typeEnvironment = d->context->typeEnvironment(d->doc.data()))
-            scopeChain.qmlTypes = typeEnvironment;
-    } else {
-        // add scope chains for all components that import this file
-        foreach (Document::Ptr otherDoc, d->snapshot) {
-            foreach (const ImportInfo &import, otherDoc->bind()->imports()) {
-                if (import.type() == ImportInfo::FileImport && d->doc->fileName() == import.name()) {
-                    ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
-                    componentScopes.insert(otherDoc.data(), component);
-                    chain->instantiatingComponents += component;
-                    makeComponentChain(otherDoc, component, &componentScopes);
-                }
-            }
-        }
-
-        // ### TODO: Which type environment do scripts see?
-
-        if (bind->rootObjectValue())
-            scopeChain.jsScopes += bind->rootObjectValue();
-    }
-
-    scopeChain.update();
-}
-
-void Link::makeComponentChain(
-        Document::Ptr doc,
-        ScopeChain::QmlComponentChain *target,
-        QHash<Document *, ScopeChain::QmlComponentChain *> *components)
-{
-    Q_D(Link);
-
-    if (!doc->qmlProgram())
-        return;
-
-    Bind *bind = doc->bind();
-
-    // add scopes for all components instantiating this one
-    foreach (Document::Ptr otherDoc, d->snapshot) {
-        if (otherDoc == doc)
-            continue;
-        if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), d->context)) {
-            if (components->contains(otherDoc.data())) {
-//                target->instantiatingComponents += components->value(otherDoc.data());
-            } else {
-                ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
-                components->insert(otherDoc.data(), component);
-                target->instantiatingComponents += component;
-
-                makeComponentChain(otherDoc, component, components);
-            }
-        }
-    }
-
-    // build this component scope
-    target->document = doc;
-}
-
 void Link::linkImports()
 {
     Q_D(Link);
diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h
index d81fcc509a6..3a922805549 100644
--- a/src/libs/qmljs/qmljslink.h
+++ b/src/libs/qmljs/qmljslink.h
@@ -63,17 +63,9 @@ public:
 private:
     Interpreter::Engine *engine();
 
-    void makeComponentChain(
-            Document::Ptr doc,
-            Interpreter::ScopeChain::QmlComponentChain *target,
-            QHash<Document *, Interpreter::ScopeChain::QmlComponentChain *> *components);
-
-    static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot,
-                                                   const QStringList &importPaths);
     static AST::UiQualifiedId *qualifiedTypeNameId(AST::Node *node);
 
     void linkImports();
-    void initializeScopeChain();
 
     void populateImportedTypes(Interpreter::TypeEnvironment *typeEnv, Document::Ptr doc);
     Interpreter::ObjectValue *importFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo);
diff --git a/src/libs/qmljs/qmljslookupcontext.cpp b/src/libs/qmljs/qmljslookupcontext.cpp
index d9439481076..5b244b7faec 100644
--- a/src/libs/qmljs/qmljslookupcontext.cpp
+++ b/src/libs/qmljs/qmljslookupcontext.cpp
@@ -46,7 +46,7 @@ public:
         // 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());
 
-        ScopeBuilder scopeBuilder(doc, &context);
+        ScopeBuilder scopeBuilder(&context, doc, snapshot);
         scopeBuilder.push(path);
     }
 
@@ -57,7 +57,7 @@ public:
           doc(doc),
           snapshot(snapshot)
     {
-        ScopeBuilder scopeBuilder(doc, &context);
+        ScopeBuilder scopeBuilder(&context, doc, snapshot);
         scopeBuilder.push(path);
     }
 
diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h
index faec7f58da6..d833fbb7491 100644
--- a/src/libs/qmljs/qmljsmodelmanagerinterface.h
+++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h
@@ -80,13 +80,39 @@ public:
         QStringList importPaths;
     };
 
+    class WorkingCopy
+    {
+    public:
+        typedef QHash<QString, QPair<QString, int> > Table;
+
+        void insert(const QString &fileName, const QString &source, int revision = 0)
+        { _elements.insert(fileName, qMakePair(source, revision)); }
+
+        bool contains(const QString &fileName) const
+        { return _elements.contains(fileName); }
+
+        QString source(const QString &fileName) const
+        { return _elements.value(fileName).first; }
+
+        QPair<QString, int> get(const QString &fileName) const
+        { return _elements.value(fileName); }
+
+        Table all() const
+        { return _elements; }
+
+    private:
+        Table _elements;
+    };
+
 public:
     ModelManagerInterface(QObject *parent = 0);
     virtual ~ModelManagerInterface();
 
     static ModelManagerInterface *instance();
 
+    virtual WorkingCopy workingCopy() const = 0;
     virtual QmlJS::Snapshot snapshot() const = 0;
+
     virtual void updateSourceFiles(const QStringList &files,
                                    bool emitDocumentOnDiskChanged) = 0;
     virtual void fileChangedOnDisk(const QString &path) = 0;
diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp
index ceffa41b4b4..d1c9374b1d3 100644
--- a/src/libs/qmljs/qmljsscopebuilder.cpp
+++ b/src/libs/qmljs/qmljsscopebuilder.cpp
@@ -38,10 +38,12 @@ using namespace QmlJS;
 using namespace QmlJS::Interpreter;
 using namespace QmlJS::AST;
 
-ScopeBuilder::ScopeBuilder(Document::Ptr doc, Interpreter::Context *context)
+ScopeBuilder::ScopeBuilder(Context *context, Document::Ptr doc, const Snapshot &snapshot)
     : _doc(doc)
+    , _snapshot(snapshot)
     , _context(context)
 {
+    initializeScopeChain();
 }
 
 ScopeBuilder::~ScopeBuilder()
@@ -92,6 +94,85 @@ void ScopeBuilder::pop()
     _context->scopeChain().update();
 }
 
+void ScopeBuilder::initializeScopeChain()
+{
+    ScopeChain &scopeChain = _context->scopeChain();
+    scopeChain = ScopeChain(); // reset
+
+    Interpreter::Engine *engine = _context->engine();
+
+    // ### TODO: This object ought to contain the global namespace additions by QML.
+    scopeChain.globalScope = engine->globalObject();
+
+    if (! _doc) {
+        scopeChain.update();
+        return;
+    }
+
+    Bind *bind = _doc->bind();
+    QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes;
+
+    ScopeChain::QmlComponentChain *chain = new ScopeChain::QmlComponentChain;
+    scopeChain.qmlComponentScope = QSharedPointer<const ScopeChain::QmlComponentChain>(chain);
+    if (_doc->qmlProgram()) {
+        componentScopes.insert(_doc.data(), chain);
+        makeComponentChain(_doc, chain, &componentScopes);
+
+        if (const TypeEnvironment *typeEnvironment = _context->typeEnvironment(_doc.data()))
+            scopeChain.qmlTypes = typeEnvironment;
+    } else {
+        // add scope chains for all components that import this file
+        foreach (Document::Ptr otherDoc, _snapshot) {
+            foreach (const ImportInfo &import, otherDoc->bind()->imports()) {
+                if (import.type() == ImportInfo::FileImport && _doc->fileName() == import.name()) {
+                    ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
+                    componentScopes.insert(otherDoc.data(), component);
+                    chain->instantiatingComponents += component;
+                    makeComponentChain(otherDoc, component, &componentScopes);
+                }
+            }
+        }
+
+        // ### TODO: Which type environment do scripts see?
+
+        if (bind->rootObjectValue())
+            scopeChain.jsScopes += bind->rootObjectValue();
+    }
+
+    scopeChain.update();
+}
+
+void ScopeBuilder::makeComponentChain(
+        Document::Ptr doc,
+        ScopeChain::QmlComponentChain *target,
+        QHash<Document *, ScopeChain::QmlComponentChain *> *components)
+{
+    if (!doc->qmlProgram())
+        return;
+
+    Bind *bind = doc->bind();
+
+    // add scopes for all components instantiating this one
+    foreach (Document::Ptr otherDoc, _snapshot) {
+        if (otherDoc == doc)
+            continue;
+        if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) {
+            if (components->contains(otherDoc.data())) {
+//                target->instantiatingComponents += components->value(otherDoc.data());
+            } else {
+                ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
+                components->insert(otherDoc.data(), component);
+                target->instantiatingComponents += component;
+
+                makeComponentChain(otherDoc, component, components);
+            }
+        }
+    }
+
+    // build this component scope
+    target->document = doc;
+}
+
 void ScopeBuilder::setQmlScopeObject(Node *node)
 {
     ScopeChain &scopeChain = _context->scopeChain();
diff --git a/src/libs/qmljs/qmljsscopebuilder.h b/src/libs/qmljs/qmljsscopebuilder.h
index 5c33a27393b..3fc44052a14 100644
--- a/src/libs/qmljs/qmljsscopebuilder.h
+++ b/src/libs/qmljs/qmljsscopebuilder.h
@@ -31,6 +31,7 @@
 #define QMLJSSCOPEBUILDER_H
 
 #include <qmljs/qmljsdocument.h>
+#include <qmljs/qmljsinterpreter.h>
 
 #include <QtCore/QList>
 
@@ -40,16 +41,10 @@ namespace AST {
     class Node;
 }
 
-namespace Interpreter {
-    class Context;
-    class Value;
-    class ObjectValue;
-}
-
 class QMLJS_EXPORT ScopeBuilder
 {
 public:
-    ScopeBuilder(Document::Ptr doc, Interpreter::Context *context);
+    ScopeBuilder(Interpreter::Context *context, Document::Ptr doc, const Snapshot &snapshot);
     ~ScopeBuilder();
 
     void push(AST::Node *node);
@@ -59,10 +54,15 @@ public:
     static const Interpreter::ObjectValue *isPropertyChangesObject(const Interpreter::Context *context, const Interpreter::ObjectValue *object);
 
 private:
+    void initializeScopeChain();
+    void makeComponentChain(Document::Ptr doc, Interpreter::ScopeChain::QmlComponentChain *target,
+                            QHash<Document *, Interpreter::ScopeChain::QmlComponentChain *> *components);
+
     void setQmlScopeObject(AST::Node *node);
     const Interpreter::Value *scopeObjectLookup(AST::UiQualifiedId *id);
 
     Document::Ptr _doc;
+    Snapshot _snapshot;
     Interpreter::Context *_context;
     QList<AST::Node *> _nodes;
 };
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 49c990d04b6..0627e0e6e88 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -205,7 +205,7 @@ public:
         , m_doc(doc)
         , m_context(new Interpreter::Context)
         , m_link(m_context, doc, snapshot, importPaths)
-        , m_scopeBuilder(doc, m_context)
+        , m_scopeBuilder(m_context, doc, snapshot)
     {
     }
 
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp
index de0f29823b4..c983f50ec8d 100644
--- a/src/plugins/qmljseditor/qmljseditor.cpp
+++ b/src/plugins/qmljseditor/qmljseditor.cpp
@@ -35,6 +35,7 @@
 #include "qmljseditorcodeformatter.h"
 #include "qmljsquickfix.h"
 #include "qmloutlinemodel.h"
+#include "qmljsfindreferences.h"
 
 #include <qmljs/qmljsbind.h>
 #include <qmljs/qmljscheck.h>
@@ -676,7 +677,8 @@ QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
     m_outlineModel(new QmlOutlineModel(this)),
     m_modelManager(0),
     m_contextPane(0),
-    m_updateSelectedElements(false)
+    m_updateSelectedElements(false),
+    m_findReferences(new FindReferences(this))
 {
     qRegisterMetaType<QmlJSEditor::Internal::SemanticInfo>("QmlJSEditor::Internal::SemanticInfo");
 
@@ -1434,6 +1436,11 @@ void QmlJSTextEditor::followSymbolUnderCursor()
     openLink(findLinkAt(textCursor()));
 }
 
+void QmlJSTextEditor::findUsages()
+{
+    m_findReferences->findUsages(file()->fileName(), textCursor().position());
+}
+
 void QmlJSTextEditor::showContextPane()
 {
     if (m_contextPane) {
diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h
index 1d7d57b82c9..8071dd03d80 100644
--- a/src/plugins/qmljseditor/qmljseditor.h
+++ b/src/plugins/qmljseditor/qmljseditor.h
@@ -63,6 +63,7 @@ namespace QmlJS {
  */
 namespace QmlJSEditor {
 class Highlighter;
+class FindReferences;
 
 namespace Internal {
 
@@ -244,6 +245,7 @@ public:
 
 public slots:
     void followSymbolUnderCursor();
+    void findUsages();
     void showContextPane();
     virtual void setFontSettings(const TextEditor::FontSettings &);
 
@@ -331,6 +333,8 @@ private:
     QmlJS::IContextPane *m_contextPane;
     int m_oldCursorPosition;
     bool m_updateSelectedElements;
+
+    FindReferences *m_findReferences;
 };
 
 } // namespace Internal
diff --git a/src/plugins/qmljseditor/qmljseditor.pro b/src/plugins/qmljseditor/qmljseditor.pro
index 976a8292819..8a6c725379c 100644
--- a/src/plugins/qmljseditor/qmljseditor.pro
+++ b/src/plugins/qmljseditor/qmljseditor.pro
@@ -31,7 +31,8 @@ HEADERS += \
     qmljsoutlinetreeview.h \
     quicktoolbarsettingspage.h \
     quicktoolbar.h \
-    qmljscomponentnamedialog.h
+    qmljscomponentnamedialog.h \
+    qmljsfindreferences.h
 
 SOURCES += \
     qmljscodecompletion.cpp \
@@ -56,7 +57,8 @@ SOURCES += \
     qmljsoutlinetreeview.cpp \
     quicktoolbarsettingspage.cpp \
     quicktoolbar.cpp \
-    qmljscomponentnamedialog.cpp
+    qmljscomponentnamedialog.cpp \
+    qmljsfindreferences.cpp
 
 RESOURCES += qmljseditor.qrc
 OTHER_FILES += QmlJSEditor.pluginspec QmlJSEditor.mimetypes.xml
diff --git a/src/plugins/qmljseditor/qmljseditorconstants.h b/src/plugins/qmljseditor/qmljseditorconstants.h
index e6f19b6112b..342f6e94acf 100644
--- a/src/plugins/qmljseditor/qmljseditorconstants.h
+++ b/src/plugins/qmljseditor/qmljseditorconstants.h
@@ -47,8 +47,10 @@ const char * const RUN_SEP = "QmlJSEditor.Run.Separator";
 const char * const C_QMLJSEDITOR_ID = "QMLProjectManager.QMLJSEditor";
 const char * const C_QMLJSEDITOR_DISPLAY_NAME = QT_TRANSLATE_NOOP("OpenWith::Editors", "QMLJS Editor");
 const char * const TASK_INDEX = "QmlJSEditor.TaskIndex";
+const char * const TASK_SEARCH = "QmlJSEditor.TaskSearch";
 
 const char * const FOLLOW_SYMBOL_UNDER_CURSOR = "QmlJSEditor.FollowSymbolUnderCursor";
+const char * const FIND_USAGES = "QmlJSEditor.FindUsages";
 const char * const SHOW_QT_QUICK_HELPER = "QmlJSEditor.ShowQtQuickHelper";
 
 const char * const QML_MIMETYPE = "application/x-qml";
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp
index 160d047cca1..e7cbfde8131 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.cpp
+++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp
@@ -164,6 +164,13 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
     contextMenu->addAction(cmd);
     qmlToolsMenu->addAction(cmd);
 
+    QAction *findUsagesAction = new QAction(tr("Find Usages"), this);
+    cmd = am->registerAction(findUsagesAction, Constants::FIND_USAGES, context);
+    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+U")));
+    connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(findUsages()));
+    contextMenu->addAction(cmd);
+    qmlToolsMenu->addAction(cmd);
+
     QAction *showQuickToolbar = new QAction(tr("Show Qt Quick Toolbar"), this);
     cmd = am->registerAction(showQuickToolbar, Constants::SHOW_QT_QUICK_HELPER, context);
     cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Space));
@@ -261,6 +268,13 @@ void QmlJSEditorPlugin::followSymbolUnderCursor()
         editor->followSymbolUnderCursor();
 }
 
+void QmlJSEditorPlugin::findUsages()
+{
+    Core::EditorManager *em = Core::EditorManager::instance();
+    if (QmlJSTextEditor *editor = qobject_cast<QmlJSTextEditor*>(em->currentEditor()->widget()))
+        editor->findUsages();
+}
+
 void QmlJSEditorPlugin::showContextPane()
 {
     Core::EditorManager *em = Core::EditorManager::instance();
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h
index 96702df75b8..b45c867dbc7 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.h
+++ b/src/plugins/qmljseditor/qmljseditorplugin.h
@@ -89,6 +89,7 @@ public:
 
 public Q_SLOTS:
     void followSymbolUnderCursor();
+    void findUsages();
     void showContextPane();
 
 private Q_SLOTS:
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp
new file mode 100644
index 00000000000..d3f9de96a00
--- /dev/null
+++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp
@@ -0,0 +1,685 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "qmljsfindreferences.h"
+
+#include <texteditor/basetexteditor.h>
+#include <texteditor/basefilefind.h>
+#include <find/searchresultwindow.h>
+#include <extensionsystem/pluginmanager.h>
+#include <utils/filesearch.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/progressmanager/futureprogress.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+
+#include <qmljs/qmljsmodelmanagerinterface.h>
+#include <qmljs/qmljsbind.h>
+#include <qmljs/qmljslink.h>
+#include <qmljs/qmljsevaluate.h>
+#include <qmljs/qmljsscopebuilder.h>
+#include <qmljs/parser/qmljsastvisitor_p.h>
+#include <qmljs/parser/qmljsast_p.h>
+
+#include "qmljseditorconstants.h"
+
+#include <QtCore/QTime>
+#include <QtCore/QTimer>
+#include <QtCore/QtConcurrentRun>
+#include <QtCore/QtConcurrentMap>
+#include <QtCore/QDir>
+#include <QtGui/QApplication>
+#include <qtconcurrent/runextensions.h>
+
+#include <functional>
+
+using namespace QmlJS;
+using namespace QmlJS::Interpreter;
+using namespace QmlJS::AST;
+using namespace QmlJSEditor;
+
+static const ObjectValue *prototypeWithMember(const Context *context, const ObjectValue *object, const QString &name)
+{
+    if (!object)
+        return 0;
+    const Value *value = object->property(name, context);
+    if (!value)
+        return 0;
+    forever {
+        const ObjectValue *prototype = object->prototype(context);
+        if (!prototype || prototype->property(name, context) != value)
+            return object;
+        object = prototype;
+    }
+}
+
+namespace {
+
+// ### These visitors could be useful in general
+
+class FindUsages: protected Visitor
+{
+public:
+    typedef QList<AST::SourceLocation> Result;
+
+    FindUsages(Document::Ptr doc, const Snapshot &snapshot, Context *context)
+        : _doc(doc)
+        , _snapshot(snapshot)
+        , _context(context)
+        , _builder(context, doc, snapshot)
+    {
+    }
+
+    Result operator()(const QString &name, const ObjectValue *scope)
+    {
+        _name = name;
+        _scope = scope;
+        _usages.clear();
+        if (_doc)
+            Node::accept(_doc->ast(), this);
+        return _usages;
+    }
+
+protected:
+    void accept(AST::Node *node)
+    { AST::Node::acceptChild(node, this); }
+
+    using Visitor::visit;
+
+    virtual bool visit(AST::UiPublicMember *node)
+    {
+        if (node->name
+                && node->name->asString() == _name
+                && _context->scopeChain().qmlScopeObjects.contains(_scope)) {
+            _usages.append(node->identifierToken);
+        }
+
+        return true;
+    }
+
+    virtual bool visit(AST::UiObjectDefinition *node)
+    {
+        _builder.push(node);
+        Node::accept(node->initializer, this);
+        _builder.pop();
+        return false;
+    }
+
+    virtual bool visit(AST::UiObjectBinding *node)
+    {
+        if (node->qualifiedId
+                && !node->qualifiedId->next
+                && node->qualifiedId->name->asString() == _name
+                && checkQmlScope()) {
+            _usages.append(node->qualifiedId->identifierToken);
+        }
+
+        _builder.push(node);
+        Node::accept(node->initializer, this);
+        _builder.pop();
+        return false;
+    }
+
+    virtual bool visit(AST::UiScriptBinding *node)
+    {
+        if (node->qualifiedId
+                && !node->qualifiedId->next
+                && node->qualifiedId->name->asString() == _name
+                && checkQmlScope()) {
+            _usages.append(node->qualifiedId->identifierToken);
+        }
+        return true;
+    }
+
+    virtual bool visit(AST::UiArrayBinding *node)
+    {
+        if (node->qualifiedId
+                && !node->qualifiedId->next
+                && node->qualifiedId->name->asString() == _name
+                && checkQmlScope()) {
+            _usages.append(node->qualifiedId->identifierToken);
+        }
+        return true;
+    }
+
+    virtual bool visit(AST::IdentifierExpression *node)
+    {
+        if (!node->name || node->name->asString() != _name)
+            return false;
+
+        const ObjectValue *scope;
+        _context->lookup(_name, &scope);
+        if (!scope)
+            return false;
+        if (check(scope)) {
+            _usages.append(node->identifierToken);
+            return false;
+        }
+
+        // the order of scopes in 'instantiatingComponents' is undefined,
+        // so it might still be a use - we just found a different value in a different scope first
+
+        // if scope is one of these, our match wasn't inside the instantiating components list
+        const ScopeChain &chain = _context->scopeChain();
+        if (chain.jsScopes.contains(scope)
+                || chain.qmlScopeObjects.contains(scope)
+                || chain.qmlTypes == scope
+                || chain.globalScope == scope)
+            return false;
+
+        if (contains(chain.qmlComponentScope.data()))
+            _usages.append(node->identifierToken);
+
+        return false;
+    }
+
+    virtual bool visit(AST::FieldMemberExpression *node)
+    {
+        if (!node->name || node->name->asString() != _name)
+            return true;
+
+        Evaluate evaluate(_context);
+        const Value *lhsValue = evaluate(node->base);
+        if (!lhsValue)
+            return true;
+
+        if (check(lhsValue->asObjectValue())) // passing null is ok
+            _usages.append(node->identifierToken);
+
+        return true;
+    }
+
+    virtual bool visit(AST::FunctionDeclaration *node)
+    {
+        if (node->name && node->name->asString() == _name) {
+            if (checkLookup())
+                _usages.append(node->identifierToken);
+        }
+        Node::accept(node->formals, this);
+        _builder.push(node);
+        Node::accept(node->body, this);
+        _builder.pop();
+        return false;
+    }
+
+    virtual bool visit(AST::VariableDeclaration *node)
+    {
+        if (node->name && node->name->asString() == _name) {
+            if (checkLookup())
+                _usages.append(node->identifierToken);
+        }
+        return true;
+    }
+
+private:
+    bool contains(const ScopeChain::QmlComponentChain *chain)
+    {
+        if (!chain || !chain->document)
+            return false;
+
+        if (chain->document->bind()->idEnvironment()->property(_name, _context))
+            return chain->document->bind()->idEnvironment() == _scope;
+        const ObjectValue *root = chain->document->bind()->rootObjectValue();
+        if (root->property(_name, _context)) {
+            return check(root);
+        }
+
+        foreach (const ScopeChain::QmlComponentChain *parent, chain->instantiatingComponents) {
+            if (contains(parent))
+                return true;
+        }
+        return false;
+    }
+
+    bool check(const ObjectValue *s)
+    {
+        if (!s)
+            return false;
+        return prototypeWithMember(_context, s, _name) == _scope;
+    }
+
+    bool checkQmlScope()
+    {
+        foreach (const ObjectValue *s, _context->scopeChain().qmlScopeObjects) {
+            if (check(s))
+                return true;
+        }
+        return false;
+    }
+
+    bool checkLookup()
+    {
+        const ObjectValue *scope = 0;
+        _context->lookup(_name, &scope);
+        return check(scope);
+    }
+
+    Result _usages;
+
+    Document::Ptr _doc;
+    Snapshot _snapshot;
+    Context *_context;
+    ScopeBuilder _builder;
+
+    QString _name;
+    const ObjectValue *_scope;
+};
+
+class ScopeAstPath: protected Visitor
+{
+public:
+    ScopeAstPath(Document::Ptr doc)
+        : _doc(doc)
+    {
+    }
+
+    QList<Node *> operator()(quint32 offset)
+    {
+        _result.clear();
+        _offset = offset;
+        if (_doc)
+            Node::accept(_doc->ast(), this);
+        return _result;
+    }
+
+protected:
+    void accept(AST::Node *node)
+    { AST::Node::acceptChild(node, this); }
+
+    using Visitor::visit;
+
+    virtual bool preVisit(Node *node)
+    {
+        if (Statement *stmt = node->statementCast()) {
+            return containsOffset(stmt->firstSourceLocation(), stmt->lastSourceLocation());
+        } else if (ExpressionNode *exp = node->expressionCast()) {
+            return containsOffset(exp->firstSourceLocation(), exp->lastSourceLocation());
+        } else if (UiObjectMember *ui = node->uiObjectMemberCast()) {
+            return containsOffset(ui->firstSourceLocation(), ui->lastSourceLocation());
+        }
+        return true;
+    }
+
+    virtual bool visit(AST::UiObjectDefinition *node)
+    {
+        _result.append(node);
+        Node::accept(node->initializer, this);
+        return false;
+    }
+
+    virtual bool visit(AST::UiObjectBinding *node)
+    {
+        _result.append(node);
+        Node::accept(node->initializer, this);
+        return false;
+    }
+
+    virtual bool visit(AST::FunctionDeclaration *node)
+    {
+        Node::accept(node->formals, this);
+        _result.append(node);
+        Node::accept(node->body, this);
+        return false;
+    }
+
+private:
+    bool containsOffset(SourceLocation start, SourceLocation end)
+    {
+        return _offset >= start.begin() && _offset <= end.end();
+    }
+
+    QList<Node *> _result;
+    Document::Ptr _doc;
+    quint32 _offset;
+};
+
+class FindTargetExpression: protected Visitor
+{
+public:
+    FindTargetExpression(Document::Ptr doc)
+        : _doc(doc)
+    {
+    }
+
+    QPair<Node *, QString> operator()(quint32 offset)
+    {
+        _result = qMakePair((Node *)0, QString());
+        _offset = offset;
+        if (_doc)
+            Node::accept(_doc->ast(), this);
+        return _result;
+    }
+
+protected:
+    void accept(AST::Node *node)
+    { AST::Node::acceptChild(node, this); }
+
+    using Visitor::visit;
+
+    virtual bool preVisit(Node *node)
+    {
+        if (Statement *stmt = node->statementCast()) {
+            return containsOffset(stmt->firstSourceLocation(), stmt->lastSourceLocation());
+        } else if (ExpressionNode *exp = node->expressionCast()) {
+            return containsOffset(exp->firstSourceLocation(), exp->lastSourceLocation());
+        } else if (UiObjectMember *ui = node->uiObjectMemberCast()) {
+            return containsOffset(ui->firstSourceLocation(), ui->lastSourceLocation());
+        }
+        return true;
+    }
+
+    virtual bool visit(IdentifierExpression *node)
+    {
+        if (containsOffset(node->identifierToken))
+            _result.second = node->name->asString();
+        return true;
+    }
+
+    virtual bool visit(FieldMemberExpression *node)
+    {
+        if (containsOffset(node->identifierToken)) {
+            _result.first = node->base;
+            _result.second = node->name->asString();
+            return false;
+        }
+        return true;
+    }
+
+    virtual bool visit(UiScriptBinding *node)
+    {
+        return !checkBindingName(node->qualifiedId);
+    }
+
+    virtual bool visit(UiArrayBinding *node)
+    {
+        return !checkBindingName(node->qualifiedId);
+    }
+
+    virtual bool visit(UiObjectBinding *node)
+    {
+        return !checkBindingName(node->qualifiedId);
+    }
+
+    virtual bool visit(UiPublicMember *node)
+    {
+        if (containsOffset(node->identifierToken)) {
+            _result.second = node->name->asString();
+            return false;
+        }
+        return true;
+    }
+
+    // ### Misses function declaration, var declaration
+
+    virtual bool visit(FunctionDeclaration *node)
+    {
+        if (containsOffset(node->identifierToken)) {
+            _result.second = node->name->asString();
+            return false;
+        }
+        return true;
+    }
+
+    virtual bool visit(VariableDeclaration *node)
+    {
+        if (containsOffset(node->identifierToken)) {
+            _result.second = node->name->asString();
+            return false;
+        }
+        return true;
+    }
+
+private:
+    bool containsOffset(SourceLocation start, SourceLocation end)
+    {
+        return _offset >= start.begin() && _offset <= end.end();
+    }
+
+    bool containsOffset(SourceLocation loc)
+    {
+        return _offset >= loc.begin() && _offset <= loc.end();
+    }
+
+    bool checkBindingName(UiQualifiedId *id)
+    {
+        if (id && !id->next && containsOffset(id->identifierToken)) {
+            _result.second = id->name->asString();
+            return true;
+        }
+        return false;
+    }
+
+    QPair<Node *, QString> _result;
+    Document::Ptr _doc;
+    quint32 _offset;
+};
+
+class ProcessFile: public std::unary_function<QString, QList<FindReferences::Usage> >
+{
+    const Snapshot &snapshot;
+    const Context &context;
+    typedef FindReferences::Usage Usage;
+    QString name;
+    const ObjectValue *scope;
+
+public:
+    ProcessFile(const Snapshot &snapshot,
+                const Context &context,
+                QString name,
+                const ObjectValue *scope)
+        : snapshot(snapshot), context(context), name(name), scope(scope)
+    { }
+
+    QList<Usage> operator()(const QString &fileName)
+    {
+        QList<Usage> usages;
+
+        Document::Ptr doc = snapshot.document(fileName);
+        if (!doc)
+            return usages;
+
+        Context contextCopy(context);
+
+        // find all idenfifier expressions, try to resolve them and check if the result is in scope
+        FindUsages findUsages(doc, snapshot, &contextCopy);
+        FindUsages::Result results = findUsages(name, scope);
+        foreach (AST::SourceLocation loc, results)
+            usages.append(Usage(fileName, matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length));
+
+        return usages;
+    }
+
+    static QString matchingLine(unsigned position, const QString &source)
+    {
+        int start = source.lastIndexOf(QLatin1Char('\n'), position);
+        start += 1;
+        int end = source.indexOf(QLatin1Char('\n'), position);
+
+        return source.mid(start, end - start);
+    }
+};
+
+class UpdateUI: public std::binary_function<QList<FindReferences::Usage> &, QList<FindReferences::Usage>, void>
+{
+    typedef FindReferences::Usage Usage;
+    QFutureInterface<Usage> *future;
+
+public:
+    UpdateUI(QFutureInterface<Usage> *future): future(future) {}
+
+    void operator()(QList<Usage> &, const QList<Usage> &usages)
+    {
+        foreach (const Usage &u, usages)
+            future->reportResult(u);
+
+        future->setProgressValue(future->progressValue() + 1);
+    }
+};
+
+} // end of anonymous namespace
+
+FindReferences::FindReferences(QObject *parent)
+    : QObject(parent)
+    , _resultWindow(Find::SearchResultWindow::instance())
+{
+    m_watcher.setPendingResultsLimit(1);
+    connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
+    connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
+}
+
+FindReferences::~FindReferences()
+{
+}
+
+static void find_helper(QFutureInterface<FindReferences::Usage> &future,
+                        const ModelManagerInterface::WorkingCopy workingCopy,
+                        Snapshot snapshot,
+                        const QString fileName,
+                        quint32 offset)
+{
+    // update snapshot from workingCopy to make sure it's up to date
+    // ### remove?
+    // ### this is a great candidate for map-reduce
+    QHashIterator< QString, QPair<QString, int> > it(workingCopy.all());
+    while (it.hasNext()) {
+        it.next();
+        Document::Ptr oldDoc = snapshot.document(it.key());
+        if (oldDoc && oldDoc->editorRevision() == it.value().second)
+            continue;
+
+        Document::Ptr newDoc = snapshot.documentFromSource(it.key(), it.value().first);
+        newDoc->parse();
+        snapshot.insert(newDoc);
+    }
+
+    // find the scope for the name we're searching
+    Context context;
+    Document::Ptr doc = snapshot.document(fileName);
+    if (!doc)
+        return;
+
+    Link link(&context, doc, snapshot, ModelManagerInterface::instance()->importPaths());
+    ScopeBuilder builder(&context, doc, snapshot);
+    ScopeAstPath astPath(doc);
+    builder.push(astPath(offset));
+
+    FindTargetExpression findTarget(doc);
+    QPair<Node *, QString> target = findTarget(offset);
+    const QString &name = target.second;
+    if (name.isEmpty())
+        return;
+
+    const ObjectValue *scope = 0;
+    if (target.first) {
+        Evaluate evaluate(&context);
+        const Value *v = evaluate(target.first);
+        if (v)
+            scope = v->asObjectValue();
+    } else {
+        context.lookup(name, &scope);
+    }
+    if (!scope)
+        return;
+    scope = prototypeWithMember(&context, scope, name);
+    if (!scope)
+        return;
+
+    QStringList files;
+    foreach (const Document::Ptr &doc, snapshot) {
+        // ### skip files that don't contain the name token
+        files.append(doc->fileName());
+    }
+
+    future.setProgressRange(0, files.size());
+
+    ProcessFile process(snapshot, context, name, scope);
+    UpdateUI reduce(&future);
+
+    QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
+
+    future.setProgressValue(files.size());
+}
+
+void FindReferences::findUsages(const QString &fileName, quint32 offset)
+{
+    Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly);
+
+    connect(search, SIGNAL(activated(Find::SearchResultItem)),
+            this, SLOT(openEditor(Find::SearchResultItem)));
+
+    findAll_helper(fileName, offset);
+}
+
+void FindReferences::findAll_helper(const QString &fileName, quint32 offset)
+{
+    _resultWindow->popup(true);
+
+    ModelManagerInterface *modelManager = ModelManagerInterface::instance();
+
+
+    QFuture<Usage> result = QtConcurrent::run(
+                &find_helper, modelManager->workingCopy(),
+                modelManager->snapshot(), fileName, offset);
+    m_watcher.setFuture(result);
+
+    Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
+    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
+                                                              QmlJSEditor::Constants::TASK_SEARCH);
+
+    connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup()));
+}
+
+void FindReferences::displayResults(int first, int last)
+{
+    for (int index = first; index != last; ++index) {
+        Usage result = m_watcher.future().resultAt(index);
+        _resultWindow->addResult(result.path,
+                                 result.line,
+                                 result.lineText,
+                                 result.col,
+                                 result.len);
+    }
+}
+
+void FindReferences::searchFinished()
+{
+    _resultWindow->finishSearch();
+    emit changed();
+}
+
+void FindReferences::openEditor(const Find::SearchResultItem &item)
+{
+    if (item.path.size() > 0) {
+        TextEditor::BaseTextEditor::openEditorAt(item.path.first(), item.lineNumber, item.textMarkPos,
+                                                 QString(),
+                                                 Core::EditorManager::ModeSwitch);
+    } else {
+        Core::EditorManager::instance()->openEditor(item.text, QString(), Core::EditorManager::ModeSwitch);
+    }
+}
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.h b/src/plugins/qmljseditor/qmljsfindreferences.h
new file mode 100644
index 00000000000..880cadb55a5
--- /dev/null
+++ b/src/plugins/qmljseditor/qmljsfindreferences.h
@@ -0,0 +1,97 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef QMLJSFINDREFERENCES_H
+#define QMLJSFINDREFERENCES_H
+
+#include <QtCore/QMutex>
+#include <QtCore/QObject>
+#include <QtCore/QPointer>
+#include <QtCore/QFuture>
+#include <QtCore/QFutureWatcher>
+#include <utils/filesearch.h>
+#include <qmljs/qmljsdocument.h>
+#include <qmljs/qmljslookupcontext.h>
+
+QT_FORWARD_DECLARE_CLASS(QTimer)
+
+namespace Find {
+    class SearchResultWindow;
+    struct SearchResultItem;
+} // end of namespace Find
+
+namespace QmlJSEditor {
+
+class FindReferences: public QObject
+{
+    Q_OBJECT
+public:
+    class Usage
+    {
+    public:
+        Usage()
+            : line(0), col(0), len(0) {}
+
+        Usage(const QString &path, const QString &lineText, int line, int col, int len)
+            : path(path), lineText(lineText), line(line), col(col), len(len) {}
+
+    public:
+        QString path;
+        QString lineText;
+        int line;
+        int col;
+        int len;
+    };
+
+public:
+    FindReferences(QObject *parent = 0);
+    virtual ~FindReferences();
+
+Q_SIGNALS:
+    void changed();
+
+public:
+    void findUsages(const QString &fileName, quint32 offset);
+
+private Q_SLOTS:
+    void displayResults(int first, int last);
+    void searchFinished();
+    void openEditor(const Find::SearchResultItem &item);
+
+private:
+    void findAll_helper(const QString &fileName, quint32 offset);
+
+private:
+    Find::SearchResultWindow *_resultWindow;
+    QFutureWatcher<Usage> m_watcher;
+};
+
+} // end of namespace QmlJSEditor
+
+#endif // QMLJSFINDREFERENCES_H
diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.cpp b/src/plugins/qmljseditor/qmljsmodelmanager.cpp
index 2dd2b256c2b..8a832bccfe3 100644
--- a/src/plugins/qmljseditor/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljseditor/qmljsmodelmanager.cpp
@@ -103,6 +103,24 @@ void ModelManager::loadQmlTypeDescriptions(const QString &resourcePath)
     //loadQmlPluginTypes(QString());
 }
 
+ModelManagerInterface::WorkingCopy ModelManager::workingCopy() const
+{
+    WorkingCopy workingCopy;
+    Core::EditorManager *editorManager = m_core->editorManager();
+
+    foreach (Core::IEditor *editor, editorManager->openedEditors()) {
+        const QString key = editor->file()->fileName();
+
+        if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) {
+            if (QmlJSTextEditor *ed = qobject_cast<QmlJSTextEditor *>(textEditor->widget())) {
+                workingCopy.insert(key, ed->toPlainText(), ed->document()->revision());
+            }
+        }
+    }
+
+    return workingCopy;
+}
+
 Snapshot ModelManager::snapshot() const
 {
     QMutexLocker locker(&m_mutex);
@@ -123,10 +141,8 @@ QFuture<void> ModelManager::refreshSourceFiles(const QStringList &sourceFiles,
         return QFuture<void>();
     }
 
-    const QMap<QString, WorkingCopy> workingCopy = buildWorkingCopyList();
-
     QFuture<void> result = QtConcurrent::run(&ModelManager::parse,
-                                              workingCopy, sourceFiles,
+                                              workingCopy(), sourceFiles,
                                               this,
                                               emitDocumentOnDiskChanged);
 
@@ -151,29 +167,10 @@ QFuture<void> ModelManager::refreshSourceFiles(const QStringList &sourceFiles,
     return result;
 }
 
-QMap<QString, ModelManager::WorkingCopy> ModelManager::buildWorkingCopyList()
-{
-    QMap<QString, WorkingCopy> workingCopy;
-    Core::EditorManager *editorManager = m_core->editorManager();
-
-    foreach (Core::IEditor *editor, editorManager->openedEditors()) {
-        const QString key = editor->file()->fileName();
-
-        if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) {
-            if (QmlJSTextEditor *ed = qobject_cast<QmlJSTextEditor *>(textEditor->widget())) {
-                workingCopy[key].contents = ed->toPlainText();
-                workingCopy[key].documentRevision = ed->document()->revision();
-            }
-        }
-    }
-
-    return workingCopy;
-}
-
 void ModelManager::fileChangedOnDisk(const QString &path)
 {
     QtConcurrent::run(&ModelManager::parse,
-                      buildWorkingCopyList(), QStringList() << path,
+                      workingCopy(), QStringList() << path,
                       this, true);
 }
 
@@ -340,7 +337,7 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap
 }
 
 void ModelManager::parse(QFutureInterface<void> &future,
-                            QMap<QString, WorkingCopy> workingCopy,
+                            WorkingCopy workingCopy,
                             QStringList files,
                             ModelManager *modelManager,
                             bool emitDocChangedOnDisk)
@@ -377,9 +374,9 @@ void ModelManager::parse(QFutureInterface<void> &future,
         int documentRevision = 0;
 
         if (workingCopy.contains(fileName)) {
-            WorkingCopy wc = workingCopy.value(fileName);
-            contents = wc.contents;
-            documentRevision = wc.documentRevision;
+            QPair<QString, int> entry = workingCopy.get(fileName);
+            contents = entry.first;
+            documentRevision = entry.second;
         } else {
             QFile inFile(fileName);
 
diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.h b/src/plugins/qmljseditor/qmljsmodelmanager.h
index 54449674ccc..79e9937e4d7 100644
--- a/src/plugins/qmljseditor/qmljsmodelmanager.h
+++ b/src/plugins/qmljseditor/qmljsmodelmanager.h
@@ -53,7 +53,9 @@ class ModelManager: public QmlJS::ModelManagerInterface
 public:
     ModelManager(QObject *parent = 0);
 
+    virtual WorkingCopy workingCopy() const;
     virtual QmlJS::Snapshot snapshot() const;
+
     virtual void updateSourceFiles(const QStringList &files,
                                    bool emitDocumentOnDiskChanged);
     virtual void fileChangedOnDisk(const QString &path);
@@ -84,19 +86,11 @@ private Q_SLOTS:
     void qmlPluginTypeDumpError(QProcess::ProcessError error);
 
 protected:
-    struct WorkingCopy
-    {
-        WorkingCopy(int revision = 0): documentRevision(revision) {}
-        int documentRevision;
-        QString contents;
-    };
-
     QFuture<void> refreshSourceFiles(const QStringList &sourceFiles,
                                      bool emitDocumentOnDiskChanged);
-    QMap<QString, WorkingCopy> buildWorkingCopyList();
 
     static void parse(QFutureInterface<void> &future,
-                      QMap<QString, WorkingCopy> workingCopy,
+                      WorkingCopy workingCopy,
                       QStringList files,
                       ModelManager *modelManager,
                       bool emitDocChangedOnDisk);
-- 
GitLab