From c3d1019deb5bb4126df0f3bf8fec63ba7fbfaa37 Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@nokia.com>
Date: Fri, 2 Oct 2009 10:49:33 +0200
Subject: [PATCH] Improved the navigation and code completion in the QML
 editor.

---
 src/plugins/qmleditor/idcollector.cpp         |  76 ++--
 src/plugins/qmleditor/idcollector.h           |  12 +-
 src/plugins/qmleditor/qmlcodecompletion.cpp   |  76 ++--
 src/plugins/qmleditor/qmlcodecompletion.h     |   6 +-
 src/plugins/qmleditor/qmldocument.cpp         |  25 +-
 src/plugins/qmleditor/qmldocument.h           |   5 +
 src/plugins/qmleditor/qmleditor.cpp           |  22 +-
 src/plugins/qmleditor/qmleditor.pro           |   8 +-
 src/plugins/qmleditor/qmleditorplugin.cpp     |   2 +-
 .../qmleditor/qmlexpressionundercursor.cpp    | 332 +++++++++++++-----
 .../qmleditor/qmlexpressionundercursor.h      |  38 +-
 src/plugins/qmleditor/qmllookupcontext.cpp    | 136 ++++---
 src/plugins/qmleditor/qmllookupcontext.h      |  13 +-
 .../qmleditor/qmlresolveexpression.cpp        |  88 ++---
 src/plugins/qmleditor/qmlresolveexpression.h  |  11 +-
 src/plugins/qmleditor/qmlscope.cpp            |  35 ++
 src/plugins/qmleditor/qmlscope.h              |  40 +++
 src/plugins/qmleditor/qmlsymbol.cpp           | 134 ++++++-
 src/plugins/qmleditor/qmlsymbol.h             |  65 +++-
 19 files changed, 787 insertions(+), 337 deletions(-)
 create mode 100644 src/plugins/qmleditor/qmlscope.cpp
 create mode 100644 src/plugins/qmleditor/qmlscope.h

diff --git a/src/plugins/qmleditor/idcollector.cpp b/src/plugins/qmleditor/idcollector.cpp
index 8677d540fdb..c98019587f3 100644
--- a/src/plugins/qmleditor/idcollector.cpp
+++ b/src/plugins/qmleditor/idcollector.cpp
@@ -1,3 +1,5 @@
+#include <QDebug>
+
 #include "idcollector.h"
 #include "qmljsast_p.h"
 #include "qmljsengine_p.h"
@@ -7,36 +9,39 @@ using namespace QmlJS::AST;
 using namespace QmlEditor;
 using namespace QmlEditor::Internal;
 
-QMap<QString, QmlIdSymbol*> IdCollector::operator()(const QString &fileName, QmlJS::AST::UiProgram *ast)
+QMap<QString, QmlIdSymbol*> IdCollector::operator()(QmlDocument *doc)
 {
-    _fileName = fileName;
+    _doc = doc;
     _ids.clear();
+    _currentSymbol = 0;
 
-    Node::accept(ast, this);
+    Node::accept(doc->program(), this);
 
     return _ids;
 }
 
-bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast)
-{
-    _scopes.push(ast);
-    return true;
-}
-
-bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast)
+bool IdCollector::visit(UiArrayBinding *ast)
 {
-    _scopes.push(ast);
-    return true;
+    QmlSymbolFromFile *oldSymbol = switchSymbol(ast);
+    Node::accept(ast->members, this);
+    _currentSymbol = oldSymbol;
+    return false;
 }
 
-void IdCollector::endVisit(QmlJS::AST::UiObjectBinding *)
+bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast)
 {
-    _scopes.pop();
+    QmlSymbolFromFile *oldSymbol = switchSymbol(ast);
+    Node::accept(ast->initializer, this);
+    _currentSymbol = oldSymbol;
+    return false;
 }
 
-void IdCollector::endVisit(QmlJS::AST::UiObjectDefinition *)
+bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast)
 {
-    _scopes.pop();
+    QmlSymbolFromFile *oldSymbol = switchSymbol(ast);
+    Node::accept(ast->initializer, this);
+    _currentSymbol = oldSymbol;
+    return false;
 }
 
 bool IdCollector::visit(QmlJS::AST::UiScriptBinding *ast)
@@ -44,21 +49,40 @@ bool IdCollector::visit(QmlJS::AST::UiScriptBinding *ast)
     if (!(ast->qualifiedId->next) && ast->qualifiedId->name->asString() == "id")
         if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement))
             if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression))
-                addId(i->name->asString(), ast);
+                if (i->name)
+                    addId(i->name->asString(), ast);
 
     return false;
 }
 
+QmlSymbolFromFile *IdCollector::switchSymbol(QmlJS::AST::UiObjectMember *node)
+{
+    QmlSymbolFromFile *newSymbol = 0;
+
+    if (_currentSymbol == 0) {
+        newSymbol = _doc->findSymbol(node);
+    } else {
+        newSymbol = _currentSymbol->findMember(node);
+    }
+
+    QmlSymbolFromFile *oldSymbol = _currentSymbol;
+
+    if (newSymbol) {
+        _currentSymbol = newSymbol;
+    } else {
+        QString filename = _doc->fileName();
+        qWarning() << "Scope without symbol @"<<filename<<":"<<node->firstSourceLocation().startLine<<":"<<node->firstSourceLocation().startColumn;
+    }
+
+    return oldSymbol;
+}
+
 void IdCollector::addId(const QString &id, QmlJS::AST::UiScriptBinding *ast)
 {
-    if (!_ids.contains(id)) {
-        Node *parent = _scopes.top();
-
-        if (UiObjectBinding *binding = cast<UiObjectBinding*>(parent))
-            _ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, binding));
-        else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(parent))
-            _ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, definition));
-        else
-            Q_ASSERT(!"Unknown parent for id");
+    if (!_ids.contains(id) && _currentSymbol) {
+        QmlSymbolFromFile *symbol = _currentSymbol->findMember(ast);
+
+        if (QmlIdSymbol *idSymbol = symbol->asIdSymbol())
+            _ids[id] = idSymbol;
     }
 }
diff --git a/src/plugins/qmleditor/idcollector.h b/src/plugins/qmleditor/idcollector.h
index e4c7e7bf292..8a02b4c355f 100644
--- a/src/plugins/qmleditor/idcollector.h
+++ b/src/plugins/qmleditor/idcollector.h
@@ -6,6 +6,7 @@
 #include <QStack>
 #include <QString>
 
+#include "qmldocument.h"
 #include "qmljsastvisitor_p.h"
 #include "qmlsymbol.h"
 
@@ -15,23 +16,22 @@ namespace Internal {
 class IdCollector: protected QmlJS::AST::Visitor
 {
 public:
-    QMap<QString, QmlIdSymbol*> operator()(const QString &fileName, QmlJS::AST::UiProgram *ast);
+    QMap<QString, QmlIdSymbol*> operator()(QmlDocument *doc);
 
 protected:
+    virtual bool visit(QmlJS::AST::UiArrayBinding *ast);
     virtual bool visit(QmlJS::AST::UiObjectBinding *ast);
     virtual bool visit(QmlJS::AST::UiObjectDefinition *ast);
     virtual bool visit(QmlJS::AST::UiScriptBinding *ast);
 
-    virtual void endVisit(QmlJS::AST::UiObjectBinding *);
-    virtual void endVisit(QmlJS::AST::UiObjectDefinition *);
-
 private:
+    QmlSymbolFromFile *switchSymbol(QmlJS::AST::UiObjectMember *node);
     void addId(const QString &id, QmlJS::AST::UiScriptBinding *ast);
 
 private:
-    QString _fileName;
+    QmlDocument *_doc;
     QMap<QString, QmlIdSymbol*> _ids;
-    QStack<QmlJS::AST::Node *> _scopes;
+    QmlSymbolFromFile *_currentSymbol;
 };
 
 } // namespace Internal
diff --git a/src/plugins/qmleditor/qmlcodecompletion.cpp b/src/plugins/qmleditor/qmlcodecompletion.cpp
index 9626d213195..53351dda406 100644
--- a/src/plugins/qmleditor/qmlcodecompletion.cpp
+++ b/src/plugins/qmleditor/qmlcodecompletion.cpp
@@ -1,17 +1,25 @@
-#include "qmlcompletionvisitor.h"
 #include "qmlcodecompletion.h"
 #include "qmleditor.h"
+#include "qmlmodelmanagerinterface.h"
+#include "qmlexpressionundercursor.h"
+#include "qmllookupcontext.h"
+#include "qmlresolveexpression.h"
+#include "qmlsymbol.h"
+
 #include <texteditor/basetexteditor.h>
 #include <QtDebug>
 
 using namespace QmlEditor::Internal;
 
-QmlCodeCompletion::QmlCodeCompletion(QObject *parent)
+QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager,QObject *parent)
     : TextEditor::ICompletionCollector(parent),
+      m_modelManager(modelManager),
       m_editor(0),
       m_startPosition(0),
       m_caseSensitivity(Qt::CaseSensitive)
-{ }
+{
+    Q_ASSERT(modelManager);
+}
 
 QmlCodeCompletion::~QmlCodeCompletion()
 { }
@@ -49,26 +57,52 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     m_startPosition = pos;
     m_completions.clear();
 
-    foreach (const QString &word, edit->keywords()) {
-        TextEditor::CompletionItem item(this);
-        item.m_text = word;
-        m_completions.append(item);
-    }
-
-    foreach (const QString &word, edit->words()) {
-        TextEditor::CompletionItem item(this);
-        item.m_text = word;
-        m_completions.append(item);
-    }
+//    foreach (const QString &word, edit->keywords()) {
+//        TextEditor::CompletionItem item(this);
+//        item.m_text = word;
+//        m_completions.append(item);
+//    }
+//
+//    foreach (const QString &word, edit->words()) {
+//        TextEditor::CompletionItem item(this);
+//        item.m_text = word;
+//        m_completions.append(item);
+//    }
 
     QmlDocument::Ptr qmlDocument = edit->qmlDocument();
-    if (!qmlDocument.isNull()) {
-        QmlJS::AST::UiProgram *program = qmlDocument->program();
-
-        if (program) {
-            QmlCompletionVisitor visitor;
-
-            foreach (const QString &word, visitor(program, m_startPosition)) {
+    qDebug() << "*** document:" << qmlDocument;
+    if (qmlDocument.isNull())
+        return pos;
+
+    if (!qmlDocument->program())
+        qmlDocument = m_modelManager->snapshot().value(qmlDocument->fileName());
+
+    if (qmlDocument->program()) {
+         QmlJS::AST::UiProgram *program = qmlDocument->program();
+        qDebug() << "*** program:" << program;
+ 
+         if (program) {
+            QmlExpressionUnderCursor expressionUnderCursor;
+            QTextCursor cursor(edit->document());
+            cursor.setPosition(pos);
+            expressionUnderCursor(cursor, qmlDocument);
+
+            QmlLookupContext context(expressionUnderCursor.expressionScopes(), qmlDocument, m_modelManager->snapshot());
+            QmlResolveExpression resolver(context);
+            QList<QmlSymbol*> symbols = resolver.visibleSymbols(expressionUnderCursor.expressionNode());
+
+            foreach (QmlSymbol *symbol, symbols) {
+                QString word;
+
+                if (symbol->isIdSymbol()) {
+                    word = symbol->asIdSymbol()->id();
+                } else {
+                    word = symbol->name();
+                }
+
+                if (word.isEmpty())
+                    continue;
+ 
                 TextEditor::CompletionItem item(this);
                 item.m_text = word;
                 m_completions.append(item);
diff --git a/src/plugins/qmleditor/qmlcodecompletion.h b/src/plugins/qmleditor/qmlcodecompletion.h
index fd5e4b1ba62..8ed9aaf05a2 100644
--- a/src/plugins/qmleditor/qmlcodecompletion.h
+++ b/src/plugins/qmleditor/qmlcodecompletion.h
@@ -8,6 +8,9 @@ class ITextEditable;
 }
 
 namespace QmlEditor {
+
+class QmlModelManagerInterface;
+
 namespace Internal {
 
 class QmlCodeCompletion: public TextEditor::ICompletionCollector
@@ -15,7 +18,7 @@ class QmlCodeCompletion: public TextEditor::ICompletionCollector
     Q_OBJECT
 
 public:
-    QmlCodeCompletion(QObject *parent = 0);
+    QmlCodeCompletion(QmlModelManagerInterface *modelManager, QObject *parent = 0);
     virtual ~QmlCodeCompletion();
 
     Qt::CaseSensitivity caseSensitivity() const;
@@ -30,6 +33,7 @@ public:
     virtual void cleanup();
 
 private:
+    QmlModelManagerInterface *m_modelManager;
     TextEditor::ITextEditable *m_editor;
     int m_startPosition;
     QList<TextEditor::CompletionItem> m_completions;
diff --git a/src/plugins/qmleditor/qmldocument.cpp b/src/plugins/qmleditor/qmldocument.cpp
index d89e69609f5..d6baba6f821 100644
--- a/src/plugins/qmleditor/qmldocument.cpp
+++ b/src/plugins/qmleditor/qmldocument.cpp
@@ -61,7 +61,7 @@ QmlDocument::~QmlDocument()
     if (_pool)
         delete _pool;
 
-    qDeleteAll(_ids.values());
+    qDeleteAll(_symbols);
 }
 
 QmlDocument::Ptr QmlDocument::create(const QString &fileName)
@@ -109,14 +109,28 @@ bool QmlDocument::parse()
     _program = parser.ast();
     _diagnosticMessages = parser.diagnosticMessages();
 
-    if (_parsedCorrectly && _program) {
-        Internal::IdCollector collect;
-        _ids = collect(_fileName, _program);
+    if (_program) {
+        for (QmlJS::AST::UiObjectMemberList *iter = _program->members; iter; iter = iter->next)
+            if (iter->member)
+                _symbols.append(new QmlSymbolFromFile(_fileName, iter->member));
+
+         Internal::IdCollector collect;
+        _ids = collect(this);
     }
 
     return _parsedCorrectly;
 }
 
+QmlSymbolFromFile *QmlDocument::findSymbol(QmlJS::AST::Node *node) const
+{
+    foreach (QmlSymbol *symbol, _symbols)
+        if (symbol->isSymbolFromFile())
+            if (symbol->asSymbolFromFile()->node() == node)
+                return symbol->asSymbolFromFile();
+
+    return 0;
+}
+
 Snapshot::Snapshot()
 {
 }
@@ -127,6 +141,9 @@ Snapshot::~Snapshot()
 
 void Snapshot::insert(const QmlDocument::Ptr &document)
 {
+    if (!document || !document->program())
+        return;
+
     QMap<QString, QmlDocument::Ptr>::insert(document->fileName(), document);
 }
 
diff --git a/src/plugins/qmleditor/qmldocument.h b/src/plugins/qmleditor/qmldocument.h
index 7639d1b25ac..be6a3658eb7 100644
--- a/src/plugins/qmleditor/qmldocument.h
+++ b/src/plugins/qmleditor/qmldocument.h
@@ -74,6 +74,10 @@ public:
     QString path() const { return _path; }
     QString componentName() const { return _componentName; }
 
+    QmlSymbolFromFile *findSymbol(QmlJS::AST::Node *node) const;
+    QmlSymbol::List symbols() const
+    { return _symbols; }
+
 private:
     QmlJS::Engine *_engine;
     QmlJS::NodePool *_pool;
@@ -85,6 +89,7 @@ private:
     QString _source;
     bool _parsedCorrectly;
     IdTable _ids;
+    QmlSymbol::List _symbols;
 };
 
 class QMLEDITOR_EXPORT Snapshot: public QMap<QString, QmlDocument::Ptr>
diff --git a/src/plugins/qmleditor/qmleditor.cpp b/src/plugins/qmleditor/qmleditor.cpp
index 2d68189a7a3..48e3b7c8ce3 100644
--- a/src/plugins/qmleditor/qmleditor.cpp
+++ b/src/plugins/qmleditor/qmleditor.cpp
@@ -719,12 +719,28 @@ TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cur
     if (!doc)
         return link;
 
+    QTextCursor expressionCursor(cursor);
+    {
+        // correct the position by moving to the end of an identifier (if we're hovering over one):
+        const QString txt = cursor.block().text();
+        int pos = cursor.position();
+        forever {
+            const QChar ch = characterAt(pos);
+
+            if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
+                ++pos;
+            else
+                break;
+        }
+        expressionCursor.setPosition(pos);
+    }
+
     QmlExpressionUnderCursor expressionUnderCursor;
-    expressionUnderCursor(cursor, doc->program());
+    expressionUnderCursor(expressionCursor, doc);
 
     QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, snapshot);
-    QmlResolveExpression resolve(context);
-    QmlSymbol *symbol = resolve(expressionUnderCursor.expressionNode());
+    QmlResolveExpression resolver(context);
+    QmlSymbol *symbol = resolver.typeOf(expressionUnderCursor.expressionNode());
 
     if (!symbol)
         return link;
diff --git a/src/plugins/qmleditor/qmleditor.pro b/src/plugins/qmleditor/qmleditor.pro
index 1399b679e1e..0f222998009 100644
--- a/src/plugins/qmleditor/qmleditor.pro
+++ b/src/plugins/qmleditor/qmleditor.pro
@@ -17,7 +17,6 @@ HEADERS += qmleditor.h \
     qmleditorconstants.h \
     qmlhoverhandler.h \
     qmldocument.h \
-    qmlcompletionvisitor.h \
     qmlmodelmanagerinterface.h \
     qmleditor_global.h \
     qmlmodelmanager.h \
@@ -27,7 +26,8 @@ HEADERS += qmleditor.h \
     qmllookupcontext.h \
     qmlresolveexpression.h \
     qmlsymbol.h \
-    qmlfilewizard.h
+    qmlfilewizard.h \
+    qmlscope.h
 SOURCES += qmleditor.cpp \
     qmleditorfactory.cpp \
     qmleditorplugin.cpp \
@@ -36,7 +36,6 @@ SOURCES += qmleditor.cpp \
     qmlcodecompletion.cpp \
     qmlhoverhandler.cpp \
     qmldocument.cpp \
-    qmlcompletionvisitor.cpp \
     qmlmodelmanagerinterface.cpp \
     qmlmodelmanager.cpp \
     qmlcodeformatter.cpp \
@@ -45,5 +44,6 @@ SOURCES += qmleditor.cpp \
     qmllookupcontext.cpp \
     qmlresolveexpression.cpp \
     qmlsymbol.cpp \
-    qmlfilewizard.cpp
+    qmlfilewizard.cpp \
+    qmlscope.cpp
 RESOURCES += qmleditor.qrc
diff --git a/src/plugins/qmleditor/qmleditorplugin.cpp b/src/plugins/qmleditor/qmleditorplugin.cpp
index 474268a6946..466fef432cb 100644
--- a/src/plugins/qmleditor/qmleditorplugin.cpp
+++ b/src/plugins/qmleditor/qmleditorplugin.cpp
@@ -111,7 +111,7 @@ bool QmlEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err
         | TextEditor::TextEditorActionHandler::UnCollapseAll);
     m_actionHandler->initializeActions();
 
-    m_completion = new QmlCodeCompletion();
+    m_completion = new QmlCodeCompletion(m_modelManager);
     addAutoReleasedObject(m_completion);
 
     addAutoReleasedObject(new QmlHoverHandler());
diff --git a/src/plugins/qmleditor/qmlexpressionundercursor.cpp b/src/plugins/qmleditor/qmlexpressionundercursor.cpp
index 133ab2bac6f..d834b2318f7 100644
--- a/src/plugins/qmleditor/qmlexpressionundercursor.cpp
+++ b/src/plugins/qmleditor/qmlexpressionundercursor.cpp
@@ -1,124 +1,294 @@
+#include <QDebug>
+
 #include "qmljsast_p.h"
+#include "qmljsastvisitor_p.h"
 #include "qmljsengine_p.h"
-
+#include "qmljslexer_p.h"
+#include "qmljsnodepool_p.h"
+#include "qmljsparser_p.h"
 #include "qmlexpressionundercursor.h"
 
-using namespace QmlEditor;
-using namespace QmlEditor::Internal;
 using namespace QmlJS;
 using namespace QmlJS::AST;
 
+namespace QmlEditor {
+    namespace Internal {
+        class PositionCalculator: protected Visitor
+        {
+        public:
+            Node *operator()(Node *ast, int pos)
+            {
+                _pos = pos;
+                _expression = 0;
+                _offset = -1;
+                _length = -1;
+
+                Node::accept(ast, this);
+
+                return _expression;
+            }
+
+            int offset() const
+            { return _offset; }
+
+            int length() const
+            { return _length; }
+
+        protected:
+            bool visit(FieldMemberExpression *ast)
+            {
+                if (ast->identifierToken.offset <= _pos && _pos <= ast->identifierToken.end()) {
+                    _expression = ast;
+                    _offset = ast->identifierToken.offset;
+                    _length = ast->identifierToken.length;
+                }
+
+                return true;
+            }
+
+            bool visit(IdentifierExpression *ast)
+            {
+                if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) {
+                    _expression = ast;
+                    _offset = ast->firstSourceLocation().offset;
+                    _length = ast->lastSourceLocation().end() - _offset;
+                }
+
+                return false;
+            }
+
+            bool visit(UiImport * /*ast*/)
+            {
+                return false;
+            }
+
+            bool visit(UiQualifiedId *ast)
+            {
+                if (ast->identifierToken.offset <= _pos) {
+                    for (UiQualifiedId *iter = ast; iter; iter = iter->next) {
+                        if (_pos <= iter->identifierToken.end()) {
+                            // found it
+                            _expression = ast;
+                            _offset = ast->identifierToken.offset;
+
+                            for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) {
+                                _length = iter2->identifierToken.end() - _offset;
+                            }
+
+                            break;
+                        }
+                    }
+                }
+
+                return false;
+            }
+
+        private:
+            quint32 _pos;
+            Node *_expression;
+            int _offset;
+            int _length;
+        };
+
+        class ScopeCalculator: protected Visitor
+        {
+        public:
+            QStack<QmlSymbol*> operator()(const QmlDocument::Ptr &doc, int pos)
+            {
+                _doc = doc;
+                _pos = pos;
+                _scopes.clear();
+                _currentSymbol = 0;
+                Node::accept(doc->program(), this);
+                return _scopes;
+            }
+
+        protected:
+            virtual bool visit(Block *ast)
+            {
+                // TODO
+//                if (_pos > ast->lbraceToken.end() && _pos < ast->rbraceToken.offset) {
+//                    push(ast);
+//                    Node::accept(ast->statements, this);
+//                }
+
+                return false;
+            }
+
+            virtual bool visit(UiObjectBinding *ast)
+            {
+                if (ast->initializer && ast->initializer->rbraceToken.offset < _pos && _pos <= ast->initializer->lbraceToken.end()) {
+                    push(ast);
+                    Node::accept(ast->initializer, this);
+                }
+
+                return false;
+            }
+
+            virtual bool visit(UiObjectDefinition *ast)
+            {
+                if (ast->initializer && ast->initializer->rbraceToken.offset < _pos && _pos <= ast->initializer->lbraceToken.end()) {
+                    push(ast);
+                    Node::accept(ast->initializer, this);
+                }
+
+                return false;
+            }
+
+            virtual bool visit(UiArrayBinding *ast)
+            {
+                if (ast->lbracketToken.offset < _pos && _pos <= ast->rbracketToken.end()) {
+                    push(ast);
+                    Node::accept(ast->members, this);
+                }
+
+                return false;
+            }
+
+        private:
+            void push(Node *node)
+            {
+                QmlSymbolFromFile* symbol;
+
+                if (_currentSymbol) {
+                    symbol = _currentSymbol->findMember(node);
+                } else {
+                    symbol = _doc->findSymbol(node);
+                }
+
+                if (symbol) {
+                    _currentSymbol = symbol;
+
+                    if (!cast<UiArrayBinding*>(node))
+                        _scopes.push(symbol);
+                }
+            }
+
+        private:
+            QmlDocument::Ptr _doc;
+            quint32 _pos;
+            QStack<QmlSymbol*> _scopes;
+            QmlSymbolFromFile* _currentSymbol;
+        };
+    }
+}
+
+using namespace QmlEditor;
+using namespace QmlEditor::Internal;
+
 QmlExpressionUnderCursor::QmlExpressionUnderCursor()
     : _expressionNode(0),
-      _pos(0)
+      _pos(0), _engine(0), _nodePool(0)
+{
+}
+
+QmlExpressionUnderCursor::~QmlExpressionUnderCursor()
 {
+    if (_engine) { delete _engine; _engine = 0; }
+    if (_nodePool) { delete _nodePool; _nodePool = 0; }
 }
 
 void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor,
-                                          QmlJS::AST::UiProgram *program)
+                                          const QmlDocument::Ptr &doc)
 {
+    if (_engine) { delete _engine; _engine = 0; }
+    if (_nodePool) { delete _nodePool; _nodePool = 0; }
+
     _pos = cursor.position();
     _expressionNode = 0;
     _expressionOffset = -1;
     _expressionLength = -1;
-    _scopes.clear();
+    _expressionScopes.clear();
 
-    if (program)
-        program->accept(this);
-}
-
-bool QmlExpressionUnderCursor::visit(QmlJS::AST::Block *ast)
-{
-    _scopes.push(ast);
-
-    return true;
-}
+    const QTextBlock block = cursor.block();
+    parseExpression(block);
 
-void QmlExpressionUnderCursor::endVisit(QmlJS::AST::Block *)
-{
-    _scopes.pop();
-}
-
-bool QmlExpressionUnderCursor::visit(QmlJS::AST::FieldMemberExpression *ast)
-{
-    if (ast->identifierToken.offset <= _pos && _pos <= ast->identifierToken.end()) {
-        _expressionNode = ast;
-        _expressionOffset = ast->identifierToken.offset;
-        _expressionLength = ast->identifierToken.length;
-        _expressionScopes = _scopes;
+    if (_expressionOffset != -1) {
+        ScopeCalculator calculator;
+        _expressionScopes = calculator(doc, _expressionOffset);
     }
-
-    return true;
 }
 
-bool QmlExpressionUnderCursor::visit(QmlJS::AST::IdentifierExpression *ast)
+void QmlExpressionUnderCursor::parseExpression(const QTextBlock &block)
 {
-    if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) {
-        _expressionNode = ast;
-        _expressionOffset = ast->firstSourceLocation().offset;
-        _expressionLength = ast->lastSourceLocation().end() - _expressionOffset;
-        _expressionScopes = _scopes;
+    int textPosition = _pos - block.position();
+    const QString blockText = block.text();
+    if (textPosition > 0) {
+        if (blockText.at(textPosition - 1) == QLatin1Char('.'))
+            --textPosition;
+    } else {
+        textPosition = 0;
     }
 
-    return false;
-}
+    const QString text = blockText.left(textPosition);
 
-bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiObjectBinding *ast)
-{
-    Node::accept(ast->qualifiedId, this);
-    Node::accept(ast->qualifiedTypeNameId, this);
-
-    _scopes.push(ast);
+    Node *node = 0;
 
-    Node::accept(ast->initializer, this);
+    if (UiObjectMember *binding = tryBinding(text)) {
+        qDebug() << "**** binding";
+        node = binding;
+    } else if (Statement *stmt = tryStatement(text)) {
+        qDebug() << "**** statement";
+        node = stmt;
+    } else if (ExpressionNode *expr = tryExpression(text)) {
+        qDebug() << "**** expression";
+        node = expr;
+    } else {
+        qDebug() << "**** none";
+    }
 
-    return false;
+    if (node) {
+        PositionCalculator calculator;
+        _expressionNode = calculator(node, textPosition);
+        _expressionOffset = calculator.offset() + block.position();
+        _expressionLength = calculator.length();
+    }
 }
 
-void QmlExpressionUnderCursor::endVisit(QmlJS::AST::UiObjectBinding *)
+ExpressionNode *QmlExpressionUnderCursor::tryExpression(const QString &text)
 {
-    _scopes.pop();
+    _engine = new Engine();
+    _nodePool = new NodePool("", _engine);
+    Lexer lexer(_engine);
+    Parser parser(_engine);
+    lexer.setCode(text, /*line = */ 1);
+
+    if (parser.parseExpression())
+        return parser.expression();
+    else
+        return 0;
 }
 
-bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiObjectDefinition *ast)
+Statement *QmlExpressionUnderCursor::tryStatement(const QString &text)
 {
-    Node::accept(ast->qualifiedTypeNameId, this);
+    _engine = new Engine();
+    _nodePool = new NodePool("", _engine);
+    Lexer lexer(_engine);
+    Parser parser(_engine);
+    lexer.setCode(text, /*line = */ 1);
 
-    _scopes.push(ast);
-
-    Node::accept(ast->initializer, this);
-
-    return false;
+    if (parser.parseStatement())
+        return parser.statement();
+    else
+        return 0;
 }
 
-void QmlExpressionUnderCursor::endVisit(QmlJS::AST::UiObjectDefinition *)
+UiObjectMember *QmlExpressionUnderCursor::tryBinding(const QString &text)
 {
-    _scopes.pop();
-}
+    _engine = new Engine();
+    _nodePool = new NodePool("", _engine);
+    Lexer lexer(_engine);
+    Parser parser(_engine);
+    lexer.setCode(text, /*line = */ 1);
 
-bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiQualifiedId *ast)
-{
-    if (ast->identifierToken.offset <= _pos) {
-        for (UiQualifiedId *iter = ast; iter; iter = iter->next) {
-            if (_pos <= iter->identifierToken.end()) {
-                // found it
-                _expressionNode = ast;
-                _expressionOffset = ast->identifierToken.offset;
-
-                for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) {
-                    _expressionLength = iter2->identifierToken.end() - _expressionOffset;
-                }
+    if (parser.parseUiObjectMember()) {
+        UiObjectMember *member = parser.uiObjectMember();
 
-                _expressionScopes = _scopes;
-                break;
-            }
-        }
+        if (cast<UiObjectBinding*>(member) || cast<UiArrayBinding*>(member) || cast<UiScriptBinding*>(member))
+            return member;
+        else
+            return 0;
+    } else {
+        return 0;
     }
-
-    return false;
-}
-
-bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiImport * /*ast*/)
-{
-    return false;
 }
diff --git a/src/plugins/qmleditor/qmlexpressionundercursor.h b/src/plugins/qmleditor/qmlexpressionundercursor.h
index a891dc8968f..144edde2ced 100644
--- a/src/plugins/qmleditor/qmlexpressionundercursor.h
+++ b/src/plugins/qmleditor/qmlexpressionundercursor.h
@@ -2,21 +2,30 @@
 #define QMLEXPRESSIONUNDERCURSOR_H
 
 #include <QStack>
+#include <QTextBlock>
 #include <QTextCursor>
 
-#include "qmljsastvisitor_p.h"
+#include "qmldocument.h"
+#include "qmljsastfwd_p.h"
+#include "qmlsymbol.h"
+
+namespace QmlJS {
+    class Engine;
+    class NodePool;
+}
 
 namespace QmlEditor {
 namespace Internal {
 
-class QmlExpressionUnderCursor: protected QmlJS::AST::Visitor
+class QmlExpressionUnderCursor
 {
 public:
     QmlExpressionUnderCursor();
+    virtual ~QmlExpressionUnderCursor();
 
-    void operator()(const QTextCursor &cursor, QmlJS::AST::UiProgram *program);
+    void operator()(const QTextCursor &cursor, const QmlDocument::Ptr &doc);
 
-    QStack<QmlJS::AST::Node *> expressionScopes() const
+    QStack<QmlSymbol *> expressionScopes() const
     { return _expressionScopes; }
 
     QmlJS::AST::Node *expressionNode() const
@@ -28,26 +37,21 @@ public:
     int expressionLength() const
     { return _expressionLength; }
 
-protected:
-    virtual bool visit(QmlJS::AST::Block *ast);
-    virtual bool visit(QmlJS::AST::FieldMemberExpression *ast);
-    virtual bool visit(QmlJS::AST::IdentifierExpression *ast);
-    virtual bool visit(QmlJS::AST::UiImport *ast);
-    virtual bool visit(QmlJS::AST::UiObjectBinding *ast);
-    virtual bool visit(QmlJS::AST::UiObjectDefinition *ast);
-    virtual bool visit(QmlJS::AST::UiQualifiedId *ast);
+private:
+    void parseExpression(const QTextBlock &block);
 
-    virtual void endVisit(QmlJS::AST::Block *);
-    virtual void endVisit(QmlJS::AST::UiObjectBinding *);
-    virtual void endVisit(QmlJS::AST::UiObjectDefinition *);
+    QmlJS::AST::ExpressionNode *tryExpression(const QString &text);
+    QmlJS::AST::Statement *tryStatement(const QString &text);
+    QmlJS::AST::UiObjectMember *tryBinding(const QString &text);
 
 private:
-    QStack<QmlJS::AST::Node *> _scopes;
-    QStack<QmlJS::AST::Node *> _expressionScopes;
+    QStack<QmlSymbol *> _expressionScopes;
     QmlJS::AST::Node *_expressionNode;
     int _expressionOffset;
     int _expressionLength;
     quint32 _pos;
+    QmlJS::Engine *_engine;
+    QmlJS::NodePool *_nodePool;
 };
 
 } // namespace Internal
diff --git a/src/plugins/qmleditor/qmllookupcontext.cpp b/src/plugins/qmleditor/qmllookupcontext.cpp
index 6e2ca83ea86..abfca64db86 100644
--- a/src/plugins/qmleditor/qmllookupcontext.cpp
+++ b/src/plugins/qmleditor/qmllookupcontext.cpp
@@ -10,7 +10,7 @@ using namespace QmlEditor::Internal;
 using namespace QmlJS;
 using namespace QmlJS::AST;
 
-QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes,
+QmlLookupContext::QmlLookupContext(const QStack<QmlSymbol *> &scopes,
                                    const QmlDocument::Ptr &doc,
                                    const Snapshot &snapshot):
         _scopes(scopes),
@@ -19,28 +19,49 @@ QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes,
 {
 }
 
-QmlLookupContext::~QmlLookupContext()
+static inline int findFirstQmlObjectScope(const QStack<QmlSymbol*> &scopes, int startIdx)
 {
-    qDeleteAll(_temporarySymbols);
+    if (startIdx < 0 || startIdx >= scopes.size())
+        return -1;
+
+    for (int i = startIdx; i >= 0; --i) {
+        QmlSymbol *current = scopes.at(i);
+
+        if (current->isSymbolFromFile()) {
+            Node *node = current->asSymbolFromFile()->node();
+
+            if (cast<UiObjectBinding*>(node) || cast<UiObjectDefinition*>(node)) {
+                return i;
+            }
+        }
+    }
+
+    return -1;
+}
+
+static inline QmlSymbol *resolveParent(const QStack<QmlSymbol*> &scopes)
+{
+    int idx = findFirstQmlObjectScope(scopes, scopes.size() - 1);
+    if (idx < 1)
+        return 0;
+
+    idx = findFirstQmlObjectScope(scopes, idx - 1);
+
+    if (idx < 0)
+        return 0;
+    else
+        return scopes.at(idx);
 }
 
 QmlSymbol *QmlLookupContext::resolve(const QString &name)
 {
     // look at property definitions
-    if (QmlSymbol *propertySymbol = resolveProperty(name, _scopes.top(), _doc->fileName()))
-        return propertySymbol;
+    if (!_scopes.isEmpty())
+        if (QmlSymbol *propertySymbol = resolveProperty(name, _scopes.top(), _doc->fileName()))
+            return propertySymbol;
 
     if (name == "parent") {
-        for (int i = _scopes.size() - 2; i >= 0; --i) {
-            Node *scope = _scopes.at(i);
-
-            if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(scope))
-                return createSymbol(_doc->fileName(), definition);
-            else if (UiObjectBinding *binding = cast<UiObjectBinding*>(scope))
-                return createSymbol(_doc->fileName(), binding);
-        }
-
-        return 0;
+        return resolveParent(_scopes);
     }
 
     // look at the ids.
@@ -52,13 +73,6 @@ QmlSymbol *QmlLookupContext::resolve(const QString &name)
         return 0;
 }
 
-QmlSymbol *QmlLookupContext::createSymbol(const QString &fileName, QmlJS::AST::UiObjectMember *node)
-{
-    QmlSymbol *symbol = new QmlSymbolFromFile(fileName, node);
-    _temporarySymbols.append(symbol);
-    return symbol;
-}
-
 QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fileName)
 {
     // TODO: handle import-as.
@@ -88,66 +102,41 @@ QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fil
         if (importedTypes.contains(name)) {
             QmlDocument::Ptr importedDoc = importedTypes.value(name);
 
-            UiProgram *importedProgram = importedDoc->program();
-            if (importedProgram && importedProgram->members && importedProgram->members->member)
-                return createSymbol(importedDoc->fileName(), importedProgram->members->member);
+            return importedDoc->symbols().at(0);
         }
     }
 
     return 0;
 }
 
-QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, Node *scope, const QString &fileName)
+QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName)
 {
+    foreach (QmlSymbol *symbol, scope->members())
+        if (symbol->name() == name)
+            return symbol;
+
     UiQualifiedId *typeName = 0;
 
-    if (UiObjectBinding *binding = cast<UiObjectBinding*>(scope)) {
-        if (QmlSymbol *symbol = resolveProperty(name, binding->initializer, fileName))
-            return symbol;
-        else
+    if (scope->isSymbolFromFile()) {
+        Node *ast = scope->asSymbolFromFile()->node();
+
+        if (UiObjectBinding *binding = cast<UiObjectBinding*>(ast)) {
             typeName = binding->qualifiedTypeNameId;
-    } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(scope)) {
-        if (QmlSymbol *symbol = resolveProperty(name, definition->initializer, fileName))
-            return symbol;
-        else
+        } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(ast)) {
             typeName = definition->qualifiedTypeNameId;
-    } // TODO: extend this to handle (JavaScript) block scopes.
+        } // TODO: extend this to handle (JavaScript) block scopes.
+    }
 
     if (typeName == 0)
         return 0;
 
     QmlSymbol *typeSymbol = resolveType(toString(typeName), fileName);
-    if (typeSymbol && typeSymbol->isSymbolFromFile()) {
-        return resolveProperty(name, typeSymbol->asSymbolFromFile()->node(), typeSymbol->asSymbolFromFile()->fileName());
-    }
-
-    return 0;
-}
-
-QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, QmlJS::AST::UiObjectInitializer *initializer, const QString &fileName)
-{
-    if (!initializer)
+    if (!typeSymbol)
         return 0;
 
-    for (UiObjectMemberList *iter = initializer->members; iter; iter = iter->next) {
-        UiObjectMember *member = iter->member;
-        if (!member)
-            continue;
-
-        if (UiPublicMember *publicMember = cast<UiPublicMember*>(member)) {
-            if (name == publicMember->name->asString())
-                return createSymbol(fileName, publicMember);
-        } else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member)) {
-            if (name == toString(objectBinding->qualifiedId))
-                return createSymbol(fileName, objectBinding);
-        } else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member)) {
-            if (name == toString(arrayBinding->qualifiedId))
-                return createSymbol(fileName, arrayBinding);
-        } else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member)) {
-            if (name == toString(scriptBinding->qualifiedId))
-                return createSymbol(fileName, scriptBinding);
-        }
-    }
+    if (typeSymbol->isSymbolFromFile()) {
+        return resolveProperty(name, typeSymbol->asSymbolFromFile(), typeSymbol->asSymbolFromFile()->fileName());
+    } // TODO: internal types
 
     return 0;
 }
@@ -169,10 +158,17 @@ QString QmlLookupContext::toString(UiQualifiedId *id)
     return str;
 }
 
-QList<QmlSymbol*> QmlLookupContext::visibleSymbols(QmlJS::AST::Node * /* scope */)
+QList<QmlSymbol*> QmlLookupContext::visibleSymbolsInScope()
 {
-    // FIXME
-    return QList<QmlSymbol*>();
+    QList<QmlSymbol*> result;
+
+    if (!_scopes.isEmpty()) {
+        QmlSymbol *scope = _scopes.top();
+
+        result.append(scope->members());
+    }
+
+    return result;
 }
 
 QList<QmlSymbol*> QmlLookupContext::visibleTypes()
@@ -196,11 +192,7 @@ QList<QmlSymbol*> QmlLookupContext::visibleTypes()
 
         const QMap<QString, QmlDocument::Ptr> types = _snapshot.componentsDefinedByImportedDocuments(_doc, path);
         foreach (const QmlDocument::Ptr typeDoc, types) {
-            UiProgram *typeProgram = typeDoc->program();
-
-            if (typeProgram && typeProgram->members && typeProgram->members->member) {
-                result.append(createSymbol(typeDoc->fileName(), typeProgram->members->member));
-            }
+            result.append(typeDoc->symbols().at(0));
         }
     }
 
diff --git a/src/plugins/qmleditor/qmllookupcontext.h b/src/plugins/qmleditor/qmllookupcontext.h
index 80635c03937..d18e0ec5ec9 100644
--- a/src/plugins/qmleditor/qmllookupcontext.h
+++ b/src/plugins/qmleditor/qmllookupcontext.h
@@ -13,10 +13,9 @@ namespace Internal {
 class QmlLookupContext
 {
 public:
-    QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes,
+    QmlLookupContext(const QStack<QmlSymbol *> &scopes,
                      const QmlDocument::Ptr &doc,
                      const Snapshot &snapshot);
-    ~QmlLookupContext();
 
     QmlSymbol *resolve(const QString &name);
     QmlSymbol *resolveType(const QString &name)
@@ -27,23 +26,19 @@ public:
     QmlDocument::Ptr document() const
     { return _doc; }
 
-    QList<QmlSymbol*> visibleSymbols(QmlJS::AST::Node *scope);
+    QList<QmlSymbol*> visibleSymbolsInScope();
     QList<QmlSymbol*> visibleTypes();
 
 private:
-    QmlSymbol *createSymbol(const QString &fileName, QmlJS::AST::UiObjectMember *node);
-
     QmlSymbol *resolveType(const QString &name, const QString &fileName);
-    QmlSymbol *resolveProperty(const QString &name, QmlJS::AST::Node *scope, const QString &fileName);
-    QmlSymbol *resolveProperty(const QString &name, QmlJS::AST::UiObjectInitializer *initializer, const QString &fileName);
+    QmlSymbol *resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName);
 
     static QString toString(QmlJS::AST::UiQualifiedId *id);
 
 private:
-    QStack<QmlJS::AST::Node *> _scopes;
+    QStack<QmlSymbol *> _scopes;
     QmlDocument::Ptr _doc;
     Snapshot _snapshot;
-    QList<QmlSymbol*> _temporarySymbols;
 };
 
 } // namespace Internal
diff --git a/src/plugins/qmleditor/qmlresolveexpression.cpp b/src/plugins/qmleditor/qmlresolveexpression.cpp
index 89c7090a4d4..1b464147aa7 100644
--- a/src/plugins/qmleditor/qmlresolveexpression.cpp
+++ b/src/plugins/qmleditor/qmlresolveexpression.cpp
@@ -12,11 +12,6 @@ QmlResolveExpression::QmlResolveExpression(const QmlLookupContext &context)
 {
 }
 
-QmlResolveExpression::~QmlResolveExpression()
-{
-    qDeleteAll(_temporarySymbols);
-}
-
 QmlSymbol *QmlResolveExpression::typeOf(Node *node)
 {
     QmlSymbol *previousValue = switchValue(0);
@@ -25,6 +20,30 @@ QmlSymbol *QmlResolveExpression::typeOf(Node *node)
     return switchValue(previousValue);
 }
 
+QList<QmlSymbol*> QmlResolveExpression::visibleSymbols(Node *node)
+{
+    QList<QmlSymbol*> result;
+
+    QmlSymbol *symbol = typeOf(node);
+    if (symbol) {
+        if (symbol->isIdSymbol())
+            symbol = symbol->asIdSymbol()->parentNode();
+        result.append(symbol->members());
+
+        // TODO: also add symbols from super-types
+    } else {
+        result.append(_context.visibleTypes());
+    }
+
+    if (node) {
+        foreach (QmlIdSymbol *idSymbol, _context.document()->ids().values())
+            result.append(idSymbol);
+    }
+
+    result.append(_context.visibleSymbolsInScope());
+
+    return result;
+}
 
 QmlSymbol *QmlResolveExpression::switchValue(QmlSymbol *value)
 {
@@ -58,52 +77,19 @@ bool QmlResolveExpression::visit(FieldMemberExpression *ast)
 {
     const QString memberName = ast->name->asString();
 
-    const QmlSymbol *base = typeOf(ast->base);
+    QmlSymbol *base = typeOf(ast->base);
     if (!base)
         return false;
 
     if (base->isIdSymbol())
         base = base->asIdSymbol()->parentNode();
 
-    UiObjectMemberList *members = 0;
-
-    if (const QmlSymbolFromFile *symbol = base->asSymbolFromFile()) {
-        Node *node = symbol->node();
-
-        if (UiObjectBinding *binding = cast<UiObjectBinding*>(node)) {
-            if (binding->initializer)
-                members = binding->initializer->members;
-        } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(node)) {
-            if (definition->initializer)
-                members = definition->initializer->members;
-        }
-    }
+    if (!base)
+        return false;
 
-    for (UiObjectMemberList *it = members; it; it = it->next) {
-        UiObjectMember *member = it->member;
-
-        if (UiPublicMember *publicMember = cast<UiPublicMember *>(member)) {
-            if (publicMember->name && publicMember->name->asString() == memberName) {
-                _value = createPropertyDefinitionSymbol(publicMember);
-                break; // we're done.
-            }
-        } else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member)) {
-            if (matches(objectBinding->qualifiedId, memberName)) {
-                _value = createSymbolFromFile(objectBinding);
-                break; // we're done
-            }
-        } else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member)) {
-            if (matches(scriptBinding->qualifiedId, memberName)) {
-                _value = createSymbolFromFile(scriptBinding);
-                break; // we're done
-            }
-        } else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member)) {
-            if (matches(arrayBinding->qualifiedId, memberName)) {
-                _value = createSymbolFromFile(arrayBinding);
-                break; // we're done
-            }
-        }
-    }
+    foreach (QmlSymbol *memberSymbol, base->members())
+        if (memberSymbol->name() == memberName)
+            _value = memberSymbol;
 
     return false;
 }
@@ -114,17 +100,3 @@ bool QmlResolveExpression::visit(QmlJS::AST::UiQualifiedId *ast)
 
     return false;
 }
-
-QmlPropertyDefinitionSymbol *QmlResolveExpression::createPropertyDefinitionSymbol(QmlJS::AST::UiPublicMember *ast)
-{
-    QmlPropertyDefinitionSymbol *symbol = new QmlPropertyDefinitionSymbol(_context.document()->fileName(), ast);
-    _temporarySymbols.append(symbol);
-    return symbol;
-}
-
-QmlSymbolFromFile *QmlResolveExpression::createSymbolFromFile(QmlJS::AST::UiObjectMember *ast)
-{
-    QmlSymbolFromFile *symbol = new QmlSymbolFromFile(_context.document()->fileName(), ast);
-    _temporarySymbols.append(symbol);
-    return symbol;
-}
diff --git a/src/plugins/qmleditor/qmlresolveexpression.h b/src/plugins/qmleditor/qmlresolveexpression.h
index b756887f4a4..41e44e88531 100644
--- a/src/plugins/qmleditor/qmlresolveexpression.h
+++ b/src/plugins/qmleditor/qmlresolveexpression.h
@@ -12,28 +12,21 @@ class QmlResolveExpression: protected QmlJS::AST::Visitor
 {
 public:
     QmlResolveExpression(const QmlLookupContext &context);
-    ~QmlResolveExpression();
 
-    QmlSymbol *operator()(QmlJS::AST::Node *node)
-    { return typeOf(node); }
+    QmlSymbol *typeOf(QmlJS::AST::Node *node);
+    QList<QmlSymbol*> visibleSymbols(QmlJS::AST::Node *node);
 
 protected:
     using QmlJS::AST::Visitor::visit;
 
-    QmlSymbol *typeOf(QmlJS::AST::Node *node);
     QmlSymbol *switchValue(QmlSymbol *symbol);
 
     virtual bool visit(QmlJS::AST::FieldMemberExpression *ast);
     virtual bool visit(QmlJS::AST::IdentifierExpression *ast);
     virtual bool visit(QmlJS::AST::UiQualifiedId *ast);
 
-private:
-    QmlPropertyDefinitionSymbol *createPropertyDefinitionSymbol(QmlJS::AST::UiPublicMember *ast);
-    QmlSymbolFromFile *createSymbolFromFile(QmlJS::AST::UiObjectMember *ast);
-
 private:
     QmlLookupContext _context;
-    QList<QmlSymbol*> _temporarySymbols;
     QmlSymbol *_value;
 };
 
diff --git a/src/plugins/qmleditor/qmlscope.cpp b/src/plugins/qmleditor/qmlscope.cpp
new file mode 100644
index 00000000000..8d467d12f73
--- /dev/null
+++ b/src/plugins/qmleditor/qmlscope.cpp
@@ -0,0 +1,35 @@
+#include "qmlscope.h"
+
+using namespace QmlEditor;
+using namespace QmlEditor::Internal;
+
+Scope::Scope(Scope *parentScope):
+        _parentScope(parentScope)
+{
+}
+
+Scope::~Scope()
+{
+}
+
+bool Scope::isJSScope()
+{ return asJSScope() != 0; }
+
+bool Scope::isQmlScope()
+{ return asQmlScope() != 0; }
+
+JSScope *Scope::asJSScope()
+{ return 0; }
+
+QmlScope *Scope::asQmlScope()
+{ return 0; }
+
+QmlScope::QmlScope(QmlScope *parentScope):
+        Scope(parentScope)
+{
+}
+
+JSScope::JSScope(Scope *parentScope):
+        Scope(parentScope)
+{
+}
diff --git a/src/plugins/qmleditor/qmlscope.h b/src/plugins/qmleditor/qmlscope.h
new file mode 100644
index 00000000000..a53c1174bb0
--- /dev/null
+++ b/src/plugins/qmleditor/qmlscope.h
@@ -0,0 +1,40 @@
+#ifndef QMLSCOPE_H
+#define QMLSCOPE_H
+
+namespace QmlEditor {
+namespace Internal {
+
+class QmlScope;
+class JSScope;
+
+class Scope
+{
+public:
+    Scope(Scope *parentScope);
+    virtual ~Scope();
+
+    bool isQmlScope();
+    bool isJSScope();
+
+    virtual QmlScope *asQmlScope();
+    virtual JSScope *asJSScope();
+
+private:
+    Scope *_parentScope;
+};
+
+class QmlScope: public Scope
+{
+public:
+    QmlScope(QmlScope *parentScope);
+};
+
+class JSScope: public Scope
+{
+    JSScope(Scope *parentScope);
+};
+
+} // namespace Internal
+} // namespace QmlEditor
+
+#endif // QMLSCOPE_H
diff --git a/src/plugins/qmleditor/qmlsymbol.cpp b/src/plugins/qmleditor/qmlsymbol.cpp
index 21271fb7099..6eacae86450 100644
--- a/src/plugins/qmleditor/qmlsymbol.cpp
+++ b/src/plugins/qmleditor/qmlsymbol.cpp
@@ -1,4 +1,5 @@
 #include "qmljsast_p.h"
+#include "qmljsengine_p.h"
 #include "qmlsymbol.h"
 
 using namespace QmlEditor;
@@ -7,41 +8,67 @@ using namespace QmlJS::AST;
 
 QmlSymbol::~QmlSymbol()
 {
+    qDeleteAll(_members);
 }
 
-bool QmlSymbol::isBuildInSymbol() const
+bool QmlSymbol::isBuildInSymbol()
 { return asBuildInSymbol() != 0; }
 
-bool QmlSymbol::isSymbolFromFile() const
+bool QmlSymbol::isSymbolFromFile()
 { return asSymbolFromFile() != 0; }
 
-bool QmlSymbol::isIdSymbol() const
+bool QmlSymbol::isIdSymbol()
 { return asIdSymbol() != 0; }
 
-QmlBuildInSymbol const *QmlSymbol::asBuildInSymbol() const
+bool QmlSymbol::isPropertyDefinitionSymbol()
+{ return asPropertyDefinitionSymbol() != 0; }
+
+QmlBuildInSymbol *QmlSymbol::asBuildInSymbol()
+{ return 0; }
+
+QmlSymbolFromFile *QmlSymbol::asSymbolFromFile()
 { return 0; }
 
-QmlSymbolFromFile const *QmlSymbol::asSymbolFromFile() const
+QmlIdSymbol *QmlSymbol::asIdSymbol()
 { return 0; }
 
-QmlIdSymbol const *QmlSymbol::asIdSymbol() const
+QmlPropertyDefinitionSymbol *QmlSymbol::asPropertyDefinitionSymbol()
 { return 0; }
 
+const QmlSymbol::List QmlSymbol::members()
+{ return _members; }
+
 QmlBuildInSymbol::~QmlBuildInSymbol()
 {}
 
-QmlBuildInSymbol const* QmlBuildInSymbol::asBuildInSymbol() const
+QmlBuildInSymbol *QmlBuildInSymbol::asBuildInSymbol()
 { return this; }
 
 QmlSymbolFromFile::QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node):
         _fileName(fileName),
         _node(node)
-{}
+{
+    if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(_node)) {
+        if (objectBinding->initializer)
+            for (UiObjectMemberList *iter = objectBinding->initializer->members; iter; iter = iter->next)
+                if (iter->member)
+                    todo.append(iter->member);
+    } else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(_node)) {
+        if (objectDefinition->initializer)
+            for (UiObjectMemberList *iter = objectDefinition->initializer->members; iter; iter = iter->next)
+                if (iter->member)
+                    todo.append(iter->member);
+    } else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(_node)) {
+        for (UiArrayMemberList *iter = arrayBinding->members; iter; iter = iter->next)
+            if (iter->member)
+                todo.append(iter->member);
+    }
+}
 
 QmlSymbolFromFile::~QmlSymbolFromFile()
 {}
 
-const QmlSymbolFromFile *QmlSymbolFromFile::asSymbolFromFile() const
+QmlSymbolFromFile *QmlSymbolFromFile::asSymbolFromFile()
 { return this; }
 
 int QmlSymbolFromFile::line() const
@@ -50,7 +77,76 @@ int QmlSymbolFromFile::line() const
 int QmlSymbolFromFile::column() const
 { return _node->firstSourceLocation().startColumn; }
 
-QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, const QmlSymbolFromFile &parentNode):
+static inline QString toString(UiQualifiedId *qId)
+{
+    QString result;
+
+    for (UiQualifiedId *iter = qId; iter; iter = iter->next) {
+        if (!iter->name)
+            continue;
+
+        result += iter->name->asString();
+
+        if (iter->next)
+            result += '.';
+    }
+
+    return result;
+}
+
+const QString QmlSymbolFromFile::name() const
+{
+    if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(_node))
+        return toString(objectBinding->qualifiedId);
+    else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(_node))
+        return toString(scriptBinding->qualifiedId);
+    else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(_node))
+        return toString(arrayBinding->qualifiedId);
+    else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(_node))
+        return toString(objectDefinition->qualifiedTypeNameId);
+    else
+        return QString::null;
+}
+
+const QmlSymbol::List QmlSymbolFromFile::members()
+{
+    if (!todo.isEmpty()) {
+        foreach (Node *node, todo) {
+            if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(node))
+                _members.append(new QmlSymbolFromFile(fileName(), objectBinding));
+            else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(node))
+                _members.append(new QmlSymbolFromFile(fileName(), objectDefinition));
+            else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(node))
+                _members.append(new QmlSymbolFromFile(fileName(), arrayBinding));
+            else if (UiPublicMember *publicMember = cast<UiPublicMember*>(node))
+                _members.append(new QmlPropertyDefinitionSymbol(fileName(), publicMember));
+            else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(node)) {
+                if (scriptBinding->qualifiedId && scriptBinding->qualifiedId->name && scriptBinding->qualifiedId->name->asString() == QLatin1String("id") && !scriptBinding->qualifiedId->next)
+                    _members.append(new QmlIdSymbol(fileName(), scriptBinding, this));
+                else
+                    _members.append(new QmlSymbolFromFile(fileName(), scriptBinding));
+            }
+        }
+
+        todo.clear();
+    }
+
+    return _members;
+}
+
+QmlSymbolFromFile *QmlSymbolFromFile::findMember(QmlJS::AST::Node *memberNode)
+{
+    List symbols = members();
+
+    foreach (QmlSymbol *symbol, symbols)
+        if (symbol->isSymbolFromFile())
+            if (memberNode == symbol->asSymbolFromFile()->node())
+                return symbol->asSymbolFromFile();
+
+    return 0;
+}
+
+QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, QmlSymbolFromFile *parentNode):
         QmlSymbolFromFile(fileName, idNode),
         _parentNode(parentNode)
 {}
@@ -58,7 +154,7 @@ QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *i
 QmlIdSymbol::~QmlIdSymbol()
 {}
 
-QmlIdSymbol const *QmlIdSymbol::asIdSymbol() const
+QmlIdSymbol *QmlIdSymbol::asIdSymbol()
 { return this; }
 
 int QmlIdSymbol::line() const
@@ -67,6 +163,16 @@ int QmlIdSymbol::line() const
 int QmlIdSymbol::column() const
 { return idNode()->statement->firstSourceLocation().startColumn; }
 
+const QString QmlIdSymbol::id() const
+{
+    if (ExpressionStatement *e = cast<ExpressionStatement*>(idNode()->statement))
+        if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression))
+            if (i->name)
+                return i->name->asString();
+
+    return QString();
+}
+
 QmlJS::AST::UiScriptBinding *QmlIdSymbol::idNode() const
 { return cast<UiScriptBinding*>(node()); }
 
@@ -77,6 +183,9 @@ QmlPropertyDefinitionSymbol::QmlPropertyDefinitionSymbol(const QString &fileName
 QmlPropertyDefinitionSymbol::~QmlPropertyDefinitionSymbol()
 {}
 
+QmlPropertyDefinitionSymbol *QmlPropertyDefinitionSymbol::asPropertyDefinitionSymbol()
+{ return this; }
+
 int QmlPropertyDefinitionSymbol::line() const
 { return propertyNode()->identifierToken.startLine; }
 
@@ -85,3 +194,6 @@ int QmlPropertyDefinitionSymbol::column() const
 
 QmlJS::AST::UiPublicMember *QmlPropertyDefinitionSymbol::propertyNode() const
 { return cast<UiPublicMember*>(node()); }
+
+const QString QmlPropertyDefinitionSymbol::name() const
+{ return propertyNode()->name->asString(); }
diff --git a/src/plugins/qmleditor/qmlsymbol.h b/src/plugins/qmleditor/qmlsymbol.h
index be827faea24..8957db80c37 100644
--- a/src/plugins/qmleditor/qmlsymbol.h
+++ b/src/plugins/qmleditor/qmlsymbol.h
@@ -1,6 +1,7 @@
 #ifndef QMLSYMBOL_H
 #define QMLSYMBOL_H
 
+#include <QList>
 #include <QString>
 
 #include "qmljsastfwd_p.h"
@@ -10,25 +11,44 @@ namespace QmlEditor {
 class QmlSymbol
 {
 public:
-    virtual ~QmlSymbol() = 0;
+    typedef QList<QmlSymbol*> List;
 
-    bool isBuildInSymbol() const;
-    bool isSymbolFromFile() const;
-    bool isIdSymbol() const;
+public:
+    virtual ~QmlSymbol();
+
+    virtual const QString name() const = 0;
+    virtual const List members();
+
+    bool isBuildInSymbol();
+    bool isSymbolFromFile();
+    bool isIdSymbol();
+    bool isPropertyDefinitionSymbol();
+
+    virtual class QmlBuildInSymbol *asBuildInSymbol();
+    virtual class QmlSymbolFromFile *asSymbolFromFile();
+    virtual class QmlIdSymbol *asIdSymbol();
+    virtual class QmlPropertyDefinitionSymbol *asPropertyDefinitionSymbol();
 
-    virtual class QmlBuildInSymbol const *asBuildInSymbol() const;
-    virtual class QmlSymbolFromFile const *asSymbolFromFile() const;
-    virtual class QmlIdSymbol const *asIdSymbol() const;
+protected:
+    List _members;
 };
 
 class QmlBuildInSymbol: public QmlSymbol
 {
 public:
+    QmlBuildInSymbol(const QString &name): _name(name) {}
     virtual ~QmlBuildInSymbol();
 
-    virtual QmlBuildInSymbol const *asBuildInSymbol() const;
+    virtual QmlBuildInSymbol *asBuildInSymbol();
+
+    virtual const QString name() const
+    { return _name; }
+
+    // TODO:
+//    virtual const List members();
 
 private:
+    QString _name;
 };
 
 class QmlSymbolFromFile: public QmlSymbol
@@ -37,7 +57,7 @@ public:
     QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node);
     virtual ~QmlSymbolFromFile();
 
-    virtual QmlSymbolFromFile const *asSymbolFromFile() const;
+    virtual QmlSymbolFromFile *asSymbolFromFile();
 
     QString fileName() const
     { return _fileName; }
@@ -48,30 +68,43 @@ public:
     QmlJS::AST::UiObjectMember *node() const
     { return _node; }
 
+    virtual const QString name() const;
+    virtual const List members();
+    virtual QmlSymbolFromFile *findMember(QmlJS::AST::Node *memberNode);
+
+private:
+    void fillTodo(QmlJS::AST::UiObjectMemberList *members);
+
 private:
     QString _fileName;
     QmlJS::AST::UiObjectMember *_node;
+    QList<QmlJS::AST::Node*> todo;
 };
 
 class QmlIdSymbol: public QmlSymbolFromFile
 {
 public:
-    QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, const QmlSymbolFromFile &parentNode);
+    QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, QmlSymbolFromFile *parentNode);
     virtual ~QmlIdSymbol();
 
-    QmlIdSymbol const *asIdSymbol() const;
+    QmlIdSymbol *asIdSymbol();
 
     virtual int line() const;
     virtual int column() const;
 
-    QmlSymbolFromFile const *parentNode() const
-    { return &_parentNode; }
+    QmlSymbolFromFile *parentNode() const
+    { return _parentNode; }
+
+    virtual const QString name() const
+    { return "id"; }
+
+    virtual const QString id() const;
 
 private:
     QmlJS::AST::UiScriptBinding *idNode() const;
 
 private:
-    QmlSymbolFromFile _parentNode;
+    QmlSymbolFromFile *_parentNode;
 };
 
 class QmlPropertyDefinitionSymbol: public QmlSymbolFromFile
@@ -80,9 +113,13 @@ public:
     QmlPropertyDefinitionSymbol(const QString &fileName, QmlJS::AST::UiPublicMember *propertyNode);
     virtual ~QmlPropertyDefinitionSymbol();
 
+    QmlPropertyDefinitionSymbol *asPropertyDefinitionSymbol();
+
     virtual int line() const;
     virtual int column() const;
 
+    virtual const QString name() const;
+
 private:
     QmlJS::AST::UiPublicMember *propertyNode() const;
 };
-- 
GitLab