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