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();
|
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)
|
ClangEditorDocumentProcessor *processor)
|
||||||
{
|
{
|
||||||
using Link = Utils::Link;
|
using Link = Utils::Link;
|
||||||
@@ -77,18 +107,35 @@ static Utils::Link linkAtCursor(QTextCursor cursor, const QString &filePath, uin
|
|||||||
if (!findMark(marks, line, column, mark))
|
if (!findMark(marks, line, column, mark))
|
||||||
return Link();
|
return Link();
|
||||||
|
|
||||||
cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
if (mark.extraInfo.includeDirectivePath && !isValidIncludePathToken(mark))
|
||||||
const QString tokenStr = cursor.selectedText();
|
return Link();
|
||||||
|
|
||||||
Link token(filePath, mark.line, mark.column);
|
Link token(filePath, mark.line, mark.column);
|
||||||
token.linkTextStart = getMarkPos(cursor, mark);
|
token.linkTextStart = getMarkPos(cursor, mark);
|
||||||
token.linkTextEnd = token.linkTextStart + mark.length;
|
token.linkTextEnd = token.linkTextStart + mark.length;
|
||||||
|
|
||||||
if (mark.extraInfo.includeDirectivePath) {
|
if (mark.extraInfo.includeDirectivePath) {
|
||||||
if (tokenStr != "include" && tokenStr != "#" && tokenStr != "<")
|
// Tweak include paths to cover everything between "" or <>.
|
||||||
return token;
|
if (mark.extraInfo.token.startsWith("\"")) {
|
||||||
return Link();
|
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 token;
|
||||||
return Link();
|
return Link();
|
||||||
}
|
}
|
||||||
@@ -127,11 +174,16 @@ void ClangFollowSymbol::findLink(const CppTools::CursorInEditor &data,
|
|||||||
if (infoFuture.isCanceled())
|
if (infoFuture.isCanceled())
|
||||||
return processLinkCallback(Utils::Link());
|
return processLinkCallback(Utils::Link());
|
||||||
|
|
||||||
QObject::connect(&m_watcher, &FutureSymbolWatcher::finished,
|
if (m_watcher)
|
||||||
[=, watcher=&m_watcher, callback=std::move(processLinkCallback)]() mutable {
|
m_watcher->cancel();
|
||||||
if (watcher->isCanceled())
|
|
||||||
|
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());
|
return callback(Utils::Link());
|
||||||
CppTools::SymbolInfo result = watcher->result();
|
CppTools::SymbolInfo result = m_watcher->result();
|
||||||
// We did not fail but the result is empty
|
// We did not fail but the result is empty
|
||||||
if (result.fileName.isEmpty()) {
|
if (result.fileName.isEmpty()) {
|
||||||
const CppTools::RefactoringEngineInterface &refactoringEngine
|
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
|
} // namespace Internal
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public:
|
|||||||
bool inNextSplit) override;
|
bool inNextSplit) override;
|
||||||
private:
|
private:
|
||||||
using FutureSymbolWatcher = QFutureWatcher<CppTools::SymbolInfo>;
|
using FutureSymbolWatcher = QFutureWatcher<CppTools::SymbolInfo>;
|
||||||
FutureSymbolWatcher m_watcher;
|
std::unique_ptr<FutureSymbolWatcher> m_watcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -64,12 +64,6 @@ using namespace ClangCodeModel::Internal;
|
|||||||
|
|
||||||
static ModelManagerSupportClang *m_instance = 0;
|
static ModelManagerSupportClang *m_instance = 0;
|
||||||
|
|
||||||
static bool useClangFollowSymbol()
|
|
||||||
{
|
|
||||||
static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL");
|
|
||||||
return use;
|
|
||||||
}
|
|
||||||
|
|
||||||
static CppTools::CppModelManager *cppModelManager()
|
static CppTools::CppModelManager *cppModelManager()
|
||||||
{
|
{
|
||||||
return CppTools::CppModelManager::instance();
|
return CppTools::CppModelManager::instance();
|
||||||
@@ -77,16 +71,12 @@ static CppTools::CppModelManager *cppModelManager()
|
|||||||
|
|
||||||
ModelManagerSupportClang::ModelManagerSupportClang()
|
ModelManagerSupportClang::ModelManagerSupportClang()
|
||||||
: m_completionAssistProvider(m_communicator)
|
: m_completionAssistProvider(m_communicator)
|
||||||
|
, m_followSymbol(new ClangFollowSymbol)
|
||||||
, m_refactoringEngine(new RefactoringEngine)
|
, m_refactoringEngine(new RefactoringEngine)
|
||||||
{
|
{
|
||||||
QTC_CHECK(!m_instance);
|
QTC_CHECK(!m_instance);
|
||||||
m_instance = this;
|
m_instance = this;
|
||||||
|
|
||||||
if (useClangFollowSymbol())
|
|
||||||
m_followSymbol.reset(new ClangFollowSymbol);
|
|
||||||
else
|
|
||||||
m_followSymbol.reset(new CppTools::FollowSymbolUnderCursor);
|
|
||||||
|
|
||||||
CppTools::CppModelManager::instance()->setCurrentDocumentFilter(
|
CppTools::CppModelManager::instance()->setCurrentDocumentFilter(
|
||||||
std::make_unique<ClangCurrentDocumentFilter>());
|
std::make_unique<ClangCurrentDocumentFilter>());
|
||||||
|
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot
|
|||||||
|
|
||||||
DocumentProcessor processor = documentProcessors().processor(document);
|
DocumentProcessor processor = documentProcessors().processor(document);
|
||||||
processor.addJob(JobRequest::Type::RequestDocumentAnnotations);
|
processor.addJob(JobRequest::Type::RequestDocumentAnnotations);
|
||||||
|
processor.addJob(JobRequest::Type::UpdateExtraDocumentAnnotations);
|
||||||
processor.process();
|
processor.process();
|
||||||
} catch (const std::exception &exception) {
|
} catch (const std::exception &exception) {
|
||||||
qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what();
|
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;
|
static constexpr int AnnotationJobsMultiplier = 2;
|
||||||
|
|
||||||
class ClangCodeModelServer : public ::testing::Test
|
class ClangCodeModelServer : public ::testing::Test
|
||||||
@@ -675,7 +686,7 @@ void ClangCodeModelServer::expectDocumentAnnotationsChangedForFileBWithSpecificH
|
|||||||
EXPECT_CALL(mockClangCodeModelClient,
|
EXPECT_CALL(mockClangCodeModelClient,
|
||||||
documentAnnotationsChanged(
|
documentAnnotationsChanged(
|
||||||
Field(&DocumentAnnotationsChangedMessage::tokenInfos,
|
Field(&DocumentAnnotationsChangedMessage::tokenInfos,
|
||||||
Contains(tokenInfo))));
|
PartlyContains(tokenInfo)))).Times(AnnotationJobsMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangCodeModelServer::updateUnsavedContent(const Utf8String &filePath,
|
void ClangCodeModelServer::updateUnsavedContent(const Utf8String &filePath,
|
||||||
|
|||||||
Reference in New Issue
Block a user