diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp index 1d1ab500d80..065ea0fe4b3 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp @@ -933,9 +933,12 @@ void IpcCommunicator::completeCode(ClangCompletionAssistProcessor *assistProcess const QString &filePath, quint32 line, quint32 column, - const QString &projectFilePath) + const QString &projectFilePath, + qint32 funcNameStartLine, + qint32 funcNameStartColumn) { - const CompleteCodeMessage message(filePath, line, column, projectFilePath); + const CompleteCodeMessage message(filePath, line, column, projectFilePath, funcNameStartLine, + funcNameStartColumn); m_ipcSender->completeCode(message); m_ipcReceiver.addExpectedCodeCompletedMessage(message.ticketNumber(), assistProcessor); } diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.h b/src/plugins/clangcodemodel/clangbackendipcintegration.h index fe38435220a..94be668114e 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.h +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.h @@ -174,7 +174,9 @@ public: void completeCode(ClangCompletionAssistProcessor *assistProcessor, const QString &filePath, quint32 line, quint32 column, - const QString &projectFilePath); + const QString &projectFilePath, + qint32 funcNameStartLine = -1, + qint32 funcNameStartColumn = -1); void registerProjectsParts(const QVector projectParts); diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index 1cd9a770d72..13d8f5531d8 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -253,7 +253,8 @@ IAssistProposal *ClangCompletionAssistProcessor::startCompletionHelper() } case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen: { m_sentRequestType = FunctionHintCompletion; - const bool requestSent = sendCompletionRequest(analyzer.positionForClang(), QByteArray()); + const bool requestSent = sendCompletionRequest(analyzer.positionForClang(), QByteArray(), + analyzer.functionNameStart()); setPerformWasApplicable(requestSent); break; } @@ -548,14 +549,26 @@ void setLastCompletionPosition(const QString &filePath, } -bool ClangCompletionAssistProcessor::sendCompletionRequest(int position, - const QByteArray &customFileContent) +ClangCompletionAssistProcessor::Position +ClangCompletionAssistProcessor::extractLineColumn(int position) { - int line, column; - TextEditor::Convenience::convertPosition(m_interface->textDocument(), position, &line, &column); + if (position < 0) + return {-1, -1}; + + int line = -1, column = -1; + TextEditor::Convenience::convertPosition(m_interface->textDocument(), + position, + &line, + &column); const QTextBlock block = m_interface->textDocument()->findBlock(position); column += ClangCodeModel::Utils::extraUtf8CharsShift(block.text(), column) + 1; + return {line, column}; +} +bool ClangCompletionAssistProcessor::sendCompletionRequest(int position, + const QByteArray &customFileContent, + int functionNameStartPosition) +{ const QString filePath = m_interface->fileName(); auto &ipcCommunicator = m_interface->ipcCommunicator(); @@ -567,8 +580,12 @@ bool ClangCompletionAssistProcessor::sendCompletionRequest(int position, setLastDocumentRevision(filePath); } + const Position cursorPosition = extractLineColumn(position); + const Position functionNameStart = extractLineColumn(functionNameStartPosition); const QString projectPartId = CppTools::CppToolsBridge::projectPartIdForFile(filePath); - ipcCommunicator.completeCode(this, filePath, uint(line), uint(column), projectPartId); + ipcCommunicator.completeCode(this, filePath, uint(cursorPosition.line), + uint(cursorPosition.column), projectPartId, + functionNameStart.line, functionNameStart.column); setLastCompletionPosition(filePath, position); return true; } diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.h b/src/plugins/clangcodemodel/clangcompletionassistprocessor.h index 4172d646f44..0f23b1a0fee 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.h +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.h @@ -82,9 +82,14 @@ private: UnsavedFileContentInfo unsavedFileContent(const QByteArray &customFileContent) const; void sendFileContent(const QByteArray &customFileContent); - bool sendCompletionRequest(int position, const QByteArray &customFileContent); + bool sendCompletionRequest(int position, + const QByteArray &customFileContent, + int functionNameStartPosition = -1); private: + struct Position { int line; int column; }; + Position extractLineColumn(int position); + QScopedPointer m_interface; unsigned m_completionOperator; enum CompletionRequestType { NormalCompletion, FunctionHintCompletion } m_sentRequestType; diff --git a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp index 00a8f96c6d7..bb8308eada5 100644 --- a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp +++ b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp @@ -91,7 +91,7 @@ void ClangCompletionContextAnalyzer::analyze() } } -bool ClangCompletionContextAnalyzer::looksLikeAFunctionCall(int endOfOperator) const +int ClangCompletionContextAnalyzer::startOfFunctionCall(int endOfOperator) const { int index = ActivationSequenceContextProcessor::skipPrecedingWhitespace(m_interface, endOfOperator); @@ -104,22 +104,24 @@ bool ClangCompletionContextAnalyzer::looksLikeAFunctionCall(int endOfOperator) c const int functionNameStart = ActivationSequenceContextProcessor::findStartOfName(m_interface, index); if (functionNameStart == -1) - return false; + return -1; QTextCursor functionNameSelector(m_interface->textDocument()); functionNameSelector.setPosition(functionNameStart); functionNameSelector.setPosition(index, QTextCursor::KeepAnchor); const QString functionName = functionNameSelector.selectedText().trimmed(); - return !functionName.isEmpty(); + return functionName.isEmpty() ? -1 : functionNameStart; } void ClangCompletionContextAnalyzer::setActionAndClangPosition(CompletionAction action, - int position) + int position, + int functionNameStart) { QTC_CHECK(position >= -1); m_completionAction = action; m_positionForClang = position; + m_functionNameStart = functionNameStart; } void @@ -157,15 +159,19 @@ void ClangCompletionContextAnalyzer::handleFunctionCall(int afterOperatorPositio // No function completion if cursor is not after '(' or ',' m_positionForProposal = afterOperatorPosition; setActionAndClangPosition(PassThroughToLibClang, afterOperatorPosition); - } else if (looksLikeAFunctionCall(afterOperatorPosition)) { - // Always pass the position right after '(' to libclang because - // positions after the comma might be problematic if a preceding - // argument is invalid code. - setActionAndClangPosition(PassThroughToLibClangAfterLeftParen, - m_positionForProposal); - } else { // e.g. "(" without any function name in front - m_positionForProposal = afterOperatorPosition; - setActionAndClangPosition(PassThroughToLibClang, afterOperatorPosition); + } else { + const int functionNameStart = startOfFunctionCall(afterOperatorPosition); + if (functionNameStart >= 0) { + // Always pass the position right after '(' to libclang because + // positions after the comma might be problematic if a preceding + // argument is invalid code. + setActionAndClangPosition(PassThroughToLibClangAfterLeftParen, + m_positionForProposal, + functionNameStart); + } else { // e.g. "(" without any function name in front + m_positionForProposal = afterOperatorPosition; + setActionAndClangPosition(PassThroughToLibClang, afterOperatorPosition); + } } } } diff --git a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.h b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.h index 3aa3bc58045..113384c7610 100644 --- a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.h +++ b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.h @@ -56,14 +56,17 @@ public: unsigned completionOperator() const { return m_completionOperator; } int positionForProposal() const { return m_positionForProposal; } int positionForClang() const { return m_positionForClang; } + int functionNameStart() const { return m_functionNameStart; } int positionEndOfExpression() const { return m_positionEndOfExpression; } private: ClangCompletionContextAnalyzer(); - bool looksLikeAFunctionCall(int endOfExpression) const; + int startOfFunctionCall(int endOfExpression) const; - void setActionAndClangPosition(CompletionAction action, int position); + void setActionAndClangPosition(CompletionAction action, + int position, + int functionNameStart = -1); void setAction(CompletionAction action); bool handleNonFunctionCall(int position); @@ -79,6 +82,7 @@ private: CPlusPlus::Kind m_completionOperator = CPlusPlus::T_EOF_SYMBOL; int m_positionForProposal = -1; int m_positionForClang = -1; + int m_functionNameStart = -1; int m_positionEndOfExpression = -1; }; diff --git a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp index ae0c412ad74..09938b2c5da 100644 --- a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp +++ b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp @@ -497,4 +497,20 @@ TEST_F(ClangCompletionContextAnalyzer, TemplatedFunctionSecondArgument) ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, -3, -3, positionInText)); } +TEST_F(ClangCompletionContextAnalyzer, FunctionNameStartPosition) +{ + auto analyzer = runAnalyzer(" f(1, @"); + int functionNameStartPosition = analyzer.functionNameStart(); + + ASSERT_THAT(functionNameStartPosition, 1); +} + +TEST_F(ClangCompletionContextAnalyzer, QualifiedFunctionNameStartPosition) +{ + auto analyzer = runAnalyzer(" Namespace::f(1, @"); + int functionNameStartPosition = analyzer.functionNameStart(); + + ASSERT_THAT(functionNameStartPosition, 1); +} + }