diff --git a/src/plugins/clangcodemodel/clangfollowsymbol.cpp b/src/plugins/clangcodemodel/clangfollowsymbol.cpp index aa60a480a5d..2876055706e 100644 --- a/src/plugins/clangcodemodel/clangfollowsymbol.cpp +++ b/src/plugins/clangcodemodel/clangfollowsymbol.cpp @@ -66,7 +66,37 @@ static int getMarkPos(QTextCursor cursor, const ClangBackEnd::TokenInfoContainer return cursor.position(); } -static Utils::Link linkAtCursor(QTextCursor cursor, const QString &filePath, uint line, uint column, +static bool isValidIncludePathToken(const ClangBackEnd::TokenInfoContainer &token) +{ + if (!token.extraInfo.includeDirectivePath) + return false; + const Utf8String &tokenName = token.extraInfo.token; + return !tokenName.startsWith("include") && tokenName != "<" && tokenName != ">" + && tokenName != "#"; +} + +static int includePathStartIndex(const QVector &marks, + int currentIndex) +{ + int startIndex = currentIndex - 1; + while (startIndex >= 0 && isValidIncludePathToken(marks[startIndex])) + --startIndex; + return startIndex + 1; +} + +static int includePathEndIndex(const QVector &marks, + int currentIndex) +{ + int endIndex = currentIndex + 1; + while (isValidIncludePathToken(marks[endIndex])) + ++endIndex; + return endIndex - 1; +} + +static Utils::Link linkAtCursor(const QTextCursor &cursor, + const QString &filePath, + uint line, + uint column, ClangEditorDocumentProcessor *processor) { using Link = Utils::Link; @@ -77,18 +107,35 @@ static Utils::Link linkAtCursor(QTextCursor cursor, const QString &filePath, uin if (!findMark(marks, line, column, mark)) return Link(); - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); - const QString tokenStr = cursor.selectedText(); + if (mark.extraInfo.includeDirectivePath && !isValidIncludePathToken(mark)) + return Link(); Link token(filePath, mark.line, mark.column); token.linkTextStart = getMarkPos(cursor, mark); token.linkTextEnd = token.linkTextStart + mark.length; + if (mark.extraInfo.includeDirectivePath) { - if (tokenStr != "include" && tokenStr != "#" && tokenStr != "<") - return token; - return Link(); + // Tweak include paths to cover everything between "" or <>. + if (mark.extraInfo.token.startsWith("\"")) { + token.linkTextStart++; + token.linkTextEnd--; + } else { + // '#include ' case. Clang gives us a separate token for each part of + // the path. We want to have the full range instead therefore we search for < and > + // tokens around the current token. + const int index = marks.indexOf(mark); + const int startIndex = includePathStartIndex(marks, index); + const int endIndex = includePathEndIndex(marks, index); + + if (startIndex != index) + token.linkTextStart = getMarkPos(cursor, marks[startIndex]); + if (endIndex != index) + token.linkTextEnd = getMarkPos(cursor, marks[endIndex]) + marks[endIndex].length; + } + return token; } - if (mark.extraInfo.identifier || tokenStr == "operator") + + if (mark.extraInfo.identifier || mark.extraInfo.token == "operator") return token; return Link(); } @@ -127,11 +174,16 @@ void ClangFollowSymbol::findLink(const CppTools::CursorInEditor &data, if (infoFuture.isCanceled()) return processLinkCallback(Utils::Link()); - QObject::connect(&m_watcher, &FutureSymbolWatcher::finished, - [=, watcher=&m_watcher, callback=std::move(processLinkCallback)]() mutable { - if (watcher->isCanceled()) + if (m_watcher) + m_watcher->cancel(); + + m_watcher.reset(new FutureSymbolWatcher()); + + QObject::connect(m_watcher.get(), &FutureSymbolWatcher::finished, + [=, callback=std::move(processLinkCallback)]() mutable { + if (m_watcher->isCanceled()) return callback(Utils::Link()); - CppTools::SymbolInfo result = watcher->result(); + CppTools::SymbolInfo result = m_watcher->result(); // We did not fail but the result is empty if (result.fileName.isEmpty()) { const CppTools::RefactoringEngineInterface &refactoringEngine @@ -147,7 +199,7 @@ void ClangFollowSymbol::findLink(const CppTools::CursorInEditor &data, } }); - m_watcher.setFuture(infoFuture); + m_watcher->setFuture(infoFuture); } } // namespace Internal diff --git a/src/plugins/clangcodemodel/clangfollowsymbol.h b/src/plugins/clangcodemodel/clangfollowsymbol.h index dd7abb38fc3..c76730fd3c3 100644 --- a/src/plugins/clangcodemodel/clangfollowsymbol.h +++ b/src/plugins/clangcodemodel/clangfollowsymbol.h @@ -45,7 +45,7 @@ public: bool inNextSplit) override; private: using FutureSymbolWatcher = QFutureWatcher; - FutureSymbolWatcher m_watcher; + std::unique_ptr m_watcher; }; } // namespace Internal diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 0d7a0575b60..9eda7a998f8 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -64,12 +64,6 @@ using namespace ClangCodeModel::Internal; static ModelManagerSupportClang *m_instance = 0; -static bool useClangFollowSymbol() -{ - static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL"); - return use; -} - static CppTools::CppModelManager *cppModelManager() { return CppTools::CppModelManager::instance(); @@ -77,16 +71,12 @@ static CppTools::CppModelManager *cppModelManager() ModelManagerSupportClang::ModelManagerSupportClang() : m_completionAssistProvider(m_communicator) + , m_followSymbol(new ClangFollowSymbol) , m_refactoringEngine(new RefactoringEngine) { QTC_CHECK(!m_instance); m_instance = this; - if (useClangFollowSymbol()) - m_followSymbol.reset(new ClangFollowSymbol); - else - m_followSymbol.reset(new CppTools::FollowSymbolUnderCursor); - CppTools::CppModelManager::instance()->setCurrentDocumentFilter( std::make_unique()); diff --git a/src/tools/clangbackend/source/clangcodemodelserver.cpp b/src/tools/clangbackend/source/clangcodemodelserver.cpp index cc2433e21c1..72f09b956ac 100644 --- a/src/tools/clangbackend/source/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/source/clangcodemodelserver.cpp @@ -261,6 +261,7 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot DocumentProcessor processor = documentProcessors().processor(document); processor.addJob(JobRequest::Type::RequestDocumentAnnotations); + processor.addJob(JobRequest::Type::UpdateExtraDocumentAnnotations); processor.process(); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what(); diff --git a/tests/unit/unittest/clangcodemodelserver-test.cpp b/tests/unit/unittest/clangcodemodelserver-test.cpp index 260aa46445a..ebcb5dd3d3e 100644 --- a/tests/unit/unittest/clangcodemodelserver-test.cpp +++ b/tests/unit/unittest/clangcodemodelserver-test.cpp @@ -92,6 +92,17 @@ MATCHER_P5(HasDirtyDocument, } } +MATCHER_P(PartlyContains, token, "") +{ + for (const auto &item: arg) { + if (item.types == token.types && item.line == token.line && item.column == token.column + && item.length == token.length) { + return true; + } + } + return false; +} + static constexpr int AnnotationJobsMultiplier = 2; class ClangCodeModelServer : public ::testing::Test @@ -675,7 +686,7 @@ void ClangCodeModelServer::expectDocumentAnnotationsChangedForFileBWithSpecificH EXPECT_CALL(mockClangCodeModelClient, documentAnnotationsChanged( Field(&DocumentAnnotationsChangedMessage::tokenInfos, - Contains(tokenInfo)))); + PartlyContains(tokenInfo)))).Times(AnnotationJobsMultiplier); } void ClangCodeModelServer::updateUnsavedContent(const Utf8String &filePath,