From 3b8b6e89cc62cdf781bd85ac92407a0af843ac55 Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Mon, 7 Jun 2010 17:33:44 +0200
Subject: [PATCH] QmlJS: Autocomplete dot and colon for properties.

Reviewed-by: Roberto Raggi
---
 .../qml-builtin-types.xml                     | 67 +++++++++----------
 src/libs/qmljs/qmljsinterpreter.cpp           | 17 +++++
 src/libs/qmljs/qmljsinterpreter.h             |  3 +
 .../qmljseditor/qmljscodecompletion.cpp       | 48 ++++++++++---
 src/plugins/qmljseditor/qmljscodecompletion.h |  3 +
 src/tools/qml/qmldump/main.cpp                | 27 ++------
 6 files changed, 98 insertions(+), 67 deletions(-)

diff --git a/share/qtcreator/qml-type-descriptions/qml-builtin-types.xml b/share/qtcreator/qml-type-descriptions/qml-builtin-types.xml
index 3f917c54797..671df8edb26 100644
--- a/share/qtcreator/qml-type-descriptions/qml-builtin-types.xml
+++ b/share/qtcreator/qml-type-descriptions/qml-builtin-types.xml
@@ -217,15 +217,6 @@
         <property name="heightRatio" type="qreal"/>
         <signal name="pageChanged"/>
     </type>
-    <type name="QDeclarativeGraphicsWidget" defaultProperty="children" extends="Qt.QGraphicsWidget">
-        <property name="anchors" type="QDeclarativeAnchors"/>
-        <property name="left" type="QDeclarativeAnchorLine"/>
-        <property name="right" type="QDeclarativeAnchorLine"/>
-        <property name="horizontalCenter" type="QDeclarativeAnchorLine"/>
-        <property name="top" type="QDeclarativeAnchorLine"/>
-        <property name="bottom" type="QDeclarativeAnchorLine"/>
-        <property name="verticalCenter" type="QDeclarativeAnchorLine"/>
-    </type>
     <type name="QDeclarativeImageBase" defaultProperty="data" extends="Qt.Item">
         <enum name="Status">
             <enumerator name="Null" value="0"/>
@@ -1456,12 +1447,12 @@
     <type name="Qt.AnchorAnimation" version="4.7" extends="Qt.Animation">
         <property name="targets" type="Qt.Item" isList="true"/>
         <property name="duration" type="int"/>
-        <property name="easing" type="Qt.Easing"/>
+        <property name="easing" type="QEasingCurve"/>
         <signal name="durationChanged">
             <param type="int"/>
         </signal>
         <signal name="easingChanged">
-            <param type="Qt.Easing"/>
+            <param type="QEasingCurve"/>
         </signal>
     </type>
     <type name="Qt.AnchorChanges" version="4.7" extends="QDeclarativeStateOperation">
@@ -2405,7 +2396,7 @@
         <property name="duration" type="int"/>
         <property name="from" type="QVariant"/>
         <property name="to" type="QVariant"/>
-        <property name="easing" type="Qt.Easing"/>
+        <property name="easing" type="QEasingCurve"/>
         <property name="target" type="Qt.QtObject"/>
         <property name="property" type="string"/>
         <property name="properties" type="string"/>
@@ -2421,7 +2412,7 @@
             <param type="QVariant"/>
         </signal>
         <signal name="easingChanged">
-            <param type="Qt.Easing"/>
+            <param type="QEasingCurve"/>
         </signal>
         <signal name="propertiesChanged">
             <param type="string"/>
@@ -2436,24 +2427,14 @@
         <property name="restoreEntryValues" type="bool"/>
         <property name="explicit" type="bool"/>
     </type>
-    <type name="Qt.QGraphicsWidget" version="4.7" defaultProperty="children" extends="QGraphicsObject">
-        <property name="palette" type="QPalette"/>
-        <property name="font" type="QFont"/>
-        <property name="layoutDirection" type="Qt.LayoutDirection"/>
-        <property name="size" type="QSizeF"/>
-        <property name="minimumSize" type="QSizeF"/>
-        <property name="preferredSize" type="QSizeF"/>
-        <property name="maximumSize" type="QSizeF"/>
-        <property name="sizePolicy" type="QSizePolicy"/>
-        <property name="focusPolicy" type="Qt.FocusPolicy"/>
-        <property name="windowFlags" type="Qt.WindowFlags"/>
-        <property name="windowTitle" type="string"/>
-        <property name="geometry" type="QRectF"/>
-        <property name="autoFillBackground" type="bool"/>
-        <property name="layout" type="QGraphicsLayout"/>
-        <signal name="geometryChanged"/>
-        <signal name="layoutChanged"/>
-        <method name="close" type="bool"/>
+    <type name="Qt.QGraphicsWidget" version="4.7" defaultProperty="children" extends="__extended__.Qt.QGraphicsWidget">
+        <property name="anchors" type="QDeclarativeAnchors"/>
+        <property name="left" type="QDeclarativeAnchorLine"/>
+        <property name="right" type="QDeclarativeAnchorLine"/>
+        <property name="horizontalCenter" type="QDeclarativeAnchorLine"/>
+        <property name="top" type="QDeclarativeAnchorLine"/>
+        <property name="bottom" type="QDeclarativeAnchorLine"/>
+        <property name="verticalCenter" type="QDeclarativeAnchorLine"/>
     </type>
     <type name="Qt.QtObject" version="4.7">
         <property name="objectName" type="string"/>
@@ -3092,6 +3073,25 @@
             <param name="count" type="int"/>
         </method>
     </type>
+    <type name="__extended__.Qt.QGraphicsWidget" version="4.7" defaultProperty="children" extends="QGraphicsObject">
+        <property name="palette" type="QPalette"/>
+        <property name="font" type="QFont"/>
+        <property name="layoutDirection" type="Qt.LayoutDirection"/>
+        <property name="size" type="QSizeF"/>
+        <property name="minimumSize" type="QSizeF"/>
+        <property name="preferredSize" type="QSizeF"/>
+        <property name="maximumSize" type="QSizeF"/>
+        <property name="sizePolicy" type="QSizePolicy"/>
+        <property name="focusPolicy" type="Qt.FocusPolicy"/>
+        <property name="windowFlags" type="Qt.WindowFlags"/>
+        <property name="windowTitle" type="string"/>
+        <property name="geometry" type="QRectF"/>
+        <property name="autoFillBackground" type="bool"/>
+        <property name="layout" type="QGraphicsLayout"/>
+        <signal name="geometryChanged"/>
+        <signal name="layoutChanged"/>
+        <method name="close" type="bool"/>
+    </type>
     <type name="org.webkit.WebView" version="1.0" defaultProperty="data" extends="Qt.Item">
         <enum name="Status">
             <enumerator name="Null" value="0"/>
@@ -3167,8 +3167,5 @@
             <param name="maxzoom" type="qreal"/>
         </method>
     </type>
-    <type name="Script">
-        <property name="script" type="string"/>
-        <property name="source" type="QUrl"/>
-    </type>
+    <type name="QEasingCurve" extends="Qt.Easing"/>
 </module>
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index e6a752a15dc..3b50b9036d3 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -911,6 +911,23 @@ bool QmlObjectValue::enumContainsKey(const QString &enumName, const QString &enu
     return false;
 }
 
+// Returns true if this object is in a package or if there is an object that
+// has this one in its prototype chain and is itself in a package.
+bool QmlObjectValue::hasChildInPackage() const
+{
+    if (!packageName().isEmpty())
+        return true;
+    foreach (const FakeMetaObject *other, MetaTypeSystem::_metaObjects) {
+        if (other->packageName().isEmpty())
+            continue;
+        for (const FakeMetaObject *iter = other; iter; iter = iter->superClass()) {
+            if (iter == _metaObject) // this object is a parent of other
+                return true;
+        }
+    }
+    return false;
+}
+
 bool QmlObjectValue::isDerivedFrom(const FakeMetaObject *base) const
 {
     for (const FakeMetaObject *iter = _metaObject; iter; iter = iter->superClass()) {
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 50254fda99c..e16f45a295c 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -404,6 +404,7 @@ public:
     bool isListProperty(const QString &name) const;
     bool isEnum(const QString &typeName) const;
     bool enumContainsKey(const QString &enumName, const QString &enumKeyName) const;
+    bool hasChildInPackage() const;
 
 protected:
     const Value *findOrCreateSignature(int index, const FakeMetaMethod &method, QString *methodName) const;
@@ -531,6 +532,8 @@ public:
 
 private:
     QHash<QString, QList<QmlObjectValue *> > _importedTypes;
+
+    friend class QmlObjectValue;
 };
 
 class ConvertToNumber: protected ValueVisitor // ECMAScript ToInt()
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp
index 556d0a903c6..7981fa1ed8f 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp
@@ -622,6 +622,31 @@ void CodeCompletion::addCompletions(const QStringList &newCompletions,
     }
 }
 
+void CodeCompletion::addCompletionsPropertyLhs(
+        const QHash<QString, const Interpreter::Value *> &newCompletions,
+        const QIcon &icon)
+{
+    QHashIterator<QString, const Interpreter::Value *> it(newCompletions);
+    while (it.hasNext()) {
+        it.next();
+
+        TextEditor::CompletionItem item(this);
+        item.text = it.key();
+        if (const Interpreter::QmlObjectValue *qmlValue = dynamic_cast<const Interpreter::QmlObjectValue *>(it.value())) {
+            // to distinguish "anchors." from "gradient:" we check if the right hand side
+            // type is instantiatable or is the prototype of an instantiatable object
+            if (qmlValue->hasChildInPackage())
+                item.text.append(QLatin1String(": "));
+            else
+                item.text.append(QLatin1Char('.'));
+        } else {
+            item.text.append(QLatin1String(": "));
+        }
+        item.icon = icon;
+        m_completions.append(item);
+    }
+}
+
 int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 {
     m_editor = editor;
@@ -665,6 +690,14 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     if (m_startPosition > 0)
         completionOperator = editor->characterAt(m_startPosition - 1);
 
+    QTextCursor startPositionCursor(edit->document());
+    startPositionCursor.setPosition(m_startPosition);
+    CompletionContextFinder contextFinder(startPositionCursor);
+
+    const Interpreter::ObjectValue *qmlScopeType = 0;
+    if (contextFinder.isInQmlContext())
+         qmlScopeType = context.lookupType(document.data(), contextFinder.qmlObjectTypeName());
+
     if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) ||
             (completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) {
 
@@ -672,14 +705,6 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         bool doQmlKeywordCompletion = true;
         bool doJsKeywordCompletion = true;
 
-        QTextCursor startPositionCursor(edit->document());
-        startPositionCursor.setPosition(m_startPosition);
-        CompletionContextFinder contextFinder(startPositionCursor);
-
-        const Interpreter::ObjectValue *qmlScopeType = 0;
-        if (contextFinder.isInQmlContext())
-             qmlScopeType = context.lookupType(document.data(), contextFinder.qmlObjectTypeName());
-
         if (contextFinder.isInLhsOfBinding() && qmlScopeType) {
             doGlobalCompletion = false;
             doJsKeywordCompletion = false;
@@ -688,7 +713,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
             enumerateProperties.setGlobalCompletion(true);
             enumerateProperties.setEnumerateGeneratedSlots(true);
 
-            addCompletions(enumerateProperties(qmlScopeType), symbolIcon);
+            addCompletionsPropertyLhs(enumerateProperties(qmlScopeType), symbolIcon);
             addCompletions(enumerateProperties(context.scopeChain().qmlTypes), symbolIcon);
 
             if (ScopeBuilder::isPropertyChangesObject(&context, qmlScopeType)
@@ -776,7 +801,10 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 
             if (value && completionOperator == QLatin1Char('.')) { // member completion
                 EnumerateProperties enumerateProperties(&context);
-                addCompletions(enumerateProperties(value), symbolIcon);
+                if (contextFinder.isInLhsOfBinding() && qmlScopeType)
+                    addCompletionsPropertyLhs(enumerateProperties(value), symbolIcon);
+                else
+                    addCompletions(enumerateProperties(value), symbolIcon);
             } else if (value && completionOperator == QLatin1Char('(') && m_startPosition == editor->position()) {
                 // function completion
                 if (const Interpreter::FunctionValue *f = value->asFunctionValue()) {
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.h b/src/plugins/qmljseditor/qmljscodecompletion.h
index 6046f6f7d98..6df5763be0c 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.h
+++ b/src/plugins/qmljseditor/qmljscodecompletion.h
@@ -84,6 +84,9 @@ private:
                         const QIcon &icon);
     void addCompletions(const QStringList &newCompletions,
                         const QIcon &icon);
+    void addCompletionsPropertyLhs(
+            const QHash<QString, const QmlJS::Interpreter::Value *> &newCompletions,
+            const QIcon &icon);
 
     ModelManagerInterface *m_modelManager;
     TextEditor::ITextEditable *m_editor;
diff --git a/src/tools/qml/qmldump/main.cpp b/src/tools/qml/qmldump/main.cpp
index 4ff4114cf06..8a452057164 100644
--- a/src/tools/qml/qmldump/main.cpp
+++ b/src/tools/qml/qmldump/main.cpp
@@ -217,33 +217,16 @@ void dump(const QMetaObject *meta, QXmlStreamWriter *xml)
     xml->writeEndElement();
 }
 
-void writeScriptElement(QXmlStreamWriter *xml)
+void writeEasingCurve(QXmlStreamWriter *xml)
 {
     xml->writeStartElement("type");
     {
         QXmlStreamAttributes attributes;
-        attributes.append(QXmlStreamAttribute("name", "Script"));
+        attributes.append(QXmlStreamAttribute("name", "QEasingCurve"));
+        attributes.append(QXmlStreamAttribute("extends", "Qt.Easing"));
         xml->writeAttributes(attributes);
     }
 
-    xml->writeStartElement("property");
-    {
-        QXmlStreamAttributes attributes;
-        attributes.append(QXmlStreamAttribute("name", "script"));
-        attributes.append(QXmlStreamAttribute("type", "string"));
-        xml->writeAttributes(attributes);
-    }
-    xml->writeEndElement();
-
-    xml->writeStartElement("property");
-    {
-        QXmlStreamAttributes attributes;
-        attributes.append(QXmlStreamAttribute("name", "source"));
-        attributes.append(QXmlStreamAttribute("type", "QUrl"));
-        xml->writeAttributes(attributes);
-    }
-    xml->writeEndElement();
-
     xml->writeEndElement();
 }
 
@@ -268,7 +251,6 @@ int main(int argc, char *argv[])
     }
 
     cppToQml.insert("QString", "string");
-    cppToQml.insert("QEasingCurve", "Qt.Easing");
     cppToQml.insert("QDeclarativeEasingValueType::Type", "Type");
 
     QSet<const QMetaObject *> metas;
@@ -340,7 +322,8 @@ int main(int argc, char *argv[])
         dump(meta, &xml);
     }
 
-    writeScriptElement(&xml);
+    // define QEasingCurve as an extension of Qt.Easing
+    writeEasingCurve(&xml);
 
     xml.writeEndElement();
     xml.writeEndDocument();
-- 
GitLab