From 2512a684d5f41d1b679f840a90862b48c72599f1 Mon Sep 17 00:00:00 2001
From: Roberto Raggi <roberto.raggi@nokia.com>
Date: Tue, 26 Jan 2010 10:50:30 +0100
Subject: [PATCH] Show the argument names from the method's signature.

---
 src/libs/qmljs/qmljsinterpreter.cpp           | 134 ++++++++++++++----
 src/libs/qmljs/qmljsinterpreter.h             |   4 +
 src/plugins/qmljseditor/qmlcodecompletion.cpp |  29 +++-
 3 files changed, 136 insertions(+), 31 deletions(-)

diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index 65b311bf40c..78efcf634da 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -47,11 +47,58 @@ namespace {
 
 #ifndef NO_DECLARATIVE_BACKEND
 
+class MetaFunction: public FunctionValue
+{
+    QMetaMethod _method;
+
+public:
+    MetaFunction(const QMetaMethod &method, Engine *engine)
+        : FunctionValue(engine), _method(method)
+    {
+        engine->registerObject(this);
+    }
+
+    virtual const Value *returnValue() const
+    {
+        return engine()->undefinedValue();
+    }
+
+    virtual int argumentCount() const
+    {
+        return _method.parameterNames().size();
+    }
+
+    virtual const Value *argument(int) const
+    {
+        return engine()->undefinedValue();
+    }
+
+    virtual QString argumentName(int index) const
+    {
+        if (index < _method.parameterNames().size())
+            return _method.parameterNames().at(index);
+
+        return FunctionValue::argumentName(index);
+    }
+
+    virtual bool isVariadic() const
+    {
+        return false;
+    }
+
+    virtual const Value *invoke(const Activation *) const
+    {
+        return engine()->undefinedValue();
+    }
+};
+
 class QmlObjectValue: public ObjectValue
 {
 public:
     QmlObjectValue(const QMetaObject *metaObject, Engine *engine)
-        : ObjectValue(engine), _metaObject(metaObject) {}
+        : ObjectValue(engine), _metaObject(metaObject)
+    {
+    }
 
     virtual ~QmlObjectValue() {}
 
@@ -64,6 +111,28 @@ public:
                 return propertyValue(prop);
         }
 
+        for (int index = 0; index < _metaObject->methodCount(); ++index) {
+            QMetaMethod method = _metaObject->method(index);
+
+            const QString signature = QString::fromUtf8(method.signature());
+
+            const int indexOfParen = signature.indexOf(QLatin1Char('('));
+            if (indexOfParen == -1)
+                continue; // skip it, invalid signature.
+
+            const QString methodName = signature.left(indexOfParen);
+
+            if (methodName != name) {
+                continue;
+
+            } else if (method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) {
+                return new MetaFunction(method, engine());
+
+            } else if (method.methodType() == QMetaMethod::Signal && method.access() != QMetaMethod::Private) {
+                return new MetaFunction(method, engine());
+            }
+        }
+
         return ObjectValue::lookupMember(name);
     }
 
@@ -76,9 +145,9 @@ public:
         }
 
         for (int index = 0; index < _metaObject->methodCount(); ++index) {
-            QMetaMethod meth = _metaObject->method(index);
+            QMetaMethod method = _metaObject->method(index);
 
-            const QString signature = QString::fromUtf8(meth.signature());
+            const QString signature = QString::fromUtf8(method.signature());
 
             const int indexOfParen = signature.indexOf(QLatin1Char('('));
             if (indexOfParen == -1)
@@ -86,12 +155,12 @@ public:
 
             const QString methodName = signature.left(indexOfParen);
 
-            if (meth.methodType() == QMetaMethod::Slot && meth.access() == QMetaMethod::Public) {
+            if (method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) {
                 processor->processSlot(methodName, engine()->undefinedValue());
 
-            } else if (meth.methodType() == QMetaMethod::Signal && meth.access() != QMetaMethod::Private) {
+            } else if (method.methodType() == QMetaMethod::Signal && method.access() != QMetaMethod::Private) {
                 // process the signal
-                processor->processSignal(methodName, engine()->undefinedValue()); // ### FIXME: assign a decent type to the signal
+                processor->processSignal(methodName, engine()->undefinedValue());
 
                 QString slotName;
                 slotName += QLatin1String("on");
@@ -99,7 +168,7 @@ public:
                 slotName += methodName.midRef(1);
 
                 // process the generated slot
-                processor->processGeneratedSlot(slotName, engine()->undefinedValue()); // ### FIXME: assign a decent type to the slot
+                processor->processGeneratedSlot(slotName, engine()->undefinedValue());
             }
         }
 
@@ -803,11 +872,36 @@ const Value *FunctionValue::call(const ObjectValue *thisObject, const ValueList
     return invoke(&activation);
 }
 
+const Value *FunctionValue::returnValue() const
+{
+    return engine()->undefinedValue();
+}
+
 int FunctionValue::argumentCount() const
 {
     return 0;
 }
 
+const Value *FunctionValue::argument(int) const
+{
+    return engine()->undefinedValue();
+}
+
+QString FunctionValue::argumentName(int index) const
+{
+    return QString::fromLatin1("arg%1").arg(index);
+}
+
+bool FunctionValue::isVariadic() const
+{
+    return true;
+}
+
+const Value *FunctionValue::invoke(const Activation *activation) const
+{
+    return activation->thisObject(); // ### FIXME: it should return undefined
+}
+
 const FunctionValue *FunctionValue::asFunctionValue() const
 {
     return this;
@@ -1202,6 +1296,11 @@ const ObjectValue *Engine::mathObject() const
     return _mathObject;
 }
 
+void Engine::registerObject(ObjectValue *object)
+{
+    _objects.append(object);
+}
+
 const Value *Engine::convertToBoolean(const Value *value)
 {
     return _convertToNumber(value);  // ### implement convert to bool
@@ -1423,8 +1522,8 @@ void Engine::initializePrototypes()
     addFunction(_mathObject, "exp", numberValue(), 1);
     addFunction(_mathObject, "floor", numberValue(), 1);
     addFunction(_mathObject, "log", numberValue(), 1);
-    addFunction(_mathObject, "max", numberValue(), 1);
-    addFunction(_mathObject, "min", numberValue(), 1);
+    addFunction(_mathObject, "max", numberValue(), 0);
+    addFunction(_mathObject, "min", numberValue(), 0);
     addFunction(_mathObject, "pow", numberValue(), 2);
     addFunction(_mathObject, "random", numberValue(), 1);
     addFunction(_mathObject, "round", numberValue(), 1);
@@ -1535,23 +1634,6 @@ ObjectValue *Engine::newQmlObject(const QString &name)
 #endif
 }
 
-
-const Value *FunctionValue::invoke(const Activation *activation) const
-{
-    return activation->thisObject(); // ### FIXME: it should return undefined
-}
-
-const Value *FunctionValue::argument(int /*index*/) const
-{
-    return engine()->undefinedValue();
-}
-
-const Value *FunctionValue::returnValue() const
-{
-    return engine()->undefinedValue();
-}
-
-
 ////////////////////////////////////////////////////////////////////////////////
 // convert to number
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 27a34806dd3..f5fb5699e23 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -295,6 +295,8 @@ public:
 
     virtual int argumentCount() const;
     virtual const Value *argument(int index) const;
+    virtual QString argumentName(int index) const;
+    virtual bool isVariadic() const;
 
     virtual const Value *invoke(const Activation *activation) const;
 
@@ -444,6 +446,8 @@ public:
     ObjectValue *globalObject() const;
     const ObjectValue *mathObject() const;
 
+    void registerObject(ObjectValue *object);
+
     // prototypes
     ObjectValue *objectPrototype() const;
     ObjectValue *functionPrototype() const;
diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp
index 45bfbe866a2..a1be0e89ae8 100644
--- a/src/plugins/qmljseditor/qmlcodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp
@@ -487,7 +487,9 @@ class FunctionArgumentWidget : public QLabel
 {
 public:
     FunctionArgumentWidget();
-    void showFunctionHint(const QString &functionName, int minimumArgumentCount, int startPosition);
+    void showFunctionHint(const QString &functionName,
+                          const QStringList &signature,
+                          int startPosition);
 
 protected:
     bool eventFilter(QObject *obj, QEvent *e);
@@ -497,6 +499,7 @@ private:
     void updateHintText();
 
     QString m_functionName;
+    QStringList m_signature;
     int m_minimumArgumentCount;
     int m_startpos;
     int m_currentarg;
@@ -563,13 +566,14 @@ FunctionArgumentWidget::FunctionArgumentWidget():
     qApp->installEventFilter(this);
 }
 
-void FunctionArgumentWidget::showFunctionHint(const QString &functionName, int mininumArgumentCount, int startPosition)
+void FunctionArgumentWidget::showFunctionHint(const QString &functionName, const QStringList &signature, int startPosition)
 {
     if (m_startpos == startPosition)
         return;
 
     m_functionName = functionName;
-    m_minimumArgumentCount = mininumArgumentCount;
+    m_signature = signature;
+    m_minimumArgumentCount = signature.size();
     m_startpos = startPosition;
     m_current = 0;
     m_escapePressed = false;
@@ -663,7 +667,17 @@ void FunctionArgumentWidget::updateHintText()
     QString prettyMethod;
     prettyMethod += QString::fromLatin1("function ");
     prettyMethod += m_functionName;
-    prettyMethod += QLatin1String("(arguments...)");
+    prettyMethod += QLatin1Char('(');
+    for (int i = 0; i < m_minimumArgumentCount; ++i) {
+        if (i != 0)
+            prettyMethod += QLatin1String(", ");
+
+        prettyMethod += QLatin1String("arg");
+
+        if (m_minimumArgumentCount != 1)
+            prettyMethod += QString::number(i + 1);
+    }
+    prettyMethod += QLatin1Char(')');
 
     m_numberLabel->setText(prettyMethod);
 
@@ -970,7 +984,12 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
                     if (!m_functionArgumentWidget)
                         m_functionArgumentWidget = new QmlJSEditor::Internal::FunctionArgumentWidget;
 
-                    m_functionArgumentWidget->showFunctionHint(functionName.trimmed(), f->argumentCount(),
+                    QStringList signature;
+                    for (int i = 0; i < f->argumentCount(); ++i)
+                        signature.append(f->argumentName(i));
+
+                    m_functionArgumentWidget->showFunctionHint(functionName.trimmed(),
+                                                               signature,
                                                                m_startPosition);
                 }
 
-- 
GitLab