Evaluate simple JS expressions.

This commit is contained in:
Roberto Raggi
2010-01-21 17:08:19 +01:00
parent 968e5e228a
commit f0e69db452

View File

@@ -34,6 +34,8 @@
#include "qmllookupcontext.h"
#include "qmlresolveexpression.h"
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljssymbol.h>
#include <texteditor/basetexteditor.h>
@@ -82,6 +84,103 @@ static QIcon iconForColor(const QColor &color)
}
class Evaluate: public QmlJS::AST::Visitor
{
QmlJS::Interpreter::Engine *_interp;
const QmlJS::Interpreter::Value *_value;
public:
Evaluate(QmlJS::Interpreter::Engine *interp)
: _interp(interp), _value(0)
{}
const QmlJS::Interpreter::Value *operator()(QmlJS::AST::Node *node)
{ return evaluate(node); }
const QmlJS::Interpreter::Value *evaluate(QmlJS::AST::Node *node)
{
const QmlJS::Interpreter::Value *previousValue = switchValue(0);
if (node)
node->accept(this);
return switchValue(previousValue);
}
protected:
using QmlJS::AST::Visitor::visit;
const QmlJS::Interpreter::Value *switchValue(const QmlJS::Interpreter::Value *value)
{
const QmlJS::Interpreter::Value *previousValue = _value;
_value = value;
return previousValue;
}
virtual bool preVisit(QmlJS::AST::Node *ast)
{
using namespace QmlJS::AST;
if (cast<NumericLiteral *>(ast))
return true;
if (cast<StringLiteral *>(ast))
return true;
if (cast<IdentifierExpression *>(ast))
return true;
else if (cast<FieldMemberExpression *>(ast))
return true;
else if (cast<CallExpression *>(ast))
return true;
return false;
}
virtual bool visit(QmlJS::AST::StringLiteral *)
{
_value = _interp->convertToObject(_interp->stringValue());
return false;
}
virtual bool visit(QmlJS::AST::NumericLiteral *)
{
_value = _interp->convertToObject(_interp->numberValue());
return false;
}
virtual bool visit(QmlJS::AST::IdentifierExpression *ast)
{
_value = _interp->globalObject()->property(ast->name->asString());
return false;
}
virtual bool visit(QmlJS::AST::FieldMemberExpression *ast)
{
if (const QmlJS::Interpreter::Value *base = evaluate(ast->base)) {
if (const QmlJS::Interpreter::ObjectValue *obj = base->asObjectValue()) {
_value = obj->property(ast->name->asString());
}
}
return false;
}
virtual bool visit(QmlJS::AST::CallExpression *ast)
{
if (const QmlJS::Interpreter::Value *base = evaluate(ast->base)) {
if (const QmlJS::Interpreter::FunctionValue *obj = base->asFunctionValue()) {
_value = obj->returnValue();
}
}
return false;
}
};
QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager, QmlJS::TypeSystem *typeSystem, QObject *parent)
: TextEditor::ICompletionCollector(parent),
m_modelManager(modelManager),
@@ -163,21 +262,29 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
}
}
const QIcon idIcon = iconForColor(Qt::darkGray);
QStringList ids = qmlDocument->ids().keys();
foreach (const QString &id, ids) {
if (id.isEmpty())
continue;
QChar previousChar;
if (m_startPosition > 0)
previousChar = editor->characterAt(m_startPosition - 1);
TextEditor::CompletionItem item(this);
item.text = id;
item.icon = idIcon;
m_completions.append(item);
if (previousChar.isSpace() || previousChar.isNull()) {
// ### FIXME
const QIcon idIcon = iconForColor(Qt::darkGray);
QStringList ids = qmlDocument->ids().keys();
foreach (const QString &id, ids) {
if (id.isEmpty())
continue;
TextEditor::CompletionItem item(this);
item.text = id;
item.icon = idIcon;
m_completions.append(item);
}
}
const QIcon otherIcon = iconForColor(Qt::darkCyan);
// FIXME: this completion strategy is not going to work when the document was never parsed correctly.
if (qmlDocument->qmlProgram() != 0) {
const QIcon otherIcon = iconForColor(Qt::darkCyan);
// qDebug() << "*** program:" << program;
QmlExpressionUnderCursor expressionUnderCursor;
QTextCursor cursor(edit->document());
@@ -186,6 +293,23 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
QmlLookupContext context(expressionUnderCursor.expressionScopes(), qmlDocument, m_modelManager->snapshot(), m_typeSystem);
QmlResolveExpression resolver(context);
QmlJS::AST::Node *expr = expressionUnderCursor.expressionNode();
QmlJS::Interpreter::Engine engine;
Evaluate evaluate(&engine);
if (const QmlJS::Interpreter::Value *value = evaluate(expr)) {
if (const QmlJS::Interpreter::ObjectValue *object = value->asObjectValue()) {
for (QmlJS::Interpreter::ObjectValue::MemberIterator it = object->firstMember(); it != object->lastMember(); ++it) {
TextEditor::CompletionItem item(this);
item.text = it.key();
item.icon = otherIcon;
m_completions.append(item);
}
return pos;
}
}
// qDebug()<<"*** expression under cursor:"<<expressionUnderCursor.expressionNode();
const QList<QmlJS::Symbol*> symbols = resolver.visibleSymbols(expressionUnderCursor.expressionNode());
// qDebug()<<"***"<<symbols.size()<<"visible symbols";
@@ -204,11 +328,6 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
}
}
QChar previousChar;
if (m_startPosition > 0)
previousChar = editor->characterAt(m_startPosition - 1);
if (previousChar.isNull()
|| previousChar.isSpace()
|| previousChar == QLatin1Char('{')