diff --git a/src/libs/qmljs/qmljscompletioncontextfinder.cpp b/src/libs/qmljs/qmljscompletioncontextfinder.cpp index 05ea7f1fa46c1a2eae177ddef3f6dd871b9261a1..dec1659a41356f2d2c421a8f0cca2eba828ad505 100644 --- a/src/libs/qmljs/qmljscompletioncontextfinder.cpp +++ b/src/libs/qmljs/qmljscompletioncontextfinder.cpp @@ -16,20 +16,22 @@ using namespace QmlJS; CompletionContextFinder::CompletionContextFinder(const QTextCursor &cursor) : m_cursor(cursor) + , m_colonCount(-1) { QTextBlock lastBlock = cursor.block(); if (lastBlock.next().isValid()) lastBlock = lastBlock.next(); initialize(cursor.document()->begin(), lastBlock); - int startTokenIndex = yyLinizerState.tokens.size() - 1; - for (; startTokenIndex >= 0; --startTokenIndex) { - const Token &token = yyLinizerState.tokens.at(startTokenIndex); + m_startTokenIndex = yyLinizerState.tokens.size() - 1; + for (; m_startTokenIndex >= 0; --m_startTokenIndex) { + const Token &token = yyLinizerState.tokens.at(m_startTokenIndex); if (token.end() <= cursor.positionInBlock()) break; } - getQmlObjectTypeName(startTokenIndex); + getQmlObjectTypeName(m_startTokenIndex); + checkBinding(); } void CompletionContextFinder::getQmlObjectTypeName(int startTokenIndex) @@ -79,11 +81,70 @@ void CompletionContextFinder::getQmlObjectTypeName(int startTokenIndex) YY_RESTORE(); } +void CompletionContextFinder::checkBinding() +{ + YY_SAVE(); + + //qDebug() << "Start line:" << *yyLine << m_startTokenIndex; + + int i = m_startTokenIndex; + int colonCount = 0; + bool delimiterFound = false; + while (!delimiterFound) { + if (i < 0) { + if (!readLine()) + break; + else + i = yyLinizerState.tokens.size() - 1; + //qDebug() << "New Line" << *yyLine; + } + + const Token &token = yyLinizerState.tokens.at(i); + //qDebug() << "Token:" << yyLine->mid(token.begin(), token.length); + + switch (token.kind) { + case Token::RightBrace: + case Token::LeftBrace: + case Token::Semicolon: + delimiterFound = true; + break; + + case Token::Colon: + ++colonCount; + break; + + default: + break; + } + + --i; + } + + YY_RESTORE(); + if (delimiterFound) + m_colonCount = colonCount; +} + QStringList CompletionContextFinder::qmlObjectTypeName() const { return m_qmlObjectTypeName; } +bool CompletionContextFinder::isInQmlContext() const +{ + return !qmlObjectTypeName().isEmpty(); +} + +bool CompletionContextFinder::isInLhsOfBinding() const +{ + return isInQmlContext() && m_colonCount == 0; +} + +bool CompletionContextFinder::isInRhsOfBinding() const +{ + return isInQmlContext() && m_colonCount == 1; +} + int CompletionContextFinder::findOpeningBrace(int startTokenIndex) { YY_SAVE(); @@ -91,7 +152,7 @@ int CompletionContextFinder::findOpeningBrace(int startTokenIndex) if (startTokenIndex == -1) readLine(); - Token::Kind nestedClosing; + Token::Kind nestedClosing = Token::EndOfFile; int nestingCount = 0; for (int i = 0; i < BigRoof; ++i) { diff --git a/src/libs/qmljs/qmljscompletioncontextfinder.h b/src/libs/qmljs/qmljscompletioncontextfinder.h index 0f912d1a9df058b718e566f00899fc6e73af6177..cd7696708cb1e9e9471160717399a71597171f53 100644 --- a/src/libs/qmljs/qmljscompletioncontextfinder.h +++ b/src/libs/qmljs/qmljscompletioncontextfinder.h @@ -18,13 +18,21 @@ public: bool inQmlBindingRhs(); QStringList qmlObjectTypeName() const; + bool isInQmlContext() const; + + bool isInLhsOfBinding() const; + bool isInRhsOfBinding() const; private: int findOpeningBrace(int startTokenIndex); void getQmlObjectTypeName(int startTokenIndex); + void checkBinding(); QTextCursor m_cursor; QStringList m_qmlObjectTypeName; + + int m_startTokenIndex; + int m_colonCount; }; } // namespace QmlJS diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index a3cf3270486efd02580e3f47ced932c74f3ff8c0..4e211a41cad9e6c547b383236e3f128de3404fe1 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -1446,6 +1446,21 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId return objectValue; } +const ObjectValue *Context::lookupType(const QmlJS::Document *doc, const QStringList &qmlTypeName) +{ + const ObjectValue *objectValue = typeEnvironment(doc); + + foreach (const QString &name, qmlTypeName) { + const Value *value = objectValue->property(name, this); + if (!value) + return 0; + + objectValue = value->asObjectValue(); + } + + return objectValue; +} + const Value *Context::property(const ObjectValue *object, const QString &name) const { const Properties properties = _properties.value(object); diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 309765e9bea7b9344b623937ed2d2551fa241e0f..252ff3af1e9e2cd7fdbde8a929537ac8cd3f43c8 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -303,6 +303,7 @@ public: const Value *lookup(const QString &name); const ObjectValue *lookupType(const Document *doc, AST::UiQualifiedId *qmlTypeName); + const ObjectValue *lookupType(const Document *doc, const QStringList &qmlTypeName); const Value *property(const ObjectValue *object, const QString &name) const; void setProperty(const ObjectValue *object, const QString &name, const Value *value); diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp index 6ef1bc35c9caf4ab1c86feb99fa1a81aac95b497..a57aadee131f9745dfbb24235d632171dd32db5a 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.cpp +++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp @@ -639,31 +639,72 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor) if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) || (completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) { -/* + QTextCursor startPositionCursor(edit->document()); startPositionCursor.setPosition(m_startPosition); CompletionContextFinder contextFinder(startPositionCursor); - qDebug() << "Qml type name" << contextFinder.qmlObjectTypeName(); -*/ - // It's a global completion. - EnumerateProperties enumerateProperties(&context); - enumerateProperties.setGlobalCompletion(true); - QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties()); - while (it.hasNext()) { - it.next(); - - TextEditor::CompletionItem item(this); - item.text = it.key(); - item.icon = symbolIcon; - m_completions.append(item); + + const Interpreter::ObjectValue *qmlScopeType = 0; + if (contextFinder.isInQmlContext()) + qmlScopeType = context.lookupType(document.data(), contextFinder.qmlObjectTypeName()); + + bool doGlobalCompletion = true; + if (contextFinder.isInLhsOfBinding() && qmlScopeType) { + qDebug() << "LHS of binding!"; + + doGlobalCompletion = false; + EnumerateProperties enumerateProperties(&context); + enumerateProperties.setGlobalCompletion(true); + + QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(qmlScopeType)); + while (it.hasNext()) { + it.next(); + + TextEditor::CompletionItem item(this); + item.text = it.key(); + item.data = QString(it.key() + QLatin1String(": ")); + item.icon = symbolIcon; + m_completions.append(item); + } + + it = enumerateProperties(context.scopeChain().qmlTypes); + while (it.hasNext()) { + it.next(); + + TextEditor::CompletionItem item(this); + item.text = it.key(); + QString data = it.key(); + data.append(QLatin1String(" {\n")); + data.append(QChar::ObjectReplacementCharacter); + data.append(QChar::ObjectReplacementCharacter); + data.append(QLatin1String("\n}")); + item.data = data; + item.icon = symbolIcon; + m_completions.append(item); + } } - // add js keywords - foreach (const QString &word, Scanner::keywords()) { - TextEditor::CompletionItem item(this); - item.text = word; - item.icon = keywordIcon; - m_completions.append(item); + if (doGlobalCompletion) { + // It's a global completion. + EnumerateProperties enumerateProperties(&context); + enumerateProperties.setGlobalCompletion(true); + QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties()); + while (it.hasNext()) { + it.next(); + + TextEditor::CompletionItem item(this); + item.text = it.key(); + item.icon = symbolIcon; + m_completions.append(item); + } + + // add js keywords + foreach (const QString &word, Scanner::keywords()) { + TextEditor::CompletionItem item(this); + item.text = word; + item.icon = keywordIcon; + m_completions.append(item); + } } // add qml extra words