diff --git a/src/plugins/qmljseditor/qmljscompletionassist.cpp b/src/plugins/qmljseditor/qmljscompletionassist.cpp index eae0794e784..cec78a97edd 100644 --- a/src/plugins/qmljseditor/qmljscompletionassist.cpp +++ b/src/plugins/qmljseditor/qmljscompletionassist.cpp @@ -492,6 +492,7 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface m_startPosition = assistInterface->position(); while (isIdentifierChar(m_interface->document()->characterAt(m_startPosition - 1), false, false)) --m_startPosition; + const bool onIdentifier = m_startPosition != assistInterface->position(); m_completions.clear(); @@ -512,7 +513,11 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface const ContextPtr &context = semanticInfo.context; const ScopeChain &scopeChain = semanticInfo.scopeChain(path); - // Search for the operator that triggered the completion. + // The completionOperator is the character under the cursor or directly before the + // identifier under cursor. Use in conjunction with onIdentifier. Examples: + // a + b -> ' ' + // a + -> '+' + // a +b -> '+' QChar completionOperator; if (m_startPosition > 0) completionOperator = m_interface->document()->characterAt(m_startPosition - 1); @@ -590,15 +595,62 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface // ### enum completion? - // completion gets triggered for / in string literals, if we don't - // return here, this will mean the snippet completion pops up for - // each / in a string literal that is not triggering file completion return 0; - } else if (completionOperator.isSpace() - || completionOperator.isNull() - || isDelimiterChar(completionOperator) - || (completionOperator == QLatin1Char('(') - && m_startPosition != m_interface->position())) { + } + // member "a.bc" or function "foo(" completion + else if (completionOperator == QLatin1Char('.') + || (completionOperator == QLatin1Char('(') && !onIdentifier)) { + // Look at the expression under cursor. + //QTextCursor tc = textWidget->textCursor(); + QTextCursor tc(qmlInterface->document()); + tc.setPosition(m_startPosition - 1); + + QmlExpressionUnderCursor expressionUnderCursor; + QmlJS::AST::ExpressionNode *expression = expressionUnderCursor(tc); + + if (expression != 0 && ! isLiteral(expression)) { + // Evaluate the expression under cursor. + ValueOwner *interp = context->valueOwner(); + const Value *value = + interp->convertToObject(scopeChain.evaluate(expression)); + //qDebug() << "type:" << interp->typeId(value); + + if (value && completionOperator == QLatin1Char('.')) { // member completion + ProcessProperties processProperties(&scopeChain); + if (contextFinder.isInLhsOfBinding() && qmlScopeType) { + LhsCompletionAdder completionAdder(&m_completions, m_interface->symbolIcon(), + PropertyOrder, contextFinder.isAfterOnInLhsOfBinding()); + processProperties.setEnumerateGeneratedSlots(true); + processProperties(value, &completionAdder); + } else { + CompletionAdder completionAdder(&m_completions, m_interface->symbolIcon(), SymbolOrder); + processProperties(value, &completionAdder); + } + } else if (value + && completionOperator == QLatin1Char('(') + && m_startPosition == m_interface->position()) { + // function completion + if (const FunctionValue *f = value->asFunctionValue()) { + QString functionName = expressionUnderCursor.text(); + int indexOfDot = functionName.lastIndexOf(QLatin1Char('.')); + if (indexOfDot != -1) + functionName = functionName.mid(indexOfDot + 1); + + QStringList signature; + for (int i = 0; i < f->argumentCount(); ++i) + signature.append(f->argumentName(i)); + + return createHintProposal(functionName.trimmed(), signature); + } + } + } + + if (! m_completions.isEmpty()) + return createContentProposal(); + return 0; + } + // global completion + else if (onIdentifier || assistInterface->reason() == ExplicitlyInvoked) { bool doGlobalCompletion = true; bool doQmlKeywordCompletion = true; @@ -698,68 +750,14 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface if (!doJsKeywordCompletion) addCompletions(&m_completions, qmlWordsAlsoInJs, m_interface->keywordIcon(), KeywordOrder); } - } - else if (completionOperator == QLatin1Char('.') || completionOperator == QLatin1Char('(')) { - // Look at the expression under cursor. - //QTextCursor tc = textWidget->textCursor(); - QTextCursor tc(qmlInterface->document()); - tc.setPosition(m_startPosition - 1); - - QmlExpressionUnderCursor expressionUnderCursor; - QmlJS::AST::ExpressionNode *expression = expressionUnderCursor(tc); - - if (expression != 0 && ! isLiteral(expression)) { - // Evaluate the expression under cursor. - ValueOwner *interp = context->valueOwner(); - const Value *value = - interp->convertToObject(scopeChain.evaluate(expression)); - //qDebug() << "type:" << interp->typeId(value); - - if (value && completionOperator == QLatin1Char('.')) { // member completion - ProcessProperties processProperties(&scopeChain); - if (contextFinder.isInLhsOfBinding() && qmlScopeType) { - LhsCompletionAdder completionAdder(&m_completions, m_interface->symbolIcon(), - PropertyOrder, contextFinder.isAfterOnInLhsOfBinding()); - processProperties.setEnumerateGeneratedSlots(true); - processProperties(value, &completionAdder); - } else { - CompletionAdder completionAdder(&m_completions, m_interface->symbolIcon(), SymbolOrder); - processProperties(value, &completionAdder); - } - } else if (value - && completionOperator == QLatin1Char('(') - && m_startPosition == m_interface->position()) { - // function completion - if (const FunctionValue *f = value->asFunctionValue()) { - QString functionName = expressionUnderCursor.text(); - int indexOfDot = functionName.lastIndexOf(QLatin1Char('.')); - if (indexOfDot != -1) - functionName = functionName.mid(indexOfDot + 1); - - QStringList signature; - for (int i = 0; i < f->argumentCount(); ++i) - signature.append(f->argumentName(i)); - - return createHintProposal(functionName.trimmed(), signature); - } - } - } + m_completions.append(m_snippetCollector.collect()); if (! m_completions.isEmpty()) return createContentProposal(); return 0; } - if (isQmlFile - && (completionOperator.isNull() - || completionOperator.isSpace() - || isDelimiterChar(completionOperator))) { - m_completions.append(m_snippetCollector.collect()); - } - - if (! m_completions.isEmpty()) - return createContentProposal(); return 0; }