forked from qt-creator/qt-creator
ClangCodeModel: Implement following a symbol to its type
Making use of LSP's "Go To Type Definition". Just the backend for now, UI to follow. Change-Id: Id73b2cf701eab03913477f6d4d3093e257e80dbd Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -822,6 +822,7 @@ void ClangdClient::followSymbol(TextDocument *document,
|
|||||||
CppEditor::CppEditorWidget *editorWidget,
|
CppEditor::CppEditorWidget *editorWidget,
|
||||||
const Utils::LinkHandler &callback,
|
const Utils::LinkHandler &callback,
|
||||||
bool resolveTarget,
|
bool resolveTarget,
|
||||||
|
FollowTo followTo,
|
||||||
bool openInSplit
|
bool openInSplit
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -839,7 +840,7 @@ void ClangdClient::followSymbol(TextDocument *document,
|
|||||||
qCDebug(clangdLog) << "follow symbol requested" << document->filePath()
|
qCDebug(clangdLog) << "follow symbol requested" << document->filePath()
|
||||||
<< adjustedCursor.blockNumber() << adjustedCursor.positionInBlock();
|
<< adjustedCursor.blockNumber() << adjustedCursor.positionInBlock();
|
||||||
d->followSymbol = new ClangdFollowSymbol(this, adjustedCursor, editorWidget, document, callback,
|
d->followSymbol = new ClangdFollowSymbol(this, adjustedCursor, editorWidget, document, callback,
|
||||||
openInSplit);
|
followTo, openInSplit);
|
||||||
connect(d->followSymbol, &ClangdFollowSymbol::done, this, [this] {
|
connect(d->followSymbol, &ClangdFollowSymbol::done, this, [this] {
|
||||||
d->followSymbol->deleteLater();
|
d->followSymbol->deleteLater();
|
||||||
d->followSymbol = nullptr;
|
d->followSymbol = nullptr;
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ Q_DECLARE_LOGGING_CATEGORY(clangdLogAst);
|
|||||||
|
|
||||||
void setupClangdConfigFile();
|
void setupClangdConfigFile();
|
||||||
|
|
||||||
|
enum class FollowTo { SymbolDef, SymbolType };
|
||||||
|
|
||||||
class ClangdClient : public LanguageClient::Client
|
class ClangdClient : public LanguageClient::Client
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -55,6 +57,7 @@ public:
|
|||||||
CppEditor::CppEditorWidget *editorWidget,
|
CppEditor::CppEditorWidget *editorWidget,
|
||||||
const Utils::LinkHandler &callback,
|
const Utils::LinkHandler &callback,
|
||||||
bool resolveTarget,
|
bool resolveTarget,
|
||||||
|
FollowTo followTo,
|
||||||
bool openInSplit);
|
bool openInSplit);
|
||||||
|
|
||||||
void switchDeclDef(TextEditor::TextDocument *document,
|
void switchDeclDef(TextEditor::TextDocument *document,
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ public:
|
|||||||
docRevision(editorWidget ? editorWidget->textDocument()->document()->revision() : -1),
|
docRevision(editorWidget ? editorWidget->textDocument()->document()->revision() : -1),
|
||||||
openInSplit(openInSplit) {}
|
openInSplit(openInSplit) {}
|
||||||
|
|
||||||
|
void goToTypeDefinition();
|
||||||
void handleGotoDefinitionResult();
|
void handleGotoDefinitionResult();
|
||||||
void sendGotoImplementationRequest(const Utils::Link &link);
|
void sendGotoImplementationRequest(const Utils::Link &link);
|
||||||
void handleGotoImplementationResult(const GotoImplementationRequest::Response &response);
|
void handleGotoImplementationResult(const GotoImplementationRequest::Response &response);
|
||||||
@@ -118,7 +119,7 @@ public:
|
|||||||
|
|
||||||
ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor,
|
ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor,
|
||||||
CppEditorWidget *editorWidget, TextDocument *document, const LinkHandler &callback,
|
CppEditorWidget *editorWidget, TextDocument *document, const LinkHandler &callback,
|
||||||
bool openInSplit)
|
FollowTo followTo, bool openInSplit)
|
||||||
: QObject(client),
|
: QObject(client),
|
||||||
d(new Private(this, client, cursor, editorWidget, document->filePath(), callback,
|
d(new Private(this, client, cursor, editorWidget, document->filePath(), callback,
|
||||||
openInSplit))
|
openInSplit))
|
||||||
@@ -133,6 +134,11 @@ ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor &
|
|||||||
d->focusChangedConnection = connect(qApp, &QApplication::focusChanged,
|
d->focusChangedConnection = connect(qApp, &QApplication::focusChanged,
|
||||||
this, [this] { emitDone(); }, Qt::QueuedConnection);
|
this, [this] { emitDone(); }, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
if (followTo == FollowTo::SymbolType) {
|
||||||
|
d->goToTypeDefinition();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Step 1: Follow the symbol via "Go to Definition". At the same time, request the
|
// Step 1: Follow the symbol via "Go to Definition". At the same time, request the
|
||||||
// AST node corresponding to the cursor position, so we can find out whether
|
// AST node corresponding to the cursor position, so we can find out whether
|
||||||
// we have to look for overrides.
|
// we have to look for overrides.
|
||||||
@@ -353,6 +359,30 @@ ClangdFollowSymbol::VirtualFunctionAssistProvider::createProcessor(const AssistI
|
|||||||
= new VirtualFunctionAssistProcessor(m_followSymbol);
|
= new VirtualFunctionAssistProcessor(m_followSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangdFollowSymbol::Private::goToTypeDefinition()
|
||||||
|
{
|
||||||
|
GotoTypeDefinitionRequest req(TextDocumentPositionParams(TextDocumentIdentifier{uri},
|
||||||
|
Position(cursor)));
|
||||||
|
req.setResponseCallback([sentinel = QPointer(q), this, reqId = req.id()]
|
||||||
|
(const GotoTypeDefinitionRequest::Response &response) {
|
||||||
|
qCDebug(clangdLog) << "received go to type definition reply";
|
||||||
|
if (!sentinel)
|
||||||
|
return;
|
||||||
|
Link link;
|
||||||
|
if (const std::optional<GotoResult> &result = response.result()) {
|
||||||
|
if (const auto ploc = std::get_if<Location>(&*result)) {
|
||||||
|
link = {ploc->toLink()};
|
||||||
|
} else if (const auto plloc = std::get_if<QList<Location>>(&*result)) {
|
||||||
|
if (!plloc->empty())
|
||||||
|
link = plloc->first().toLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q->emitDone(link);
|
||||||
|
});
|
||||||
|
client->sendMessage(req, ClangdClient::SendDocUpdates::Ignore);
|
||||||
|
qCDebug(clangdLog) << "sending go to type definition request";
|
||||||
|
}
|
||||||
|
|
||||||
void ClangdFollowSymbol::Private::handleGotoDefinitionResult()
|
void ClangdFollowSymbol::Private::handleGotoDefinitionResult()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(defLink.hasValidTarget(), return);
|
QTC_ASSERT(defLink.hasValidTarget(), return);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ QT_END_NAMESPACE
|
|||||||
namespace ClangCodeModel::Internal {
|
namespace ClangCodeModel::Internal {
|
||||||
class ClangdAstNode;
|
class ClangdAstNode;
|
||||||
class ClangdClient;
|
class ClangdClient;
|
||||||
|
enum class FollowTo;
|
||||||
|
|
||||||
class ClangdFollowSymbol : public QObject
|
class ClangdFollowSymbol : public QObject
|
||||||
{
|
{
|
||||||
@@ -25,7 +26,7 @@ public:
|
|||||||
ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor,
|
ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor,
|
||||||
CppEditor::CppEditorWidget *editorWidget,
|
CppEditor::CppEditorWidget *editorWidget,
|
||||||
TextEditor::TextDocument *document, const Utils::LinkHandler &callback,
|
TextEditor::TextDocument *document, const Utils::LinkHandler &callback,
|
||||||
bool openInSplit);
|
FollowTo followTo, bool openInSplit);
|
||||||
~ClangdFollowSymbol();
|
~ClangdFollowSymbol();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ void ClangdSwitchDeclDef::Private::handleDeclDefSwitchReplies()
|
|||||||
const QTextCursor funcNameCursor = cursorForFunctionName(*functionNode);
|
const QTextCursor funcNameCursor = cursorForFunctionName(*functionNode);
|
||||||
if (!funcNameCursor.isNull()) {
|
if (!funcNameCursor.isNull()) {
|
||||||
client->followSymbol(document.data(), funcNameCursor, editorWidget, callback,
|
client->followSymbol(document.data(), funcNameCursor, editorWidget, callback,
|
||||||
true, false);
|
true, FollowTo::SymbolDef, false);
|
||||||
}
|
}
|
||||||
q->emitDone();
|
q->emitDone();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &dat
|
|||||||
if (ClangdClient * const client = clientForFile(data.filePath());
|
if (ClangdClient * const client = clientForFile(data.filePath());
|
||||||
client && client->isFullyIndexed()) {
|
client && client->isFullyIndexed()) {
|
||||||
client->followSymbol(data.textDocument(), data.cursor(), data.editorWidget(),
|
client->followSymbol(data.textDocument(), data.cursor(), data.editorWidget(),
|
||||||
processLinkCallback, resolveTarget, inNextSplit);
|
processLinkCallback, resolveTarget, FollowTo::SymbolDef, inNextSplit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -313,38 +313,50 @@ void ClangdTestFollowSymbol::test_data()
|
|||||||
QTest::addColumn<QString>("targetFile");
|
QTest::addColumn<QString>("targetFile");
|
||||||
QTest::addColumn<int>("targetLine");
|
QTest::addColumn<int>("targetLine");
|
||||||
QTest::addColumn<int>("targetColumn");
|
QTest::addColumn<int>("targetColumn");
|
||||||
|
QTest::addColumn<bool>("goToType");
|
||||||
|
|
||||||
QTest::newRow("on namespace") << "main.cpp" << 27 << 1 << "header.h" << 28 << 11;
|
QTest::newRow("on namespace") << "main.cpp" << 27 << 1 << "header.h" << 28 << 11 << false;
|
||||||
QTest::newRow("class ref") << "main.cpp" << 27 << 9 << "header.h" << 34 << 7;
|
QTest::newRow("class ref") << "main.cpp" << 27 << 9 << "header.h" << 34 << 7 << false;
|
||||||
QTest::newRow("forward decl (same file)") << "header.h" << 32 << 7 << "header.h" << 34 << 7;
|
QTest::newRow("forward decl (same file)") << "header.h" << 32 << 7 << "header.h" << 34 << 7
|
||||||
QTest::newRow("forward decl (different file") << "header.h" << 48 << 9 << "main.cpp" << 54 << 7;
|
<< false;
|
||||||
QTest::newRow("class definition (same file)") << "header.h" << 34 << 7 << "header.h" << 32 << 7;
|
QTest::newRow("forward decl (different file") << "header.h" << 48 << 9 << "main.cpp" << 54 << 7
|
||||||
|
<< false;
|
||||||
|
QTest::newRow("class definition (same file)") << "header.h" << 34 << 7 << "header.h" << 32 << 7
|
||||||
|
<< false;
|
||||||
QTest::newRow("class definition (different file)") << "main.cpp" << 54 << 7
|
QTest::newRow("class definition (different file)") << "main.cpp" << 54 << 7
|
||||||
<< "header.h" << 48 << 7;
|
<< "header.h" << 48 << 7 << false;
|
||||||
QTest::newRow("constructor decl") << "header.h" << 36 << 5 << "main.cpp" << 27 << 14;
|
QTest::newRow("constructor decl") << "header.h" << 36 << 5 << "main.cpp" << 27 << 14 << false;
|
||||||
QTest::newRow("constructor definition") << "main.cpp" << 27 << 14 << "header.h" << 36 << 5;
|
QTest::newRow("constructor definition") << "main.cpp" << 27 << 14 << "header.h" << 36 << 5
|
||||||
QTest::newRow("member ref") << "main.cpp" << 39 << 10 << "header.h" << 38 << 18;
|
<< false;
|
||||||
QTest::newRow("union member ref") << "main.cpp" << 91 << 20 << "main.cpp" << 86 << 13;
|
QTest::newRow("member ref") << "main.cpp" << 39 << 10 << "header.h" << 38 << 18 << false;
|
||||||
QTest::newRow("member decl") << "header.h" << 38 << 18 << "header.h" << 38 << 18;
|
QTest::newRow("union member ref") << "main.cpp" << 91 << 20 << "main.cpp" << 86 << 13 << false;
|
||||||
QTest::newRow("function ref") << "main.cpp" << 66 << 12 << "main.cpp" << 35 << 5;
|
QTest::newRow("member decl") << "header.h" << 38 << 18 << "header.h" << 38 << 18 << false;
|
||||||
QTest::newRow("member function ref") << "main.cpp" << 42 << 12 << "main.cpp" << 49 << 21;
|
QTest::newRow("function ref") << "main.cpp" << 66 << 12 << "main.cpp" << 35 << 5 << false;
|
||||||
QTest::newRow("function with no def ref") << "main.cpp" << 43 << 5 << "header.h" << 59 << 5;
|
QTest::newRow("member function ref") << "main.cpp" << 42 << 12 << "main.cpp" << 49 << 21
|
||||||
QTest::newRow("function def") << "main.cpp" << 35 << 5 << "header.h" << 52 << 5;
|
<< false;
|
||||||
QTest::newRow("member function def") << "main.cpp" << 49 << 21 << "header.h" << 43 << 9;
|
QTest::newRow("function with no def ref") << "main.cpp" << 43 << 5 << "header.h" << 59 << 5
|
||||||
QTest::newRow("member function decl") << "header.h" << 43 << 9 << "main.cpp" << 49 << 21;
|
<< false;
|
||||||
QTest::newRow("include") << "main.cpp" << 25 << 13 << "header.h" << 1 << 1;
|
QTest::newRow("function def") << "main.cpp" << 35 << 5 << "header.h" << 52 << 5 << false;
|
||||||
QTest::newRow("local var") << "main.cpp" << 39 << 6 << "main.cpp" << 36 << 9;
|
QTest::newRow("member function def") << "main.cpp" << 49 << 21 << "header.h" << 43 << 9
|
||||||
QTest::newRow("alias") << "main.cpp" << 36 << 5 << "main.cpp" << 33 << 7;
|
<< false;
|
||||||
QTest::newRow("static var") << "main.cpp" << 40 << 27 << "header.h" << 30 << 7;
|
QTest::newRow("member function decl") << "header.h" << 43 << 9 << "main.cpp" << 49 << 21
|
||||||
|
<< false;
|
||||||
|
QTest::newRow("include") << "main.cpp" << 25 << 13 << "header.h" << 1 << 1 << false;
|
||||||
|
QTest::newRow("local var") << "main.cpp" << 39 << 6 << "main.cpp" << 36 << 9 << false;
|
||||||
|
QTest::newRow("alias") << "main.cpp" << 36 << 5 << "main.cpp" << 33 << 7 << false;
|
||||||
|
QTest::newRow("static var") << "main.cpp" << 40 << 27 << "header.h" << 30 << 7 << false;
|
||||||
QTest::newRow("member function ref (other class)") << "main.cpp" << 62 << 39
|
QTest::newRow("member function ref (other class)") << "main.cpp" << 62 << 39
|
||||||
<< "cursor.cpp" << 104 << 22;
|
<< "cursor.cpp" << 104 << 22 << false;
|
||||||
QTest::newRow("macro ref") << "main.cpp" << 66 << 43 << "header.h" << 27 << 9;
|
QTest::newRow("macro ref") << "main.cpp" << 66 << 43 << "header.h" << 27 << 9 << false;
|
||||||
QTest::newRow("on namespace 2") << "main.cpp" << 27 << 3 << "header.h" << 28 << 11;
|
QTest::newRow("on namespace 2") << "main.cpp" << 27 << 3 << "header.h" << 28 << 11 << false;
|
||||||
QTest::newRow("after namespace") << "main.cpp" << 27 << 7 << "header.h" << 28 << 11;
|
QTest::newRow("after namespace") << "main.cpp" << 27 << 7 << "header.h" << 28 << 11 << false;
|
||||||
QTest::newRow("operator def") << "main.cpp" << 76 << 13 << "main.cpp" << 72 << 9;
|
QTest::newRow("operator def") << "main.cpp" << 76 << 13 << "main.cpp" << 72 << 9 << false;
|
||||||
QTest::newRow("operator def 2") << "main.cpp" << 80 << 15 << "main.cpp" << 73 << 10;
|
QTest::newRow("operator def 2") << "main.cpp" << 80 << 15 << "main.cpp" << 73 << 10 << false;
|
||||||
QTest::newRow("operator decl") << "main.cpp" << 72 << 12 << "main.cpp" << 76 << 10;
|
QTest::newRow("operator decl") << "main.cpp" << 72 << 12 << "main.cpp" << 76 << 10 << false;
|
||||||
QTest::newRow("operator decl 2") << "main.cpp" << 73 << 12 << "main.cpp" << 80 << 11;
|
QTest::newRow("operator decl 2") << "main.cpp" << 73 << 12 << "main.cpp" << 80 << 11 << false;
|
||||||
|
|
||||||
|
QTest::newRow("go to typedef") << "main.cpp" << 100 << 19 << "main.cpp" << 33 << 7 << true;
|
||||||
|
QTest::newRow("go to type") << "main.cpp" << 101 << 19 << "main.cpp" << 69 << 7 << true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdTestFollowSymbol::test()
|
void ClangdTestFollowSymbol::test()
|
||||||
@@ -355,6 +367,7 @@ void ClangdTestFollowSymbol::test()
|
|||||||
QFETCH(QString, targetFile);
|
QFETCH(QString, targetFile);
|
||||||
QFETCH(int, targetLine);
|
QFETCH(int, targetLine);
|
||||||
QFETCH(int, targetColumn);
|
QFETCH(int, targetColumn);
|
||||||
|
QFETCH(bool, goToType);
|
||||||
|
|
||||||
TextEditor::TextDocument * const doc = document(sourceFile);
|
TextEditor::TextDocument * const doc = document(sourceFile);
|
||||||
QVERIFY(doc);
|
QVERIFY(doc);
|
||||||
@@ -371,7 +384,8 @@ void ClangdTestFollowSymbol::test()
|
|||||||
QTextCursor cursor(doc->document());
|
QTextCursor cursor(doc->document());
|
||||||
const int pos = Utils::Text::positionInText(doc->document(), sourceLine, sourceColumn);
|
const int pos = Utils::Text::positionInText(doc->document(), sourceLine, sourceColumn);
|
||||||
cursor.setPosition(pos);
|
cursor.setPosition(pos);
|
||||||
client()->followSymbol(doc, cursor, nullptr, handler, true, false);
|
client()->followSymbol(doc, cursor, nullptr, handler, true,
|
||||||
|
goToType ? FollowTo::SymbolType : FollowTo::SymbolDef, false);
|
||||||
timer.start(10000);
|
timer.start(10000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
QVERIFY(timer.isActive());
|
QVERIFY(timer.isActive());
|
||||||
|
|||||||
@@ -92,4 +92,10 @@ struct S {
|
|||||||
int i = 42;
|
int i = 42;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
YYY getYYY() const { return YYY(); }
|
||||||
|
Bar getBar() const { return Bar(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const auto yyyVar = S().getYYY();
|
||||||
|
static const auto barVar = S().getBar();
|
||||||
|
|||||||
Reference in New Issue
Block a user