forked from qt-creator/qt-creator
ClangCodeModel: Refer to clangd for the AST node under the cursor
The question which concrete AST node corresponds to a given cursor position is surprisingly difficult to answer, and clangd already has a lot of code for this. Therefore, we always refer to clangd to get a concrete node, even if we already have the full AST. Change-Id: I5d1528d776ee459a53b8e650a616ea7019ec59bf Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -316,14 +316,6 @@ static QList<AstNode> getAstPath(const AstNode &root, const Range &range)
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AstNode getAstNode(const AstNode &root, const Range &range)
|
|
||||||
{
|
|
||||||
const QList<AstNode> path = getAstPath(root, range);
|
|
||||||
if (!path.isEmpty())
|
|
||||||
return path.last();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static Usage::Type getUsageType(const QList<AstNode> &path)
|
static Usage::Type getUsageType(const QList<AstNode> &path)
|
||||||
{
|
{
|
||||||
bool potentialWrite = false;
|
bool potentialWrite = false;
|
||||||
@@ -926,7 +918,7 @@ public:
|
|||||||
using TextDocOrFile = const Utils::variant<const TextDocument *, Utils::FilePath>;
|
using TextDocOrFile = const Utils::variant<const TextDocument *, Utils::FilePath>;
|
||||||
using AstHandler = const std::function<void(const AstNode &ast, const MessageId &)>;
|
using AstHandler = const std::function<void(const AstNode &ast, const MessageId &)>;
|
||||||
MessageId getAndHandleAst(TextDocOrFile &doc, AstHandler &astHandler,
|
MessageId getAndHandleAst(TextDocOrFile &doc, AstHandler &astHandler,
|
||||||
AstCallbackMode callbackMode);
|
AstCallbackMode callbackMode, const Range &range = {});
|
||||||
|
|
||||||
ClangdClient * const q;
|
ClangdClient * const q;
|
||||||
const CppEditor::ClangdSettings::Data settings;
|
const CppEditor::ClangdSettings::Data settings;
|
||||||
@@ -1571,16 +1563,16 @@ void ClangdClient::followSymbol(TextDocument *document,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto astHandler = [this, id = d->followSymbolData->id, range = Range(cursor)]
|
const auto astHandler = [this, id = d->followSymbolData->id]
|
||||||
(const AstNode &ast, const MessageId &) {
|
(const AstNode &ast, const MessageId &) {
|
||||||
qCDebug(clangdLog) << "received ast response for cursor";
|
qCDebug(clangdLog) << "received ast response for cursor";
|
||||||
if (!d->followSymbolData || d->followSymbolData->id != id)
|
if (!d->followSymbolData || d->followSymbolData->id != id)
|
||||||
return;
|
return;
|
||||||
d->followSymbolData->cursorNode = getAstNode(ast, range);
|
d->followSymbolData->cursorNode = ast;
|
||||||
if (d->followSymbolData->defLink.hasValidTarget())
|
if (d->followSymbolData->defLink.hasValidTarget())
|
||||||
d->handleGotoDefinitionResult();
|
d->handleGotoDefinitionResult();
|
||||||
};
|
};
|
||||||
d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::SyncIfPossible);
|
d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::AlwaysAsync, Range(cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor,
|
void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor,
|
||||||
@@ -2030,18 +2022,19 @@ void ClangdClient::Private::handleGotoImplementationResult(
|
|||||||
: TextDocOrFile(defLinkFilePath);
|
: TextDocOrFile(defLinkFilePath);
|
||||||
const Position defLinkPos(followSymbolData->defLink.targetLine - 1,
|
const Position defLinkPos(followSymbolData->defLink.targetLine - 1,
|
||||||
followSymbolData->defLink.targetColumn);
|
followSymbolData->defLink.targetColumn);
|
||||||
const auto astHandler = [this, range = Range(defLinkPos, defLinkPos), id = followSymbolData->id]
|
const auto astHandler = [this, id = followSymbolData->id]
|
||||||
(const AstNode &ast, const MessageId &) {
|
(const AstNode &ast, const MessageId &) {
|
||||||
qCDebug(clangdLog) << "received ast response for def link";
|
qCDebug(clangdLog) << "received ast response for def link";
|
||||||
if (!followSymbolData || followSymbolData->id != id)
|
if (!followSymbolData || followSymbolData->id != id)
|
||||||
return;
|
return;
|
||||||
followSymbolData->defLinkNode = getAstNode(ast, range);
|
followSymbolData->defLinkNode = ast;
|
||||||
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
||||||
&& followSymbolData->pendingGotoDefRequests.isEmpty()) {
|
&& followSymbolData->pendingGotoDefRequests.isEmpty()) {
|
||||||
handleDocumentInfoResults();
|
handleDocumentInfoResults();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
getAndHandleAst(defLinkDocVariant, astHandler, AstCallbackMode::SyncIfPossible);
|
getAndHandleAst(defLinkDocVariant, astHandler, AstCallbackMode::AlwaysAsync,
|
||||||
|
Range(defLinkPos, defLinkPos));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::Private::handleDocumentInfoResults()
|
void ClangdClient::Private::handleDocumentInfoResults()
|
||||||
@@ -2727,26 +2720,30 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
|
|||||||
|
|
||||||
MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
|
MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
|
||||||
const AstHandler &astHandler,
|
const AstHandler &astHandler,
|
||||||
AstCallbackMode callbackMode)
|
AstCallbackMode callbackMode, const Range &range)
|
||||||
{
|
{
|
||||||
const auto textDocPtr = Utils::get_if<const TextDocument *>(&doc);
|
const auto textDocPtr = Utils::get_if<const TextDocument *>(&doc);
|
||||||
const TextDocument * const textDoc = textDocPtr ? *textDocPtr : nullptr;
|
const TextDocument * const textDoc = textDocPtr ? *textDocPtr : nullptr;
|
||||||
const Utils::FilePath filePath = textDoc ? textDoc->filePath()
|
const Utils::FilePath filePath = textDoc ? textDoc->filePath()
|
||||||
: Utils::get<Utils::FilePath>(doc);
|
: Utils::get<Utils::FilePath>(doc);
|
||||||
|
|
||||||
// If the document's AST is in the cache and is up to date, call the handler.
|
// If the entire AST is requested and the document's AST is in the cache and it is up to date,
|
||||||
if (const auto ast = textDoc ? astCache.get(textDoc) : externalAstCache.get(filePath)) {
|
// call the handler.
|
||||||
qCDebug(clangdLog) << "using AST from cache";
|
const bool fullAstRequested = !range.isValid();
|
||||||
switch (callbackMode) {
|
if (fullAstRequested) {
|
||||||
case AstCallbackMode::SyncIfPossible:
|
if (const auto ast = textDoc ? astCache.get(textDoc) : externalAstCache.get(filePath)) {
|
||||||
astHandler(*ast, {});
|
qCDebug(clangdLog) << "using AST from cache";
|
||||||
break;
|
switch (callbackMode) {
|
||||||
case AstCallbackMode::AlwaysAsync:
|
case AstCallbackMode::SyncIfPossible:
|
||||||
QMetaObject::invokeMethod(q, [ast, astHandler] { astHandler(*ast, {}); },
|
astHandler(*ast, {});
|
||||||
|
break;
|
||||||
|
case AstCallbackMode::AlwaysAsync:
|
||||||
|
QMetaObject::invokeMethod(q, [ast, astHandler] { astHandler(*ast, {}); },
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise retrieve the AST from clangd.
|
// Otherwise retrieve the AST from clangd.
|
||||||
@@ -2754,7 +2751,6 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
|
|||||||
class AstParams : public JsonObject
|
class AstParams : public JsonObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AstParams() {}
|
|
||||||
AstParams(const TextDocumentIdentifier &document, const Range &range = {})
|
AstParams(const TextDocumentIdentifier &document, const Range &range = {})
|
||||||
{
|
{
|
||||||
setTextDocument(document);
|
setTextDocument(document);
|
||||||
@@ -2784,19 +2780,22 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
|
|||||||
explicit AstRequest(const AstParams ¶ms) : Request("textDocument/ast", params) {}
|
explicit AstRequest(const AstParams ¶ms) : Request("textDocument/ast", params) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
AstRequest request(AstParams(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath))));
|
AstRequest request(AstParams(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)),
|
||||||
|
range));
|
||||||
request.setResponseCallback([this, filePath, guardedTextDoc = QPointer(textDoc), astHandler,
|
request.setResponseCallback([this, filePath, guardedTextDoc = QPointer(textDoc), astHandler,
|
||||||
docRev = textDoc ? getRevision(textDoc) : -1,
|
fullAstRequested, docRev = textDoc ? getRevision(textDoc) : -1,
|
||||||
fileRev = getRevision(filePath), reqId = request.id()]
|
fileRev = getRevision(filePath), reqId = request.id()]
|
||||||
(AstRequest::Response response) {
|
(AstRequest::Response response) {
|
||||||
qCDebug(clangdLog) << "retrieved AST from clangd";
|
qCDebug(clangdLog) << "retrieved AST from clangd";
|
||||||
const auto result = response.result();
|
const auto result = response.result();
|
||||||
const AstNode ast = result ? *result : AstNode();
|
const AstNode ast = result ? *result : AstNode();
|
||||||
if (guardedTextDoc) {
|
if (fullAstRequested) {
|
||||||
if (docRev == getRevision(guardedTextDoc))
|
if (guardedTextDoc) {
|
||||||
astCache.insert(guardedTextDoc, ast);
|
if (docRev == getRevision(guardedTextDoc))
|
||||||
} else if (fileRev == getRevision(filePath) && !q->documentForFilePath(filePath)) {
|
astCache.insert(guardedTextDoc, ast);
|
||||||
externalAstCache.insert(filePath, ast);
|
} else if (fileRev == getRevision(filePath) && !q->documentForFilePath(filePath)) {
|
||||||
|
externalAstCache.insert(filePath, ast);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
astHandler(ast, reqId);
|
astHandler(ast, reqId);
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user