diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp
index 513773d34fdfe28c7f7c63222a783a01e4c364f2..58900fec9328156b7b1c0f3c6e4f2de4bed34719 100644
--- a/src/libs/qmljs/qmljsdocument.cpp
+++ b/src/libs/qmljs/qmljsdocument.cpp
@@ -42,8 +42,7 @@ using namespace QmlJS::AST;
 Document::Document(const QString &fileName)
     : _engine(0)
     , _pool(0)
-    , _uiProgram(0)
-    , _jsProgram(0)
+    , _ast(0)
     , _fileName(fileName)
     , _parsedCorrectly(false)
 {
@@ -74,20 +73,17 @@ Document::Ptr Document::create(const QString &fileName)
 
 AST::UiProgram *Document::qmlProgram() const
 {
-    return _uiProgram;
+    return cast<UiProgram *>(_ast);
 }
 
 AST::Program *Document::jsProgram() const
 {
-    return _jsProgram;
+    return cast<Program *>(_ast);
 }
 
 AST::Node *Document::ast() const
 {
-    Q_ASSERT(!_uiProgram || !_jsProgram);
-    if (_uiProgram)
-        return _uiProgram;
-    return _jsProgram;
+    return _ast;
 }
 
 QList<DiagnosticMessage> Document::diagnosticMessages() const
@@ -109,8 +105,7 @@ bool Document::parseQml()
 {
     Q_ASSERT(! _engine);
     Q_ASSERT(! _pool);
-    Q_ASSERT(! _uiProgram);
-    Q_ASSERT(! _jsProgram);
+    Q_ASSERT(! _ast);
 
     _engine = new Engine();
     _pool = new NodePool(_fileName, _engine);
@@ -122,11 +117,11 @@ bool Document::parseQml()
     lexer.setCode(_source, /*line = */ 1);
 
     _parsedCorrectly = parser.parse();
-    _uiProgram = parser.ast();
+    _ast = parser.ast();
     _diagnosticMessages = parser.diagnosticMessages();
 
-    if (_uiProgram) {
-        for (QmlJS::AST::UiObjectMemberList *iter = _uiProgram->members; iter; iter = iter->next)
+    if (qmlProgram()) {
+        for (QmlJS::AST::UiObjectMemberList *iter = qmlProgram()->members; iter; iter = iter->next)
             if (iter->member)
                 _symbols.append(new SymbolFromFile(_fileName, iter->member));
 
@@ -143,8 +138,7 @@ bool Document::parseJavaScript()
 {
     Q_ASSERT(! _engine);
     Q_ASSERT(! _pool);
-    Q_ASSERT(! _uiProgram);
-    Q_ASSERT(! _jsProgram);
+    Q_ASSERT(! _ast);
 
     _engine = new Engine();
     _pool = new NodePool(_fileName, _engine);
@@ -156,7 +150,31 @@ bool Document::parseJavaScript()
     lexer.setCode(_source, /*line = */ 1);
 
     _parsedCorrectly = parser.parseProgram();
-    _jsProgram = cast<Program*>(parser.rootNode());
+    _ast = cast<Program*>(parser.rootNode());
+    _diagnosticMessages = parser.diagnosticMessages();
+
+    return _parsedCorrectly;
+}
+
+bool Document::parseExpression()
+{
+    Q_ASSERT(! _engine);
+    Q_ASSERT(! _pool);
+    Q_ASSERT(! _ast);
+
+    _engine = new Engine();
+    _pool = new NodePool(_fileName, _engine);
+    _ids.clear();
+
+    Lexer lexer(_engine);
+    Parser parser(_engine);
+
+    lexer.setCode(_source, /*line = */ 1);
+
+    _parsedCorrectly = parser.parseExpression();
+    _ast = parser.rootNode();
+    if (_ast)
+        _ast = _ast->expressionCast();
     _diagnosticMessages = parser.diagnosticMessages();
 
     return _parsedCorrectly;
diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h
index 98bedda327d7c7fa52a1ad1692a4e7d850a5831e..3ea931746a3871a6ad63744c0e42c8f314451da8 100644
--- a/src/libs/qmljs/qmljsdocument.h
+++ b/src/libs/qmljs/qmljsdocument.h
@@ -67,6 +67,7 @@ public:
 
     bool parseQml();
     bool parseJavaScript();
+    bool parseExpression();
 
     bool isParsedCorrectly() const
     { return _parsedCorrectly; }
@@ -84,8 +85,7 @@ public:
 private:
     QmlJS::Engine *_engine;
     QmlJS::NodePool *_pool;
-    QmlJS::AST::UiProgram *_uiProgram;
-    QmlJS::AST::Program *_jsProgram;
+    QmlJS::AST::Node *_ast;
     QList<QmlJS::DiagnosticMessage> _diagnosticMessages;
     QString _fileName;
     QString _path;
diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp
index 4123ab0f0426526efbe6ea11b81195f2943418a3..21afc6666eeafa7511df70630abd63c320b07a6b 100644
--- a/src/plugins/qmljseditor/qmlcodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp
@@ -50,6 +50,7 @@
 
 using namespace QmlJSEditor;
 using namespace QmlJSEditor::Internal;
+using namespace QmlJS;
 
 
 // Temporary workaround until we have proper icons for QML completion items
@@ -83,6 +84,7 @@ static QIcon iconForColor(const QColor &color)
     return pix;
 }
 
+namespace {
 
 class Evaluate: public QmlJS::AST::Visitor
 {
@@ -181,6 +183,46 @@ protected:
 
 };
 
+class EnumerateProperties
+{
+    QSet<const Interpreter::ObjectValue *> _processed;
+    QHash<QString, const Interpreter::Value *> _properties;
+
+public:
+    QHash<QString, const Interpreter::Value *> operator()(const Interpreter::Value *value)
+    {
+        _processed.clear();
+        _properties.clear();
+        enumerateProperties(value);
+        return _properties;
+    }
+
+private:
+    void enumerateProperties(const Interpreter::Value *value)
+    {
+        if (! value)
+            return;
+        else if (const Interpreter::ObjectValue *object = value->asObjectValue()) {
+            enumerateProperties(object);
+        }
+    }
+
+    void enumerateProperties(const Interpreter::ObjectValue *object)
+    {
+        if (! object || _processed.contains(object))
+            return;
+
+        _processed.insert(object);
+        enumerateProperties(object->prototype());
+
+        for (Interpreter::ObjectValue::MemberIterator it = object->firstMember(); it != object->lastMember(); ++it) {
+            _properties.insert(it.key(), it.value());
+        }
+    }
+};
+
+} // end of anonymous namespace
+
 QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager, QmlJS::TypeSystem *typeSystem, QObject *parent)
     : TextEditor::ICompletionCollector(parent),
       m_modelManager(modelManager),
@@ -248,32 +290,33 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 
     const QIcon typeIcon = iconForColor(Qt::yellow);
 
-    foreach (QmlJS::Document::Ptr doc, snapshot) {
-        const QFileInfo fileInfo(doc->fileName());
-
-        if (fileInfo.suffix() != QLatin1String("qml"))
-            continue;
-        else if (fileInfo.absolutePath() != currentFilePath) // ### FIXME includ `imported' components
-            continue;
-
-        const QString typeName = fileInfo.baseName();
-        if (typeName.isEmpty())
-            continue;
-
-        if (typeName.at(0).isUpper()) {
-            TextEditor::CompletionItem item(this);
-            item.text = typeName;
-            item.icon = typeIcon;
-            m_completions.append(item);
-        }
-    }
-
     QChar previousChar;
     if (m_startPosition > 0)
         previousChar = editor->characterAt(m_startPosition - 1);
 
     if (previousChar.isSpace() || previousChar.isNull()) {
-        // ### FIXME
+        // Add the visible components to the completion box.
+        foreach (QmlJS::Document::Ptr doc, snapshot) {
+            const QFileInfo fileInfo(doc->fileName());
+
+            if (fileInfo.suffix() != QLatin1String("qml"))
+                continue;
+            else if (fileInfo.absolutePath() != currentFilePath) // ### FIXME includ `imported' components
+                continue;
+
+            const QString typeName = fileInfo.baseName();
+            if (typeName.isEmpty())
+                continue;
+
+            if (typeName.at(0).isUpper()) {
+                TextEditor::CompletionItem item(this);
+                item.text = typeName;
+                item.icon = typeIcon;
+                m_completions.append(item);
+            }
+        }
+
+        // Add the visible IDs to the completion box
         const QIcon idIcon = iconForColor(Qt::darkGray);
         QStringList ids = qmlDocument->ids().keys();
         foreach (const QString &id, ids) {
@@ -287,54 +330,53 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         }
     }
 
-#if 0
-    // FIXME: this completion strategy is not going to work when the document was never parsed correctly.
-    if (qmlDocument->qmlProgram() != 0) {
-        const QIcon otherIcon = iconForColor(Qt::darkCyan);
+    if (previousChar == QLatin1Char('.')) {
+        const int endOfExpression = m_startPosition - 1;
+        int startOfExpression = endOfExpression - 2;
 
-        // qDebug() << "*** program:" << program;
-        QmlExpressionUnderCursor expressionUnderCursor;
-        QTextCursor cursor(edit->document());
-        cursor.setPosition(pos);
-        expressionUnderCursor(cursor, qmlDocument);
+        while (startOfExpression >= 0) {
+            const QChar ch = editor->characterAt(startOfExpression);
 
-        QmlLookupContext context(expressionUnderCursor.expressionScopes(), qmlDocument, m_modelManager->snapshot(), m_typeSystem);
-        QmlResolveExpression resolver(context);
+            if (ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.'))
+                --startOfExpression;
+            else
+                break;
+        }
+        ++startOfExpression;
 
-        QmlJS::AST::Node *expr = expressionUnderCursor.expressionNode();
+        const QString expression = m_editor->textAt(startOfExpression, endOfExpression - startOfExpression);
+        //qDebug() << "expression:" << expression;
 
-        QmlJS::Interpreter::Engine engine;
-        Evaluate evaluate(&engine);
-        if (const QmlJS::Interpreter::Value *value = evaluate(expr)) {
-            if (const QmlJS::Interpreter::ObjectValue *object = value->asObjectValue()) {
-                for (QmlJS::Interpreter::ObjectValue::MemberIterator it = object->firstMember(); it != object->lastMember(); ++it) {
-                    TextEditor::CompletionItem item(this);
-                    item.text = it.key();
-                    item.icon = otherIcon;
-                    m_completions.append(item);
-                }
-                return pos;
-            }
-        }
+        QmlJS::Document::Ptr exprDoc = QmlJS::Document::create(QLatin1String("<expression>"));
+        exprDoc->setSource(expression);
+        exprDoc->parseExpression();
+
+        if (exprDoc->ast()) {
+            Interpreter::Engine interp;
+            Evaluate evaluate(&interp);
+
+            const Interpreter::Value *value = interp.convertToObject(evaluate(exprDoc->ast()));
+            //qDebug() << "type:" << interp.typeId(value);
 
-        // qDebug()<<"*** expression under cursor:"<<expressionUnderCursor.expressionNode();
-        const QList<QmlJS::Symbol*> symbols = resolver.visibleSymbols(expressionUnderCursor.expressionNode());
-        // qDebug()<<"***"<<symbols.size()<<"visible symbols";
+            const QIcon symbolIcon = iconForColor(Qt::darkCyan);
 
-        foreach (QmlJS::Symbol *symbol, symbols) {
-            if (symbol->isIdSymbol())
-                continue; // nothing to do here.
+            EnumerateProperties enumerateProperties;
+            QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(value));
+            while (it.hasNext()) {
+                it.next();
 
-            const QString word = symbol->name();
-            if (! word.isEmpty()) {
                 TextEditor::CompletionItem item(this);
-                item.text = word;
-                item.icon = otherIcon;
+                item.text = it.key();
+                item.icon = symbolIcon;
                 m_completions.append(item);
             }
         }
+
+        if (! m_completions.isEmpty())
+            return m_startPosition;
+
+        return -1;
     }
-#endif
 
     if (previousChar.isNull()
             || previousChar.isSpace()