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

View File

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

View File

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

View File

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

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