forked from qt-creator/qt-creator
Clang: Use current TranslationUnit follow symbol based on clang code model
It might be quite a safe replacement which can fix builtin code model issues. If clang code model fails to follow symbol or does not find a definition when it's required we fall back to the built-in code model to proceed with project-wide follow symbol. To make it almost a full replacement tweak include paths underline on cursor hover to match what we have in built-in code model. SIGNAL/SLOTS macros are not yet supported but can be handled in follow up patch. Task-number: QTCREATORBUG-19477 Change-Id: Id1611511d661a8aaf3e93502b4e03e1792c7c1d3 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
@@ -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<ClangBackEnd::TokenInfoContainer> &marks,
|
||||
int currentIndex)
|
||||
{
|
||||
int startIndex = currentIndex - 1;
|
||||
while (startIndex >= 0 && isValidIncludePathToken(marks[startIndex]))
|
||||
--startIndex;
|
||||
return startIndex + 1;
|
||||
}
|
||||
|
||||
static int includePathEndIndex(const QVector<ClangBackEnd::TokenInfoContainer> &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 <path/file.h>' 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;
|
||||
}
|
||||
if (mark.extraInfo.identifier || tokenStr == "operator")
|
||||
return token;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
bool inNextSplit) override;
|
||||
private:
|
||||
using FutureSymbolWatcher = QFutureWatcher<CppTools::SymbolInfo>;
|
||||
FutureSymbolWatcher m_watcher;
|
||||
std::unique_ptr<FutureSymbolWatcher> m_watcher;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -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<ClangCurrentDocumentFilter>());
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user