diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index da3d84e7f07e28ff62b7fa19dc8812e67f63cb34..ae8ebccb619ef84bc87211c80f5aa15bf0b5317e 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -1398,7 +1398,7 @@ void Engine::initializePrototypes() _globalObject->setProperty("RegExp", regexpCtor()); } -const ObjectValue *Engine::newQmlObject(const QString &name) +ObjectValue *Engine::newQmlObject(const QString &name) { #ifndef NO_DECLARATIVE_BACKEND if (name == QLatin1String("QmlGraphicsAnchors")) { diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 9474783998ac40b144bed4ac179358ef771c5738..2b2338e7641eb9ad998651c866e7f0517af33192 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -435,7 +435,7 @@ public: const Value *newArray(); // ### remove me // QML objects - const ObjectValue *newQmlObject(const QString &name); + ObjectValue *newQmlObject(const QString &name); // global object ObjectValue *globalObject() const; diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index f20dd803d415b15b6f92bedaa7642e7e37e7e457..88de8b468d5e3e52530e4fd2a340f96614a3ecf6 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -308,7 +308,7 @@ protected: if (! ast->name) return false; - _value = _scope->property(ast->name->asString()); + _value = _scope->lookup(ast->name->asString()); return false; } @@ -345,11 +345,12 @@ class EnumerateProperties: private Interpreter::MemberProcessor QHash<QString, const Interpreter::Value *> _properties; public: - QHash<QString, const Interpreter::Value *> operator()(const Interpreter::Value *value) + QHash<QString, const Interpreter::Value *> operator()(const Interpreter::Value *value, + bool lookAtScope = false) { _processed.clear(); _properties.clear(); - enumerateProperties(value); + enumerateProperties(value, lookAtScope); return _properties; } @@ -360,22 +361,25 @@ private: return true; } - void enumerateProperties(const Interpreter::Value *value) + void enumerateProperties(const Interpreter::Value *value, bool lookAtScope) { if (! value) return; else if (const Interpreter::ObjectValue *object = value->asObjectValue()) { - enumerateProperties(object); + enumerateProperties(object, lookAtScope); } } - void enumerateProperties(const Interpreter::ObjectValue *object) + void enumerateProperties(const Interpreter::ObjectValue *object, bool lookAtScope) { if (! object || _processed.contains(object)) return; _processed.insert(object); - enumerateProperties(object->prototype()); + enumerateProperties(object->prototype(), /* lookAtScope = */ false); + + if (lookAtScope) + enumerateProperties(object->scope(), /* lookAtScope = */ true); object->processMembers(this); } @@ -662,33 +666,25 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) return -1; m_startPosition = editor->position(); + const QString fileName = editor->file()->fileName(); - while (editor->characterAt(m_startPosition - 1).isLetterOrNumber() || editor->characterAt(m_startPosition - 1) == QLatin1Char('_')) + while (editor->characterAt(m_startPosition - 1).isLetterOrNumber() || + editor->characterAt(m_startPosition - 1) == QLatin1Char('_')) --m_startPosition; m_completions.clear(); - QmlJS::Document::Ptr qmlDocument = edit->qmlDocument(); - - const QmlJS::Snapshot &snapshot = m_modelManager->snapshot(); - - if (! qmlDocument) - qmlDocument = snapshot.value(qmlDocument->fileName()); + QmlJS::Snapshot snapshot = m_modelManager->snapshot(); + Document::Ptr qmlDocument = snapshot.document(fileName); const QFileInfo currentFileInfo(qmlDocument->fileName()); const QString currentFilePath = currentFileInfo.absolutePath(); - const QIcon typeIcon = iconForColor(Qt::yellow); + const QIcon componentIcon = iconForColor(Qt::yellow); const QIcon symbolIcon = iconForColor(Qt::darkCyan); Interpreter::Engine interp; - if (qmlDocument && qmlDocument->qmlProgram()) { - const Interpreter::ObjectValue *parentItem = 0; - parentItem = interp.newQmlObject(QLatin1String("Item")); // ### TODO: find the parent item. - interp.globalObject()->setProperty(QLatin1String("parent"), parentItem); - } - foreach (Document::Ptr doc, snapshot) { const QFileInfo fileInfo(doc->fileName()); @@ -710,6 +706,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) if (symbol->parentNode()) { const QString component = symbol->parentNode()->name(); + if (const Interpreter::ObjectValue *object = interp.newQmlObject(component)) interp.globalObject()->setProperty(id, object); } @@ -717,12 +714,29 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } } + // Set up the current scope chain. + Interpreter::ObjectValue *scope = interp.newObject(/* prototype = */ 0); + + // ### FIXME: get the declaring item + Interpreter::ObjectValue *declaringItem = interp.newQmlObject(QLatin1String("Item")); + + scope->setScope(declaringItem); + declaringItem->setScope(interp.globalObject()); + + if (qmlDocument && qmlDocument->qmlProgram()) { + const Interpreter::ObjectValue *parentItem = 0; + const QString declaringItem = QLatin1String("Item"); // ### FIXME: resolve the parent of the declaring item + parentItem = interp.newQmlObject(declaringItem); + scope->setProperty(QLatin1String("parent"), parentItem); + } + + // Search for the operator that triggered the completion. QChar completionOperator; if (m_startPosition > 0) completionOperator = editor->characterAt(m_startPosition - 1); - if (completionOperator.isSpace() || completionOperator.isNull()) { - // Add the visible components to the completion box. + if (completionOperator.isSpace() || completionOperator.isNull()) { // It's a global completion. + // Process the visible user defined components. foreach (QmlJS::Document::Ptr doc, snapshot) { const QFileInfo fileInfo(doc->fileName()); @@ -731,23 +745,17 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) else if (fileInfo.absolutePath() != currentFilePath) // ### FIXME includ `imported' components continue; - const QString typeName = fileInfo.baseName(); - if (typeName.isEmpty()) - continue; - - if (typeName.at(0).isUpper()) { + const QString componentName = fileInfo.baseName(); + if (! componentName.isEmpty() && componentName.at(0).isUpper()) { TextEditor::CompletionItem item(this); - item.text = typeName; - item.icon = typeIcon; + item.text = componentName; + item.icon = componentIcon; m_completions.append(item); } } - // ### set up the current scope chain. - const Interpreter::ObjectValue *scope = interp.globalObject(); - EnumerateProperties enumerateProperties; - QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(scope)); + QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(scope, /* lookAtScope = */ true)); while (it.hasNext()) { it.next(); @@ -759,22 +767,25 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } else if (completionOperator == QLatin1Char('.') || completionOperator == QLatin1Char('(')) { - ExpressionUnderCursor expressionUnderCursor; - + // Look at the expression under cursor. QTextCursor tc = edit->textCursor(); tc.setPosition(m_startPosition - 1); + ExpressionUnderCursor expressionUnderCursor; const QString expression = expressionUnderCursor(tc); //qDebug() << "expression:" << expression; + // Wrap the expression in a QML document. QmlJS::Document::Ptr exprDoc = Document::create(QLatin1String("<expression>")); exprDoc->setSource(expression); exprDoc->parseExpression(); if (exprDoc->ast()) { Evaluate evaluate(&interp); - // ### set up the scope chain + evaluate.setScope(scope); + // ### TODO: remove me. This is just a quick and dirty hack to get some completion + // for the property definitions. SearchPropertyDefinitions searchPropertyDefinitions; const QList<AST::UiPublicMember *> properties = searchPropertyDefinitions(qmlDocument); @@ -785,6 +796,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) const QString propName = prop->name->asString(); const QString propType = prop->memberType->asString(); + // ### TODO: generalize if (propType == QLatin1String("string")) interp.globalObject()->setProperty(propName, interp.stringValue()); else if (propType == QLatin1String("bool")) @@ -793,6 +805,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) interp.globalObject()->setProperty(propName, interp.numberValue()); } + // Evaluate the expression under cursor. const Interpreter::Value *value = interp.convertToObject(evaluate(exprDoc->ast())); //qDebug() << "type:" << interp.typeId(value);