QmlJS: Improve completion and hints for functions.

* FunctionValues know about optional arguments (for builtins)
* ASTFunctionValues only report themselves as variadic if they
  use the 'arguments' array.
* Function argument hint shows optional args and variadic.
* Completion automatically adds parentheses.

Change-Id: Ib2598600ff8b1ce8c5de3bcabd24a3e171ff3a57
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
This commit is contained in:
Christian Kamm
2011-11-16 09:56:53 +01:00
parent 8e236db9f5
commit b671baeffb
6 changed files with 223 additions and 74 deletions

View File

@@ -152,7 +152,7 @@ public:
{ {
} }
virtual int argumentCount() const virtual int namedArgumentCount() const
{ {
return _method.parameterNames().size(); return _method.parameterNames().size();
} }
@@ -1219,7 +1219,7 @@ const Value *FunctionValue::returnValue() const
return valueOwner()->unknownValue(); return valueOwner()->unknownValue();
} }
int FunctionValue::argumentCount() const int FunctionValue::namedArgumentCount() const
{ {
return 0; return 0;
} }
@@ -1234,6 +1234,11 @@ QString FunctionValue::argumentName(int index) const
return QString::fromLatin1("arg%1").arg(index + 1); return QString::fromLatin1("arg%1").arg(index + 1);
} }
int FunctionValue::optionalNamedArgumentCount() const
{
return 0;
}
bool FunctionValue::isVariadic() const bool FunctionValue::isVariadic() const
{ {
return true; return true;
@@ -1255,7 +1260,10 @@ void FunctionValue::accept(ValueVisitor *visitor) const
} }
Function::Function(ValueOwner *valueOwner) Function::Function(ValueOwner *valueOwner)
: FunctionValue(valueOwner), _returnValue(0) : FunctionValue(valueOwner)
, _returnValue(0)
, _optionalNamedArgumentCount(0)
, _isVariadic(false)
{ {
setClassName("Function"); setClassName("Function");
} }
@@ -1284,11 +1292,26 @@ void Function::setReturnValue(const Value *returnValue)
_returnValue = returnValue; _returnValue = returnValue;
} }
int Function::argumentCount() const void Function::setVariadic(bool variadic)
{
_isVariadic = variadic;
}
void Function::setOptionalNamedArgumentCount(int count)
{
_optionalNamedArgumentCount = count;
}
int Function::namedArgumentCount() const
{ {
return _arguments.size(); return _arguments.size();
} }
int Function::optionalNamedArgumentCount() const
{
return _optionalNamedArgumentCount;
}
const Value *Function::argument(int index) const const Value *Function::argument(int index) const
{ {
return _arguments.at(index); return _arguments.at(index);
@@ -1309,6 +1332,11 @@ const Value *Function::invoke(const Activation *) const
return _returnValue; return _returnValue;
} }
bool Function::isVariadic() const
{
return _isVariadic;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// typing environment // typing environment
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -1879,13 +1907,47 @@ bool ASTVariableReference::getSourceLocation(QString *fileName, int *line, int *
return true; return true;
} }
namespace {
class UsesArgumentsArray : protected Visitor
{
bool _usesArgumentsArray;
public:
bool operator()(FunctionBody *ast)
{
if (!ast || !ast->elements)
return false;
_usesArgumentsArray = false;
Node::accept(ast->elements, this);
return _usesArgumentsArray;
}
protected:
bool visit(ArrayMemberExpression *ast)
{
if (IdentifierExpression *idExp = cast<IdentifierExpression *>(ast->base)) {
if (idExp->name == QLatin1String("arguments"))
_usesArgumentsArray = true;
}
return true;
}
// don't go into nested functions
bool visit(FunctionBody *) { return false; }
};
} // anonymous namespace
ASTFunctionValue::ASTFunctionValue(FunctionExpression *ast, const Document *doc, ValueOwner *valueOwner) ASTFunctionValue::ASTFunctionValue(FunctionExpression *ast, const Document *doc, ValueOwner *valueOwner)
: FunctionValue(valueOwner), _ast(ast), _doc(doc) : FunctionValue(valueOwner)
, _ast(ast)
, _doc(doc)
{ {
setPrototype(valueOwner->functionPrototype()); setPrototype(valueOwner->functionPrototype());
for (FormalParameterList *it = ast->formals; it; it = it->next) for (FormalParameterList *it = ast->formals; it; it = it->next)
_argumentNames.append(it->name.toString()); _argumentNames.append(it->name.toString());
_isVariadic = UsesArgumentsArray()(ast->body);
} }
ASTFunctionValue::~ASTFunctionValue() ASTFunctionValue::~ASTFunctionValue()
@@ -1897,7 +1959,7 @@ FunctionExpression *ASTFunctionValue::ast() const
return _ast; return _ast;
} }
int ASTFunctionValue::argumentCount() const int ASTFunctionValue::namedArgumentCount() const
{ {
return _argumentNames.size(); return _argumentNames.size();
} }
@@ -1913,6 +1975,11 @@ QString ASTFunctionValue::argumentName(int index) const
return FunctionValue::argumentName(index); return FunctionValue::argumentName(index);
} }
bool ASTFunctionValue::isVariadic() const
{
return _isVariadic;
}
bool ASTFunctionValue::getSourceLocation(QString *fileName, int *line, int *column) const bool ASTFunctionValue::getSourceLocation(QString *fileName, int *line, int *column) const
{ {
*fileName = _doc->fileName(); *fileName = _doc->fileName();
@@ -2021,7 +2088,7 @@ const ASTSignal *ASTSignal::asAstSignal() const
return this; return this;
} }
int ASTSignal::argumentCount() const int ASTSignal::namedArgumentCount() const
{ {
int count = 0; int count = 0;
for (UiParameterList *it = _ast->parameters; it; it = it->next) for (UiParameterList *it = _ast->parameters; it; it = it->next)

View File

@@ -583,11 +583,24 @@ public:
virtual const Value *returnValue() const; virtual const Value *returnValue() const;
virtual int argumentCount() const; // Access to the names of arguments
virtual const Value *argument(int index) const; // Named arguments can be optional (usually known for builtins only)
virtual int namedArgumentCount() const;
virtual QString argumentName(int index) const; virtual QString argumentName(int index) const;
// The number of optional named arguments
// Example: JSON.stringify(value[, replacer[, space]])
// has namedArgumentCount = 3
// and optionalNamedArgumentCount = 2
virtual int optionalNamedArgumentCount() const;
// Whether the function accepts an unlimited number of arguments
// after the named ones. Defaults to false.
// Example: Math.max(...)
virtual bool isVariadic() const; virtual bool isVariadic() const;
virtual const Value *argument(int index) const;
virtual const Value *invoke(const Activation *activation) const; virtual const Value *invoke(const Activation *activation) const;
// Value interface // Value interface
@@ -603,18 +616,24 @@ public:
void addArgument(const Value *argument, const QString &name = QString()); void addArgument(const Value *argument, const QString &name = QString());
void setReturnValue(const Value *returnValue); void setReturnValue(const Value *returnValue);
void setVariadic(bool variadic);
void setOptionalNamedArgumentCount(int count);
// FunctionValue interface // FunctionValue interface
virtual const Value *returnValue() const; virtual const Value *returnValue() const;
virtual int argumentCount() const; virtual int namedArgumentCount() const;
virtual int optionalNamedArgumentCount() const;
virtual const Value *argument(int index) const; virtual const Value *argument(int index) const;
virtual QString argumentName(int index) const; virtual QString argumentName(int index) const;
virtual const Value *invoke(const Activation *activation) const; virtual const Value *invoke(const Activation *activation) const;
virtual bool isVariadic() const;
private: private:
ValueList _arguments; ValueList _arguments;
QStringList _argumentNames; QStringList _argumentNames;
const Value *_returnValue; const Value *_returnValue;
int _optionalNamedArgumentCount;
bool _isVariadic;
}; };
@@ -799,6 +818,7 @@ class QMLJS_EXPORT ASTFunctionValue: public FunctionValue
AST::FunctionExpression *_ast; AST::FunctionExpression *_ast;
const Document *_doc; const Document *_doc;
QList<QString> _argumentNames; QList<QString> _argumentNames;
bool _isVariadic;
public: public:
ASTFunctionValue(AST::FunctionExpression *ast, const Document *doc, ValueOwner *valueOwner); ASTFunctionValue(AST::FunctionExpression *ast, const Document *doc, ValueOwner *valueOwner);
@@ -806,8 +826,9 @@ public:
AST::FunctionExpression *ast() const; AST::FunctionExpression *ast() const;
virtual int argumentCount() const; virtual int namedArgumentCount() const;
virtual QString argumentName(int index) const; virtual QString argumentName(int index) const;
virtual bool isVariadic() const;
virtual bool getSourceLocation(QString *fileName, int *line, int *column) const; virtual bool getSourceLocation(QString *fileName, int *line, int *column) const;
}; };
@@ -851,7 +872,7 @@ public:
const ObjectValue *bodyScope() const { return _bodyScope; } const ObjectValue *bodyScope() const { return _bodyScope; }
// FunctionValue interface // FunctionValue interface
virtual int argumentCount() const; virtual int namedArgumentCount() const;
virtual const Value *argument(int index) const; virtual const Value *argument(int index) const;
virtual QString argumentName(int index) const; virtual QString argumentName(int index) const;

View File

@@ -499,21 +499,20 @@ QString ValueOwner::typeId(const Value *value)
return _typeId(value); return _typeId(value);
} }
Function *ValueOwner::addFunction(ObjectValue *object, const QString &name, const Value *result, int argumentCount) Function *ValueOwner::addFunction(ObjectValue *object, const QString &name, const Value *result, int argumentCount, int optionalCount, bool variadic)
{ {
Function *function = newFunction(); Function *function = addFunction(object, name, argumentCount, optionalCount, variadic);
function->setReturnValue(result); function->setReturnValue(result);
for (int i = 0; i < argumentCount; ++i)
function->addArgument(unknownValue());
object->setMember(name, function);
return function; return function;
} }
Function *ValueOwner::addFunction(ObjectValue *object, const QString &name, int argumentCount) Function *ValueOwner::addFunction(ObjectValue *object, const QString &name, int argumentCount, int optionalCount, bool variadic)
{ {
Function *function = newFunction(); Function *function = newFunction();
for (int i = 0; i < argumentCount; ++i) for (int i = 0; i < argumentCount; ++i)
function->addArgument(unknownValue()); function->addArgument(unknownValue());
function->setVariadic(variadic);
function->setOptionalNamedArgumentCount(optionalCount);
object->setMember(name, function); object->setMember(name, function);
return function; return function;
} }
@@ -538,46 +537,58 @@ void ValueOwner::initializePrototypes()
_objectCtor->setPrototype(_functionPrototype); _objectCtor->setPrototype(_functionPrototype);
_objectCtor->setMember("prototype", _objectPrototype); _objectCtor->setMember("prototype", _objectPrototype);
_objectCtor->setReturnValue(newObject()); _objectCtor->setReturnValue(newObject());
_objectCtor->addArgument(unknownValue(), "value");
_objectCtor->setOptionalNamedArgumentCount(1);
_functionCtor = new FunctionCtor(this); _functionCtor = new FunctionCtor(this);
_functionCtor->setPrototype(_functionPrototype); _functionCtor->setPrototype(_functionPrototype);
_functionCtor->setMember("prototype", _functionPrototype); _functionCtor->setMember("prototype", _functionPrototype);
_functionCtor->setReturnValue(newFunction()); _functionCtor->setReturnValue(newFunction());
_functionCtor->setVariadic(true);
_arrayCtor = new ArrayCtor(this); _arrayCtor = new ArrayCtor(this);
_arrayCtor->setPrototype(_functionPrototype); _arrayCtor->setPrototype(_functionPrototype);
_arrayCtor->setMember("prototype", _arrayPrototype); _arrayCtor->setMember("prototype", _arrayPrototype);
_arrayCtor->setReturnValue(newArray()); _arrayCtor->setReturnValue(newArray());
_arrayCtor->setVariadic(true);
_stringCtor = new StringCtor(this); _stringCtor = new StringCtor(this);
_stringCtor->setPrototype(_functionPrototype); _stringCtor->setPrototype(_functionPrototype);
_stringCtor->setMember("prototype", _stringPrototype); _stringCtor->setMember("prototype", _stringPrototype);
_stringCtor->setReturnValue(stringValue()); _stringCtor->setReturnValue(stringValue());
_stringCtor->addArgument(unknownValue(), "value");
_stringCtor->setOptionalNamedArgumentCount(1);
_booleanCtor = new BooleanCtor(this); _booleanCtor = new BooleanCtor(this);
_booleanCtor->setPrototype(_functionPrototype); _booleanCtor->setPrototype(_functionPrototype);
_booleanCtor->setMember("prototype", _booleanPrototype); _booleanCtor->setMember("prototype", _booleanPrototype);
_booleanCtor->setReturnValue(booleanValue()); _booleanCtor->setReturnValue(booleanValue());
_booleanCtor->addArgument(unknownValue(), "value");
_numberCtor = new NumberCtor(this); _numberCtor = new NumberCtor(this);
_numberCtor->setPrototype(_functionPrototype); _numberCtor->setPrototype(_functionPrototype);
_numberCtor->setMember("prototype", _numberPrototype); _numberCtor->setMember("prototype", _numberPrototype);
_numberCtor->setReturnValue(numberValue()); _numberCtor->setReturnValue(numberValue());
_numberCtor->addArgument(unknownValue(), "value");
_numberCtor->setOptionalNamedArgumentCount(1);
_dateCtor = new DateCtor(this); _dateCtor = new DateCtor(this);
_dateCtor->setPrototype(_functionPrototype); _dateCtor->setPrototype(_functionPrototype);
_dateCtor->setMember("prototype", _datePrototype); _dateCtor->setMember("prototype", _datePrototype);
_dateCtor->setReturnValue(_datePrototype); _dateCtor->setReturnValue(_datePrototype);
_dateCtor->setVariadic(true);
_regexpCtor = new RegExpCtor(this); _regexpCtor = new RegExpCtor(this);
_regexpCtor->setPrototype(_functionPrototype); _regexpCtor->setPrototype(_functionPrototype);
_regexpCtor->setMember("prototype", _regexpPrototype); _regexpCtor->setMember("prototype", _regexpPrototype);
_regexpCtor->setReturnValue(_regexpPrototype); _regexpCtor->setReturnValue(_regexpPrototype);
_regexpCtor->addArgument(unknownValue(), "pattern");
_regexpCtor->addArgument(unknownValue(), "flags");
addFunction(_objectCtor, "getPrototypeOf", 1); addFunction(_objectCtor, "getPrototypeOf", 1);
addFunction(_objectCtor, "getOwnPropertyDescriptor", 2); addFunction(_objectCtor, "getOwnPropertyDescriptor", 2);
addFunction(_objectCtor, "getOwnPropertyNames", newArray(), 1); addFunction(_objectCtor, "getOwnPropertyNames", newArray(), 1);
addFunction(_objectCtor, "create", 1); addFunction(_objectCtor, "create", 1, 1);
addFunction(_objectCtor, "defineProperty", 3); addFunction(_objectCtor, "defineProperty", 3);
addFunction(_objectCtor, "defineProperties", 2); addFunction(_objectCtor, "defineProperties", 2);
addFunction(_objectCtor, "seal", 1); addFunction(_objectCtor, "seal", 1);
@@ -599,8 +610,8 @@ void ValueOwner::initializePrototypes()
_functionPrototype->setMember("constructor", _functionCtor); _functionPrototype->setMember("constructor", _functionCtor);
addFunction(_functionPrototype, "toString", stringValue(), 0); addFunction(_functionPrototype, "toString", stringValue(), 0);
addFunction(_functionPrototype, "apply", 2); addFunction(_functionPrototype, "apply", 2);
addFunction(_functionPrototype, "call", 1); addFunction(_functionPrototype, "call", 1, 0, true);
addFunction(_functionPrototype, "bind", 1); addFunction(_functionPrototype, "bind", 1, 0, true);
// set up the default Array prototype // set up the default Array prototype
addFunction(_arrayCtor, "isArray", booleanValue(), 1); addFunction(_arrayCtor, "isArray", booleanValue(), 1);
@@ -608,35 +619,35 @@ void ValueOwner::initializePrototypes()
_arrayPrototype->setMember("constructor", _arrayCtor); _arrayPrototype->setMember("constructor", _arrayCtor);
addFunction(_arrayPrototype, "toString", stringValue(), 0); addFunction(_arrayPrototype, "toString", stringValue(), 0);
addFunction(_arrayPrototype, "toLocalString", stringValue(), 0); addFunction(_arrayPrototype, "toLocalString", stringValue(), 0);
addFunction(_arrayPrototype, "concat", 0); addFunction(_arrayPrototype, "concat", 0, 0, true);
addFunction(_arrayPrototype, "join", 1); addFunction(_arrayPrototype, "join", 1);
addFunction(_arrayPrototype, "pop", 0); addFunction(_arrayPrototype, "pop", 0);
addFunction(_arrayPrototype, "push", 0); addFunction(_arrayPrototype, "push", 0, 0, true);
addFunction(_arrayPrototype, "reverse", 0); addFunction(_arrayPrototype, "reverse", 0);
addFunction(_arrayPrototype, "shift", 0); addFunction(_arrayPrototype, "shift", 0);
addFunction(_arrayPrototype, "slice", 2); addFunction(_arrayPrototype, "slice", 2);
addFunction(_arrayPrototype, "sort", 1); addFunction(_arrayPrototype, "sort", 1);
addFunction(_arrayPrototype, "splice", 2); addFunction(_arrayPrototype, "splice", 2);
addFunction(_arrayPrototype, "unshift", 0); addFunction(_arrayPrototype, "unshift", 0, 0, true);
addFunction(_arrayPrototype, "indexOf", numberValue(), 1); addFunction(_arrayPrototype, "indexOf", numberValue(), 2, 1);
addFunction(_arrayPrototype, "lastIndexOf", numberValue(), 1); addFunction(_arrayPrototype, "lastIndexOf", numberValue(), 2, 1);
addFunction(_arrayPrototype, "every", 1); addFunction(_arrayPrototype, "every", 2, 1);
addFunction(_arrayPrototype, "some", 1); addFunction(_arrayPrototype, "some", 2, 1);
addFunction(_arrayPrototype, "forEach", 1); addFunction(_arrayPrototype, "forEach", 2, 1);
addFunction(_arrayPrototype, "map", 1); addFunction(_arrayPrototype, "map", 2, 1);
addFunction(_arrayPrototype, "filter", 1); addFunction(_arrayPrototype, "filter", 2, 1);
addFunction(_arrayPrototype, "reduce", 1); addFunction(_arrayPrototype, "reduce", 2, 1);
addFunction(_arrayPrototype, "reduceRight", 1); addFunction(_arrayPrototype, "reduceRight", 2, 1);
// set up the default String prototype // set up the default String prototype
addFunction(_stringCtor, "fromCharCode", stringValue(), 0); addFunction(_stringCtor, "fromCharCode", stringValue(), 0, 0, true);
_stringPrototype->setMember("constructor", _stringCtor); _stringPrototype->setMember("constructor", _stringCtor);
addFunction(_stringPrototype, "toString", stringValue(), 0); addFunction(_stringPrototype, "toString", stringValue(), 0);
addFunction(_stringPrototype, "valueOf", stringValue(), 0); addFunction(_stringPrototype, "valueOf", stringValue(), 0);
addFunction(_stringPrototype, "charAt", stringValue(), 1); addFunction(_stringPrototype, "charAt", stringValue(), 1);
addFunction(_stringPrototype, "charCodeAt", stringValue(), 1); addFunction(_stringPrototype, "charCodeAt", stringValue(), 1);
addFunction(_stringPrototype, "concat", stringValue(), 0); addFunction(_stringPrototype, "concat", stringValue(), 0, 0, true);
addFunction(_stringPrototype, "indexOf", numberValue(), 2); addFunction(_stringPrototype, "indexOf", numberValue(), 2);
addFunction(_stringPrototype, "lastIndexOf", numberValue(), 2); addFunction(_stringPrototype, "lastIndexOf", numberValue(), 2);
addFunction(_stringPrototype, "localeCompare", booleanValue(), 1); addFunction(_stringPrototype, "localeCompare", booleanValue(), 1);
@@ -669,7 +680,7 @@ void ValueOwner::initializePrototypes()
addFunction(_numberCtor, "fromCharCode", 0); addFunction(_numberCtor, "fromCharCode", 0);
_numberPrototype->setMember("constructor", _numberCtor); _numberPrototype->setMember("constructor", _numberCtor);
addFunction(_numberPrototype, "toString", stringValue(), 0); addFunction(_numberPrototype, "toString", stringValue(), 1, 1);
addFunction(_numberPrototype, "toLocaleString", stringValue(), 0); addFunction(_numberPrototype, "toLocaleString", stringValue(), 0);
addFunction(_numberPrototype, "valueOf", numberValue(), 0); addFunction(_numberPrototype, "valueOf", numberValue(), 0);
addFunction(_numberPrototype, "toFixed", numberValue(), 1); addFunction(_numberPrototype, "toFixed", numberValue(), 1);
@@ -697,8 +708,8 @@ void ValueOwner::initializePrototypes()
addFunction(_mathObject, "exp", numberValue(), 1); addFunction(_mathObject, "exp", numberValue(), 1);
addFunction(_mathObject, "floor", numberValue(), 1); addFunction(_mathObject, "floor", numberValue(), 1);
addFunction(_mathObject, "log", numberValue(), 1); addFunction(_mathObject, "log", numberValue(), 1);
addFunction(_mathObject, "max", numberValue(), 0); addFunction(_mathObject, "max", numberValue(), 0, 0, true);
addFunction(_mathObject, "min", numberValue(), 0); addFunction(_mathObject, "min", numberValue(), 0, 0, true);
addFunction(_mathObject, "pow", numberValue(), 2); addFunction(_mathObject, "pow", numberValue(), 2);
addFunction(_mathObject, "random", numberValue(), 1); addFunction(_mathObject, "random", numberValue(), 1);
addFunction(_mathObject, "round", numberValue(), 1); addFunction(_mathObject, "round", numberValue(), 1);
@@ -708,6 +719,7 @@ void ValueOwner::initializePrototypes()
// set up the default Boolean prototype // set up the default Boolean prototype
addFunction(_dateCtor, "parse", numberValue(), 1); addFunction(_dateCtor, "parse", numberValue(), 1);
addFunction(_dateCtor, "UTC", numberValue(), 7, 5);
addFunction(_dateCtor, "now", numberValue(), 0); addFunction(_dateCtor, "now", numberValue(), 0);
_datePrototype->setMember("constructor", _dateCtor); _datePrototype->setMember("constructor", _dateCtor);
@@ -737,18 +749,18 @@ void ValueOwner::initializePrototypes()
addFunction(_datePrototype, "setTime", 1); addFunction(_datePrototype, "setTime", 1);
addFunction(_datePrototype, "setMilliseconds", 1); addFunction(_datePrototype, "setMilliseconds", 1);
addFunction(_datePrototype, "setUTCMilliseconds", 1); addFunction(_datePrototype, "setUTCMilliseconds", 1);
addFunction(_datePrototype, "setSeconds", 1); addFunction(_datePrototype, "setSeconds", 2, 1);
addFunction(_datePrototype, "setUTCSeconds", 1); addFunction(_datePrototype, "setUTCSeconds", 2, 1);
addFunction(_datePrototype, "setMinutes", 1); addFunction(_datePrototype, "setMinutes", 3, 2);
addFunction(_datePrototype, "setUTCMinutes", 1); addFunction(_datePrototype, "setUTCMinutes", 3, 2);
addFunction(_datePrototype, "setHours", 1); addFunction(_datePrototype, "setHours", 4, 3);
addFunction(_datePrototype, "setUTCHours", 1); addFunction(_datePrototype, "setUTCHours", 4, 3);
addFunction(_datePrototype, "setDate", 1); addFunction(_datePrototype, "setDate", 1);
addFunction(_datePrototype, "setUTCDate", 1); addFunction(_datePrototype, "setUTCDate", 1);
addFunction(_datePrototype, "setMonth", 1); addFunction(_datePrototype, "setMonth", 2, 1);
addFunction(_datePrototype, "setUTCMonth", 1); addFunction(_datePrototype, "setUTCMonth", 2, 1);
addFunction(_datePrototype, "setFullYear", 1); addFunction(_datePrototype, "setFullYear", 3, 2);
addFunction(_datePrototype, "setUTCFullYear", 1); addFunction(_datePrototype, "setUTCFullYear", 3, 2);
addFunction(_datePrototype, "toUTCString", stringValue(), 0); addFunction(_datePrototype, "toUTCString", stringValue(), 0);
addFunction(_datePrototype, "toISOString", stringValue(), 0); addFunction(_datePrototype, "toISOString", stringValue(), 0);
addFunction(_datePrototype, "toJSON", stringValue(), 1); addFunction(_datePrototype, "toJSON", stringValue(), 1);
@@ -829,10 +841,12 @@ void ValueOwner::initializePrototypes()
f = addFunction(json, "parse", objectPrototype()); f = addFunction(json, "parse", objectPrototype());
f->addArgument(stringValue(), "text"); f->addArgument(stringValue(), "text");
f->addArgument(functionPrototype(), "reviver"); f->addArgument(functionPrototype(), "reviver");
f->setOptionalNamedArgumentCount(1);
f = addFunction(json, "stringify", stringValue()); f = addFunction(json, "stringify", stringValue());
f->addArgument(unknownValue(), "value"); f->addArgument(unknownValue(), "value");
f->addArgument(unknownValue(), "replacer"); f->addArgument(unknownValue(), "replacer");
f->addArgument(unknownValue(), "space"); f->addArgument(unknownValue(), "space");
f->setOptionalNamedArgumentCount(2);
_globalObject->setMember("JSON", json); _globalObject->setMember("JSON", json);
// global Qt object, in alphabetic order // global Qt object, in alphabetic order

View File

@@ -142,8 +142,10 @@ public:
private: private:
void initializePrototypes(); void initializePrototypes();
Function *addFunction(ObjectValue *object, const QString &name, const Value *result, int argumentCount = 0); Function *addFunction(ObjectValue *object, const QString &name, const Value *result,
Function *addFunction(ObjectValue *object, const QString &name, int argumentCount = 0); int argumentCount = 0, int optionalCount = 0, bool variadic = false);
Function *addFunction(ObjectValue *object, const QString &name,
int argumentCount = 0, int optionalCount = 0, bool variadic = false);
private: private:
ObjectValue *_objectPrototype; ObjectValue *_objectPrototype;

View File

@@ -41,6 +41,8 @@
#include <texteditor/codeassist/genericproposal.h> #include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/functionhintproposal.h> #include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h> #include <texteditor/codeassist/ifunctionhintproposalmodel.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/completionsettings.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -111,6 +113,13 @@ public:
virtual void operator()(const Value *base, const QString &name, const Value *value) = 0; virtual void operator()(const Value *base, const QString &name, const Value *value) = 0;
}; };
class CompleteFunctionCall
{
public:
CompleteFunctionCall(bool hasArguments = true) : hasArguments(hasArguments) {}
bool hasArguments;
};
class CompletionAdder : public PropertyProcessor class CompletionAdder : public PropertyProcessor
{ {
protected: protected:
@@ -127,8 +136,15 @@ public:
virtual void operator()(const Value *base, const QString &name, const Value *value) virtual void operator()(const Value *base, const QString &name, const Value *value)
{ {
Q_UNUSED(base) Q_UNUSED(base)
Q_UNUSED(value) QVariant data;
addCompletion(completions, name, icon, order); if (const FunctionValue *func = value->asFunctionValue()) {
// constructors usually also have other interesting members,
// don't consider them pure functions and complete the '()'
if (!func->lookupMember("prototype", 0, 0, false)) {
data = QVariant::fromValue(CompleteFunctionCall(func->namedArgumentCount() || func->isVariadic()));
}
}
addCompletion(completions, name, icon, order, data);
} }
QIcon icon; QIcon icon;
@@ -319,6 +335,8 @@ bool isLiteral(AST::Node *ast)
} // Anonymous } // Anonymous
Q_DECLARE_METATYPE(CompleteFunctionCall)
// ----------------------- // -----------------------
// QmlJSAssistProposalItem // QmlJSAssistProposalItem
// ----------------------- // -----------------------
@@ -338,12 +356,21 @@ void QmlJSAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor
editor->setCursorPosition(basePosition); editor->setCursorPosition(basePosition);
editor->remove(currentPosition - basePosition); editor->remove(currentPosition - basePosition);
QString replaceable; QString content = text();
const QString &content = text(); int cursorOffset = 0;
if (content.endsWith(QLatin1String(": ")))
replaceable = QLatin1String(": "); const CompletionSettings &completionSettings =
else if (content.endsWith(QLatin1Char('.'))) TextEditorSettings::instance()->completionSettings();
replaceable = QLatin1String("."); const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets;
if (autoInsertBrackets && data().canConvert<CompleteFunctionCall>()) {
CompleteFunctionCall function = data().value<CompleteFunctionCall>();
content += QLatin1String("()");
if (function.hasArguments)
cursorOffset = -1;
}
QString replaceable = content;
int replacedLength = 0; int replacedLength = 0;
for (int i = 0; i < replaceable.length(); ++i) { for (int i = 0; i < replaceable.length(); ++i) {
const QChar a = replaceable.at(i); const QChar a = replaceable.at(i);
@@ -355,6 +382,8 @@ void QmlJSAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor
} }
const int length = editor->position() - basePosition + replacedLength; const int length = editor->position() - basePosition + replacedLength;
editor->replace(length, content); editor->replace(length, content);
if (cursorOffset)
editor->setCursorPosition(editor->position() + cursorOffset);
} }
// ------------------------- // -------------------------
@@ -363,10 +392,12 @@ void QmlJSAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor
class FunctionHintProposalModel : public TextEditor::IFunctionHintProposalModel class FunctionHintProposalModel : public TextEditor::IFunctionHintProposalModel
{ {
public: public:
FunctionHintProposalModel(const QString &functionName, const QStringList &signature) FunctionHintProposalModel(const QString &functionName, const QStringList &namedArguments,
int optionalNamedArguments, bool isVariadic)
: m_functionName(functionName) : m_functionName(functionName)
, m_signature(signature) , m_namedArguments(namedArguments)
, m_minimumArgumentCount(signature.size()) , m_optionalNamedArguments(optionalNamedArguments)
, m_isVariadic(isVariadic)
{} {}
virtual void reset() {} virtual void reset() {}
@@ -376,8 +407,9 @@ public:
private: private:
QString m_functionName; QString m_functionName;
QStringList m_signature; QStringList m_namedArguments;
int m_minimumArgumentCount; int m_optionalNamedArguments;
bool m_isVariadic;
}; };
QString FunctionHintProposalModel::text(int index) const QString FunctionHintProposalModel::text(int index) const
@@ -388,11 +420,13 @@ QString FunctionHintProposalModel::text(int index) const
prettyMethod += QString::fromLatin1("function "); prettyMethod += QString::fromLatin1("function ");
prettyMethod += m_functionName; prettyMethod += m_functionName;
prettyMethod += QLatin1Char('('); prettyMethod += QLatin1Char('(');
for (int i = 0; i < m_minimumArgumentCount; ++i) { for (int i = 0; i < m_namedArguments.size(); ++i) {
if (i == m_namedArguments.size() - m_optionalNamedArguments)
prettyMethod += QLatin1Char('[');
if (i != 0) if (i != 0)
prettyMethod += QLatin1String(", "); prettyMethod += QLatin1String(", ");
QString arg = m_signature.at(i); QString arg = m_namedArguments.at(i);
if (arg.isEmpty()) { if (arg.isEmpty()) {
arg = QLatin1String("arg"); arg = QLatin1String("arg");
arg += QString::number(i + 1); arg += QString::number(i + 1);
@@ -400,6 +434,13 @@ QString FunctionHintProposalModel::text(int index) const
prettyMethod += arg; prettyMethod += arg;
} }
if (m_isVariadic) {
if (m_namedArguments.size())
prettyMethod += QLatin1String(", ");
prettyMethod += QLatin1String("...");
}
if (m_optionalNamedArguments)
prettyMethod += QLatin1Char(']');
prettyMethod += QLatin1Char(')'); prettyMethod += QLatin1Char(')');
return prettyMethod; return prettyMethod;
} }
@@ -472,10 +513,12 @@ IAssistProposal *QmlJSCompletionAssistProcessor::createContentProposal() const
return proposal; return proposal;
} }
IAssistProposal *QmlJSCompletionAssistProcessor::createHintProposal(const QString &functionName, IAssistProposal *QmlJSCompletionAssistProcessor::createHintProposal(
const QStringList &signature) const const QString &functionName, const QStringList &namedArguments,
int optionalNamedArguments, bool isVariadic) const
{ {
IFunctionHintProposalModel *model = new FunctionHintProposalModel(functionName, signature); IFunctionHintProposalModel *model = new FunctionHintProposalModel(
functionName, namedArguments, optionalNamedArguments, isVariadic);
IAssistProposal *proposal = new FunctionHintProposal(m_startPosition, model); IAssistProposal *proposal = new FunctionHintProposal(m_startPosition, model);
return proposal; return proposal;
} }
@@ -642,11 +685,12 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface
if (indexOfDot != -1) if (indexOfDot != -1)
functionName = functionName.mid(indexOfDot + 1); functionName = functionName.mid(indexOfDot + 1);
QStringList signature; QStringList namedArguments;
for (int i = 0; i < f->argumentCount(); ++i) for (int i = 0; i < f->namedArgumentCount(); ++i)
signature.append(f->argumentName(i)); namedArguments.append(f->argumentName(i));
return createHintProposal(functionName.trimmed(), signature); return createHintProposal(functionName.trimmed(), namedArguments,
f->optionalNamedArgumentCount(), f->isVariadic());
} }
} }
} }

View File

@@ -99,8 +99,9 @@ public:
private: private:
TextEditor::IAssistProposal *createContentProposal() const; TextEditor::IAssistProposal *createContentProposal() const;
TextEditor::IAssistProposal *createHintProposal(const QString &functionName, TextEditor::IAssistProposal *createHintProposal(
const QStringList &signature) const; const QString &functionName, const QStringList &namedArguments,
int optionalNamedArguments, bool isVariadic) const;
bool acceptsIdleEditor() const; bool acceptsIdleEditor() const;