OSDN Git Service

QmlJS: Completion for enums.
authorChristian Kamm <christian.d.kamm@nokia.com>
Thu, 29 Apr 2010 13:52:17 +0000 (15:52 +0200)
committerChristian Kamm <christian.d.kamm@nokia.com>
Thu, 29 Apr 2010 14:05:25 +0000 (16:05 +0200)
Done-with: Erik Verbruggen

src/libs/qmljs/qmljs-lib.pri
src/libs/qmljs/qmljscompletioncontextfinder.cpp
src/libs/qmljs/qmljscompletioncontextfinder.h
src/libs/qmljs/qmljsinterpreter.cpp
src/libs/qmljs/qmljsinterpreter.h
src/plugins/qmljseditor/qmljscodecompletion.cpp
src/plugins/qmljseditor/qmljshoverhandler.cpp

index 5a9b622..c74ad34 100644 (file)
@@ -34,6 +34,9 @@ SOURCES += \
     $$PWD/qmljslineinfo.cpp \
     $$PWD/qmljscompletioncontextfinder.cpp
 
+OTHER_FILES += \
+    $$PWD/parser/qmljs.g
+
 contains(QT, gui) {
     SOURCES += $$PWD/qmljsindenter.cpp
     HEADERS += $$PWD/qmljsindenter.h
index c1abc51..0fd7ab8 100644 (file)
@@ -92,6 +92,8 @@ void CompletionContextFinder::checkBinding()
     int colonCount = 0;
     bool delimiterFound = false;
     bool firstToken = true;
+    bool identifierExpected = false;
+    bool dotExpected = false;
     while (!delimiterFound) {
         if (i < 0) {
             if (!readLine())
@@ -113,14 +115,36 @@ void CompletionContextFinder::checkBinding()
 
         case Token::Colon:
             ++colonCount;
+            identifierExpected = true;
+            dotExpected = false;
+            m_bindingPropertyName.clear();
             break;
 
-        case Token::Identifier:
-            if (firstToken && yyLine->midRef(token.begin(), token.length) == QLatin1String("on"))
+        case Token::Identifier: {
+            QStringRef tokenString = yyLine->midRef(token.begin(), token.length);
+            if (firstToken && tokenString == QLatin1String("on")) {
                 m_behaviorBinding = true;
+            } else if (identifierExpected) {
+                m_bindingPropertyName.prepend(tokenString.toString());
+                identifierExpected = false;
+                dotExpected = true;
+            } else {
+                dotExpected = false;
+            }
+        } break;
+
+        case Token::Dot:
+            if (dotExpected) {
+                dotExpected = false;
+                identifierExpected = true;
+            } else {
+                identifierExpected = false;
+            }
             break;
 
         default:
+            dotExpected = false;
+            identifierExpected = false;
             break;
         }
 
@@ -150,7 +174,12 @@ bool CompletionContextFinder::isInLhsOfBinding() const
 
 bool CompletionContextFinder::isInRhsOfBinding() const
 {
-    return isInQmlContext() && m_colonCount == 1;
+    return isInQmlContext() && m_colonCount >= 1;
+}
+
+QStringList CompletionContextFinder::bindingPropertyName() const
+{
+    return m_bindingPropertyName;
 }
 
 /*!
index 7fba2d9..ec2baf7 100644 (file)
@@ -24,6 +24,7 @@ public:
     bool isInRhsOfBinding() const;
 
     bool isAfterOnInLhsOfBinding() const;
+    QStringList bindingPropertyName() const;
 
 private:
     int findOpeningBrace(int startTokenIndex);
@@ -32,7 +33,7 @@ private:
 
     QTextCursor m_cursor;
     QStringList m_qmlObjectTypeName;
-
+    QStringList m_bindingPropertyName;
     int m_startTokenIndex;
     int m_colonCount;
     bool m_behaviorBinding;
index b3c4a30..564482d 100644 (file)
@@ -123,6 +123,9 @@ public:
 
     int keyCount() const
     { return m_keys.size(); }
+
+    QStringList keys() const
+    { return m_keys; }
 };
 
 class FakeMetaMethod {
@@ -854,6 +857,13 @@ const Value *QmlObjectValue::propertyValue(const FakeMetaProperty &prop) const
         value = object;
     }
 
+    // might be an enum
+    int enumIndex = _metaObject->enumeratorIndex(prop.typeName());
+    if (enumIndex != -1) {
+        const FakeMetaEnum &metaEnum = _metaObject->enumerator(enumIndex);
+        value = new QmlEnumValue(metaEnum, engine());
+    }
+
     return value;
 }
 
@@ -916,6 +926,28 @@ bool QmlObjectValue::isDerivedFrom(const FakeMetaObject *base) const
     return false;
 }
 
+QmlEnumValue::QmlEnumValue(const FakeMetaEnum &metaEnum, Engine *engine)
+    : NumberValue(),
+      _metaEnum(new FakeMetaEnum(metaEnum))
+{
+    engine->registerValue(this);
+}
+
+QmlEnumValue::~QmlEnumValue()
+{
+    delete _metaEnum;
+}
+
+QString QmlEnumValue::name() const
+{
+    return _metaEnum->name();
+}
+
+QStringList QmlEnumValue::keys() const
+{
+    return _metaEnum->keys();
+}
+
 namespace {
 
 ////////////////////////////////////////////////////////////////////////////////
index 252ff3a..01d65af 100644 (file)
@@ -69,6 +69,7 @@ typedef QList<const Value *> ValueList;
 class FakeMetaObject;
 class FakeMetaMethod;
 class FakeMetaProperty;
+class FakeMetaEnum;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Value visitor
@@ -435,6 +436,19 @@ private:
     mutable QHash<int, const Value *> _metaSignature;
 };
 
+class QMLJS_EXPORT QmlEnumValue: public NumberValue
+{
+public:
+    QmlEnumValue(const FakeMetaEnum &metaEnum, Engine *engine);
+    virtual ~QmlEnumValue();
+
+    QString name() const;
+    QStringList keys() const;
+
+private:
+    FakeMetaEnum *_metaEnum;
+};
+
 class QMLJS_EXPORT Activation
 {
 public:
index 4298eed..a5a5d15 100644 (file)
@@ -157,12 +157,14 @@ class EnumerateProperties: private Interpreter::MemberProcessor
     QSet<const Interpreter::ObjectValue *> _processed;
     QHash<QString, const Interpreter::Value *> _properties;
     bool _globalCompletion;
+    bool _enumerateGeneratedSlots;
     Interpreter::Context *_context;
     const Interpreter::ObjectValue *_currentObject;
 
 public:
     EnumerateProperties(Interpreter::Context *context)
         : _globalCompletion(false),
+          _enumerateGeneratedSlots(false),
           _context(context),
           _currentObject(0)
     {
@@ -173,6 +175,11 @@ public:
         _globalCompletion = globalCompletion;
     }
 
+    void setEnumerateGeneratedSlots(bool enumerate)
+    {
+        _enumerateGeneratedSlots = enumerate;
+    }
+
     QHash<QString, const Interpreter::Value *> operator ()(const Interpreter::Value *value)
     {
         _processed.clear();
@@ -231,7 +238,7 @@ private:
 
     virtual bool processGeneratedSlot(const QString &name, const Interpreter::Value *value)
     {
-        if (_globalCompletion || (_currentObject && _currentObject->className().endsWith(QLatin1String("Keys")))) {
+        if (_enumerateGeneratedSlots || (_currentObject && _currentObject->className().endsWith(QLatin1String("Keys")))) {
             // ### FIXME: add support for attached properties.
             insertProperty(name, value);
         }
@@ -640,6 +647,10 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) ||
             (completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) {
 
+        bool doGlobalCompletion = true;
+        bool doQmlKeywordCompletion = true;
+        bool doJsKeywordCompletion = true;
+
         QTextCursor startPositionCursor(edit->document());
         startPositionCursor.setPosition(m_startPosition);
         CompletionContextFinder contextFinder(startPositionCursor);
@@ -648,11 +659,13 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         if (contextFinder.isInQmlContext())
              qmlScopeType = context.lookupType(document.data(), contextFinder.qmlObjectTypeName());
 
-        bool doGlobalCompletion = true;
         if (contextFinder.isInLhsOfBinding() && qmlScopeType) {
             doGlobalCompletion = false;
+            doJsKeywordCompletion = false;
+
             EnumerateProperties enumerateProperties(&context);
             enumerateProperties.setGlobalCompletion(true);
+            enumerateProperties.setEnumerateGeneratedSlots(true);
 
             QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(qmlScopeType));
             while (it.hasNext()) {
@@ -660,9 +673,6 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 
                 TextEditor::CompletionItem item(this);
                 item.text = it.key();
-                item.data = it.key();
-                if (!contextFinder.isAfterOnInLhsOfBinding())
-                    item.data = QString(item.data.toString() + QLatin1String(": "));
                 item.icon = symbolIcon;
                 m_completions.append(item);
             }
@@ -678,6 +688,35 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
             }
         }
 
+        if (contextFinder.isInRhsOfBinding() && qmlScopeType) {
+            doQmlKeywordCompletion = false;
+            qDebug() << "property name: " << contextFinder.bindingPropertyName();
+
+            if (!contextFinder.bindingPropertyName().isEmpty()) {
+                const Interpreter::Value *value = qmlScopeType;
+                foreach (const QString &name, contextFinder.bindingPropertyName()) {
+                    if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) {
+                        value = objectValue->property(name, &context);
+                        if (!value)
+                            break;
+                    } else {
+                        value = 0;
+                        break;
+                    }
+                }
+
+                if (const Interpreter::QmlEnumValue *enumValue = dynamic_cast<const Interpreter::QmlEnumValue *>(value)) {
+                    foreach (const QString &key, enumValue->keys()) {
+                        TextEditor::CompletionItem item(this);
+                        item.text = key;
+                        item.data = QString("\"%1\"").arg(key);
+                        item.icon = symbolIcon;
+                        m_completions.append(item);
+                    }
+                }
+            }
+        }
+
         if (doGlobalCompletion) {
             // It's a global completion.
             EnumerateProperties enumerateProperties(&context);
@@ -691,7 +730,9 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
                 item.icon = symbolIcon;
                 m_completions.append(item);
             }
+        }
 
+        if (doJsKeywordCompletion) {
             // add js keywords
             foreach (const QString &word, Scanner::keywords()) {
                 TextEditor::CompletionItem item(this);
@@ -702,7 +743,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         }
 
         // add qml extra words
-        if (isQmlFile) {
+        if (doQmlKeywordCompletion && isQmlFile) {
             static QStringList qmlWords;
 
             if (qmlWords.isEmpty()) {
@@ -718,6 +759,22 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
                 item.icon = keywordIcon;
                 m_completions.append(item);
             }
+
+            if (!doJsKeywordCompletion) {
+                {
+                    TextEditor::CompletionItem item(this);
+                    item.text = QLatin1String("default");
+                    item.icon = keywordIcon;
+                    m_completions.append(item);
+                }
+
+                {
+                    TextEditor::CompletionItem item(this);
+                    item.text = QLatin1String("function");
+                    item.icon = keywordIcon;
+                    m_completions.append(item);
+                }
+            }
         }
     }
 
index 9155957..682ec98 100644 (file)
@@ -227,6 +227,8 @@ QString HoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS:
 
         if (! baseClasses->isEmpty())
             return baseClasses->first();
+    } else if (const Interpreter::QmlEnumValue *enumValue = dynamic_cast<const Interpreter::QmlEnumValue *>(value)) {
+        return enumValue->name();
     }
 
     QString typeId = context->engine()->typeId(value);