diff --git a/src/libs/clangbackendipc/requestfollowsymbolmessage.cpp b/src/libs/clangbackendipc/requestfollowsymbolmessage.cpp index af302a7680c..9d31139840a 100644 --- a/src/libs/clangbackendipc/requestfollowsymbolmessage.cpp +++ b/src/libs/clangbackendipc/requestfollowsymbolmessage.cpp @@ -38,6 +38,7 @@ QDebug operator<<(QDebug debug, const RequestFollowSymbolMessage &message) debug.nospace() << "RequestFollowSymbolMessage("; debug.nospace() << message.m_fileContainer << ", "; + debug.nospace() << message.m_dependentFiles << ", "; debug.nospace() << message.m_ticketNumber << ", "; debug.nospace() << message.m_line << ", "; debug.nospace() << message.m_column << ", "; @@ -52,6 +53,7 @@ std::ostream &operator<<(std::ostream &os, const RequestFollowSymbolMessage &mes { os << "(" << message.m_fileContainer << ", " + << message.m_dependentFiles << ", " << message.m_ticketNumber << ", " << message.m_line << ", " << message.m_column << ", " diff --git a/src/libs/clangbackendipc/requestfollowsymbolmessage.h b/src/libs/clangbackendipc/requestfollowsymbolmessage.h index 1f7e348300b..fe564bf8285 100644 --- a/src/libs/clangbackendipc/requestfollowsymbolmessage.h +++ b/src/libs/clangbackendipc/requestfollowsymbolmessage.h @@ -38,13 +38,15 @@ class RequestFollowSymbolMessage public: RequestFollowSymbolMessage() = default; RequestFollowSymbolMessage(const FileContainer &fileContainer, - quint32 line, - quint32 column, - bool resolveTarget = true) + const QVector &dependentFiles, + quint32 line, + quint32 column, + bool resolveTarget = true) : m_fileContainer(fileContainer) , m_ticketNumber(++ticketCounter) , m_line(line) , m_column(column) + , m_dependentFiles(dependentFiles) , m_resolveTarget(resolveTarget) { } @@ -54,6 +56,11 @@ public: return m_fileContainer; } + const QVector &dependentFiles() const + { + return m_dependentFiles; + } + quint32 line() const { return m_line; @@ -77,6 +84,7 @@ public: friend QDataStream &operator<<(QDataStream &out, const RequestFollowSymbolMessage &message) { out << message.m_fileContainer; + out << message.m_dependentFiles; out << message.m_ticketNumber; out << message.m_line; out << message.m_column; @@ -88,6 +96,7 @@ public: friend QDataStream &operator>>(QDataStream &in, RequestFollowSymbolMessage &message) { in >> message.m_fileContainer; + in >> message.m_dependentFiles; in >> message.m_ticketNumber; in >> message.m_line; in >> message.m_column; @@ -103,7 +112,8 @@ public: && first.m_line == second.m_line && first.m_column == second.m_column && first.m_fileContainer == second.m_fileContainer - && first.m_resolveTarget == second.m_resolveTarget; + && first.m_resolveTarget == second.m_resolveTarget + && first.m_dependentFiles == second.m_dependentFiles; } friend CMBIPC_EXPORT QDebug operator<<(QDebug debug, const RequestFollowSymbolMessage &message); @@ -113,6 +123,7 @@ private: quint64 m_ticketNumber = 0; quint32 m_line = 0; quint32 m_column = 0; + QVector m_dependentFiles; bool m_resolveTarget = true; static CMBIPC_EXPORT quint64 ticketCounter; }; diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp index a04c1c7c371..f966ef6ae40 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp @@ -144,6 +144,18 @@ QFuture IpcReceiver::addExpectedReferencesMessage(quint64 return futureInterface.future(); } +QFuture IpcReceiver::addExpectedRequestFollowSymbolMessage(quint64 ticket) +{ + QTC_CHECK(!m_followTable.contains(ticket)); + + QFutureInterface futureInterface; + futureInterface.reportStarted(); + + m_followTable.insert(ticket, futureInterface); + + return futureInterface.future(); +} + bool IpcReceiver::isExpectingCodeCompletedMessage() const { return !m_assistProcessorsTable.isEmpty(); @@ -159,6 +171,9 @@ void IpcReceiver::reset() for (ReferencesEntry &entry : m_referencesTable) entry.futureInterface.cancel(); m_referencesTable.clear(); + for (QFutureInterface &futureInterface : m_followTable) + futureInterface.cancel(); + m_followTable.clear(); } void IpcReceiver::alive() @@ -243,6 +258,24 @@ CppTools::CursorInfo toCursorInfo(const QTextDocument &textDocument, return result; } +static +CppTools::SymbolInfo toSymbolInfo(const FollowSymbolMessage &message) +{ + CppTools::SymbolInfo result; + const SourceRangeContainer &range = message.sourceRange(); + + const SourceLocationContainer start = range.start(); + const SourceLocationContainer end = range.end(); + result.startLine = static_cast(start.line()); + result.startColumn = static_cast(start.column()); + result.endLine = static_cast(end.line()); + result.endColumn = static_cast(end.column()); + result.fileName = start.filePath(); + result.failedToFollow = message.failedToFollow(); + + return result; +} + void IpcReceiver::references(const ReferencesMessage &message) { qCDebug(log) << "<<< ReferencesMessage with" @@ -267,7 +300,14 @@ void IpcReceiver::followSymbol(const ClangBackEnd::FollowSymbolMessage &message) << message.sourceRange() << "range"; const quint64 ticket = message.ticketNumber(); - // TODO: add implementation + QFutureInterface futureInterface = m_followTable.take(ticket); + QTC_CHECK(futureInterface != QFutureInterface()); + + if (futureInterface.isCanceled()) + return; // Editor document closed or a new request was issued making this result outdated. + + futureInterface.reportResult(toSymbolInfo(message)); + futureInterface.reportFinished(); } class IpcSender : public IpcSenderInterface @@ -698,6 +738,23 @@ QFuture IpcCommunicator::requestReferences( return m_ipcReceiver.addExpectedReferencesMessage(message.ticketNumber(), textDocument); } +QFuture IpcCommunicator::requestFollowSymbol( + const FileContainer &curFileContainer, + const QVector &dependentFiles, + quint32 line, + quint32 column, + bool resolveTarget) +{ + const RequestFollowSymbolMessage message(curFileContainer, + dependentFiles, + line, + column, + resolveTarget); + m_ipcSender->requestFollowSymbol(message); + + return m_ipcReceiver.addExpectedRequestFollowSymbolMessage(message.ticketNumber()); +} + void IpcCommunicator::updateTranslationUnitWithRevisionCheck(Core::IDocument *document) { const auto textDocument = qobject_cast(document); diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.h b/src/plugins/clangcodemodel/clangbackendipcintegration.h index f040afe9331..6ebbeb2a2da 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.h +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -77,6 +78,7 @@ public: QFuture addExpectedReferencesMessage(quint64 ticket, QTextDocument *textDocument); + QFuture addExpectedRequestFollowSymbolMessage(quint64 ticket); bool isExpectingCodeCompletedMessage() const; void reset(); @@ -107,6 +109,8 @@ private: QPointer textDocument; }; QHash m_referencesTable; + + QHash> m_followTable; }; class IpcSenderInterface @@ -154,6 +158,11 @@ public: QFuture requestReferences(const FileContainer &fileContainer, quint32 line, quint32 column, QTextDocument *textDocument); + QFuture requestFollowSymbol(const FileContainer &curFileContainer, + const QVector &dependentFiles, + quint32 line, + quint32 column, + bool resolveTarget); void completeCode(ClangCompletionAssistProcessor *assistProcessor, const QString &filePath, quint32 line, quint32 column, diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index e29f72ae9ef..64a91a478c4 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -341,6 +341,55 @@ ClangEditorDocumentProcessor::cursorInfo(const CppTools::CursorInfoParams ¶m textDocument()); } +static QVector prioritizeByBaseName(const QString &curPath, + const ::Utils::FileNameList &fileDeps) +{ + QList dependentFiles; + dependentFiles.reserve(fileDeps.size()); + for (const ::Utils::FileName &dep: fileDeps) + dependentFiles.push_back(dep.toString()); + + const QString curFilename = QFileInfo(curPath).fileName(); + if (CppTools::ProjectFile::isHeader(CppTools::ProjectFile::classify(curFilename))) { + const QString withoutExt = QFileInfo(curFilename).baseName(); + int posToMove = 0; + // Move exact match to the first place and partial matches after it + for (int i = 0; i < dependentFiles.size(); ++i) { + const QString baseName = QFileInfo(dependentFiles[i]).baseName(); + if (withoutExt == baseName) { + dependentFiles.move(i, 0); + posToMove++; + continue; + } + if (baseName.contains(withoutExt)) + dependentFiles.move(i, posToMove++); + } + } + // Limit the number of scans (don't search for overrides) + if (dependentFiles.size() > 5) + dependentFiles.erase(dependentFiles.begin() + 5, dependentFiles.end()); + return QVector::fromList(dependentFiles); +} + +QFuture +ClangEditorDocumentProcessor::requestFollowSymbol(int line, int column, bool resolveTarget) +{ + QVector dependentFiles; + CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); + if (modelManager && !modelManager->projectPart(filePath()).isEmpty()) { + // This might be not so fast - index will change that + const ::Utils::FileNameList fileDeps + = modelManager->snapshot().filesDependingOn(filePath()); + dependentFiles = prioritizeByBaseName(filePath(), fileDeps); + } + + return m_ipcCommunicator.requestFollowSymbol(simpleFileContainer(), + dependentFiles, + static_cast(line), + static_cast(column), + resolveTarget); +} + ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const { return fileContainerWithArguments(m_projectPart.data()); diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index d7c27a79ff2..6255608dc23 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -86,6 +86,9 @@ public: void setParserConfig(const CppTools::BaseEditorDocumentParser::Configuration config) override; QFuture cursorInfo(const CppTools::CursorInfoParams ¶ms) override; + QFuture requestFollowSymbol(int line, + int column, + bool resolveTarget) override; ClangBackEnd::FileContainer fileContainerWithArguments() const; diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index d31a4eb899c..cfc7d33f38d 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -386,6 +386,12 @@ CppEditorDocument::cursorInfo(const CppTools::CursorInfoParams ¶ms) return processor()->cursorInfo(params); } +QFuture +CppEditorDocument::requestFollowSymbol(int line, int column, bool resolveTarget) +{ + return processor()->requestFollowSymbol(line, column, resolveTarget); +} + const MinimizableInfoBars &CppEditorDocument::minimizableInfoBars() const { return m_minimizableInfoBars; diff --git a/src/plugins/cppeditor/cppeditordocument.h b/src/plugins/cppeditor/cppeditordocument.h index e2e8a0a28e2..671141b00ad 100644 --- a/src/plugins/cppeditor/cppeditordocument.h +++ b/src/plugins/cppeditor/cppeditordocument.h @@ -67,6 +67,9 @@ public: ParseContextModel &parseContextModel(); QFuture cursorInfo(const CppTools::CursorInfoParams ¶ms); + QFuture requestFollowSymbol(int line, + int column, + bool resolveTarget = true); signals: void codeWarningsUpdated(unsigned contentsRevision, diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index 6e95cc63fca..4999a791a03 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -25,6 +25,7 @@ #include "cppfollowsymbolundercursor.h" #include "cppeditor.h" +#include "cppeditordocument.h" #include "cppvirtualfunctionassistprovider.h" #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include @@ -53,6 +55,12 @@ typedef TextEditorWidget::Link Link; namespace { +static bool useClangFollowSymbol() +{ + static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL"); + return use; +} + class VirtualFunctionHelper { public: VirtualFunctionHelper(TypeOfExpression &typeOfExpression, @@ -487,12 +495,56 @@ static int skipMatchingParentheses(const Tokens &tokens, int idx, int initialDep return j; } +bool FollowSymbolUnderCursor::processorFollowSymbol(uint line, uint column, bool resolveTarget, + Link &linkResult) +{ + if (!useClangFollowSymbol()) + return false; + CppEditorDocument* editorDocument = m_widget->cppEditorDocument(); + if (!editorDocument) + return false; + + QFuture info + = editorDocument->requestFollowSymbol(static_cast(line), + static_cast(column), + resolveTarget); + if (info.isCanceled()) + return false; + + while (!info.isFinished()) { + if (info.isCanceled()) + return false; + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } + CppTools::SymbolInfo result = info.result(); + + // Try again with built-in code model (happens with some includes) + if (result.failedToFollow) + return false; + + // We did not fail but the result is empty + if (result.fileName.isEmpty()) + return true; + + linkResult = Link(result.fileName, result.startLine, result.startColumn - 1); + + return true; +} + TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &cursor, bool resolveTarget, const Snapshot &theSnapshot, const Document::Ptr &documentFromSemanticInfo, SymbolFinder *symbolFinder, bool inNextSplit) { Link link; + int lineNumber = 0, positionInBlock = 0; + m_widget->convertPosition(cursor.position(), &lineNumber, &positionInBlock); + const unsigned line = lineNumber; + const unsigned column = positionInBlock + 1; + + if (resolveTarget && processorFollowSymbol(line, column, resolveTarget, link)) + return link; + Snapshot snapshot = theSnapshot; // Move to end of identifier @@ -519,11 +571,6 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs } } - int lineNumber = 0, positionInBlock = 0; - m_widget->convertPosition(cursor.position(), &lineNumber, &positionInBlock); - const unsigned line = lineNumber; - const unsigned column = positionInBlock + 1; - // Try to find a signal or slot inside SIGNAL() or SLOT() int beginOfToken = 0; int endOfToken = 0; diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.h b/src/plugins/cppeditor/cppfollowsymbolundercursor.h index 3121fe00e83..ee1e7433e1a 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.h +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.h @@ -57,6 +57,10 @@ public: void setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider); private: + // Try to follow symbol with clang processor + // Returns false if it has failed and we want to try again with built-in one + bool processorFollowSymbol(uint line, uint column, bool resolveTarget, + Link &result); CppEditorWidget *m_widget; VirtualFunctionAssistProvider *m_virtualFunctionAssistProvider; }; diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h index 3c9d864aeb9..342979dae35 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.h +++ b/src/plugins/cpptools/baseeditordocumentprocessor.h @@ -27,6 +27,7 @@ #include "baseeditordocumentparser.h" #include "cppcursorinfo.h" +#include "cppsymbolinfo.h" #include "cppsemanticinfo.h" #include "cpptools_global.h" @@ -75,6 +76,7 @@ public: virtual void setParserConfig(const BaseEditorDocumentParser::Configuration config); virtual QFuture cursorInfo(const CursorInfoParams ¶ms) = 0; + virtual QFuture requestFollowSymbol(int line, int column, bool resolveTarget) = 0; public: using HeaderErrorDiagnosticWidgetCreator = std::function; diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.h b/src/plugins/cpptools/builtineditordocumentprocessor.h index 32a50d6e23f..7d3d96f992f 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.h +++ b/src/plugins/cpptools/builtineditordocumentprocessor.h @@ -52,6 +52,8 @@ public: bool isParserRunning() const override; QFuture cursorInfo(const CursorInfoParams ¶ms) override; + QFuture requestFollowSymbol(int, int, bool) override + { return QFuture(); } private: void onParserFinished(CPlusPlus::Document::Ptr document, CPlusPlus::Snapshot snapshot); diff --git a/src/plugins/cpptools/cppsymbolinfo.h b/src/plugins/cpptools/cppsymbolinfo.h new file mode 100644 index 00000000000..17d2713bc37 --- /dev/null +++ b/src/plugins/cpptools/cppsymbolinfo.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "cpptools_global.h" + +#include + +namespace CppTools { + +class CPPTOOLS_EXPORT SymbolInfo +{ +public: + int startLine = 0; + int startColumn = 0; + int endLine = 0; + int endColumn = 0; + QString fileName; + bool failedToFollow = false; +}; + +} // namespace CppTools diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 8381a3fdfaa..e007c813f3a 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -87,6 +87,7 @@ HEADERS += \ cppprojectfilecategorizer.h \ clangcompileroptionsbuilder.h \ cppprojectpartchooser.h \ + cppsymbolinfo.h SOURCES += \ abstracteditorsupport.cpp \ diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp index 3da0575c058..d852c2a5463 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp @@ -264,7 +264,7 @@ void ClangCodeModelServer::requestReferences(const RequestReferencesMessage &mes void ClangCodeModelServer::requestFollowSymbol(const RequestFollowSymbolMessage &message) { - TIME_SCOPE_DURATION("ClangCodeModelServer::followSymbol"); + TIME_SCOPE_DURATION("ClangCodeModelServer::requestFollowSymbol"); try { auto projectPartId = message.fileContainer().projectPartId(); @@ -274,11 +274,12 @@ void ClangCodeModelServer::requestFollowSymbol(const RequestFollowSymbolMessage JobRequest jobRequest = processor.createJobRequest(JobRequest::Type::FollowSymbol); fillJobRequest(jobRequest, message); + jobRequest.dependentFiles = message.dependentFiles(); jobRequest.resolveTarget = message.resolveTarget(); processor.addJob(jobRequest); processor.process(); } catch (const std::exception &exception) { - qWarning() << "Error in ClangCodeModelServer::followSymbol:" << exception.what(); + qWarning() << "Error in ClangCodeModelServer::requestFollowSymbol:" << exception.what(); } } diff --git a/src/tools/clangbackend/ipcsource/clangfollowsymboljob.cpp b/src/tools/clangbackend/ipcsource/clangfollowsymboljob.cpp index 19c94f6f690..813f6f839ff 100644 --- a/src/tools/clangbackend/ipcsource/clangfollowsymboljob.cpp +++ b/src/tools/clangbackend/ipcsource/clangfollowsymboljob.cpp @@ -36,6 +36,7 @@ namespace ClangBackEnd { static FollowSymbolJob::AsyncResult runAsyncHelperFollow(const TranslationUnit &translationUnit, quint32 line, quint32 column, + const QVector &dependentFiles, bool resolveTarget) { TIME_SCOPE_DURATION("FollowSymbolJobRunner"); @@ -57,9 +58,10 @@ IAsyncJob::AsyncPrepareResult FollowSymbolJob::prepareAsyncRun() = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); const quint32 line = jobRequest.line; const quint32 column = jobRequest.column; + const QVector &dependentFiles = jobRequest.dependentFiles; const bool resolveTarget = jobRequest.resolveTarget; - setRunner([translationUnit, line, column, resolveTarget]() { - return runAsyncHelperFollow(translationUnit, line, column, resolveTarget); + setRunner([translationUnit, line, column, dependentFiles, resolveTarget]() { + return runAsyncHelperFollow(translationUnit, line, column, dependentFiles, resolveTarget); }); return AsyncPrepareResult{translationUnit.id()}; diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h index 53dca6087cb..2f096c0d9f8 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.h +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h @@ -103,6 +103,7 @@ public: quint32 line = 0; quint32 column = 0; quint64 ticketNumber = 0; + QVector dependentFiles; bool resolveTarget = true; };