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:
Ivan Donchevskii
2018-01-05 10:50:37 +01:00
parent 388713df12
commit 7b2774dea1
5 changed files with 79 additions and 25 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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>());

View File

@@ -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();

View File

@@ -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,