forked from qt-creator/qt-creator
ClangCodeModel: Move decl/def switch functionality to dedicated class
Change-Id: Id583ac58933e35e979083311907331b627d3c067 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -22,6 +22,7 @@ add_qtc_plugin(ClangCodeModel
|
||||
clangdquickfixfactory.cpp clangdquickfixfactory.h
|
||||
clangdqpropertyhighlighter.cpp clangdqpropertyhighlighter.h
|
||||
clangdsemantichighlighting.cpp clangdsemantichighlighting.h
|
||||
clangdswitchdecldef.cpp clangdswitchdecldef.h
|
||||
clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h
|
||||
clangfixitoperation.cpp clangfixitoperation.h
|
||||
clangdlocatorfilters.cpp clangdlocatorfilters.h
|
||||
|
@@ -46,6 +46,8 @@ QtcPlugin {
|
||||
"clangdquickfixfactory.h",
|
||||
"clangdsemantichighlighting.cpp",
|
||||
"clangdsemantichighlighting.h",
|
||||
"clangdswitchdecldef.cpp",
|
||||
"clangdswitchdecldef.h",
|
||||
"clangeditordocumentprocessor.cpp",
|
||||
"clangeditordocumentprocessor.h",
|
||||
"clangfixitoperation.cpp",
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "clangdast.h"
|
||||
#include "clangdfollowsymbol.h"
|
||||
#include "clangdlocatorfilters.h"
|
||||
#include "clangdswitchdecldef.h"
|
||||
#include "clangpreprocessorassistproposalitem.h"
|
||||
#include "clangtextmark.h"
|
||||
#include "clangutils.h"
|
||||
@@ -59,7 +60,6 @@
|
||||
#include <cppeditor/semantichighlighter.h>
|
||||
#include <cppeditor/cppsemanticinfo.h>
|
||||
#include <languageclient/diagnosticmanager.h>
|
||||
#include <languageclient/documentsymbolcache.h>
|
||||
#include <languageclient/languageclientcompletionassist.h>
|
||||
#include <languageclient/languageclientfunctionhint.h>
|
||||
#include <languageclient/languageclienthoverhandler.h>
|
||||
@@ -117,8 +117,8 @@ namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
Q_LOGGING_CATEGORY(clangdLog, "qtc.clangcodemodel.clangd", QtWarningMsg);
|
||||
Q_LOGGING_CATEGORY(clangdLogAst, "qtc.clangcodemodel.clangd.ast", QtWarningMsg);
|
||||
static Q_LOGGING_CATEGORY(clangdLogServer, "qtc.clangcodemodel.clangd.server", QtWarningMsg);
|
||||
static Q_LOGGING_CATEGORY(clangdLogAst, "qtc.clangcodemodel.clangd.ast", QtWarningMsg);
|
||||
static Q_LOGGING_CATEGORY(clangdLogCompletion, "qtc.clangcodemodel.clangd.completion",
|
||||
QtWarningMsg);
|
||||
static QString indexingToken() { return "backgroundIndexProgress"; }
|
||||
@@ -310,59 +310,6 @@ public:
|
||||
bool categorize = CppEditor::codeModelSettings()->categorizeFindReferences();
|
||||
};
|
||||
|
||||
class SwitchDeclDefData {
|
||||
public:
|
||||
SwitchDeclDefData(quint64 id, TextDocument *doc, const QTextCursor &cursor,
|
||||
CppEditor::CppEditorWidget *editorWidget,
|
||||
const Utils::LinkHandler &callback)
|
||||
: id(id), document(doc), uri(DocumentUri::fromFilePath(doc->filePath())),
|
||||
cursor(cursor), editorWidget(editorWidget), callback(callback) {}
|
||||
|
||||
Utils::optional<ClangdAstNode> getFunctionNode() const
|
||||
{
|
||||
QTC_ASSERT(ast, return {});
|
||||
|
||||
const ClangdAstPath path = getAstPath(*ast, Range(cursor));
|
||||
for (auto it = path.rbegin(); it != path.rend(); ++it) {
|
||||
if (it->role() == "declaration"
|
||||
&& (it->kind() == "CXXMethod" || it->kind() == "CXXConversion"
|
||||
|| it->kind() == "CXXConstructor" || it->kind() == "CXXDestructor"
|
||||
|| it->kind() == "Function")) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QTextCursor cursorForFunctionName(const ClangdAstNode &functionNode) const
|
||||
{
|
||||
QTC_ASSERT(docSymbols, return {});
|
||||
|
||||
const auto symbolList = Utils::get_if<QList<DocumentSymbol>>(&*docSymbols);
|
||||
if (!symbolList)
|
||||
return {};
|
||||
const Range &astRange = functionNode.range();
|
||||
QList symbolsToCheck = *symbolList;
|
||||
while (!symbolsToCheck.isEmpty()) {
|
||||
const DocumentSymbol symbol = symbolsToCheck.takeFirst();
|
||||
if (symbol.range() == astRange)
|
||||
return symbol.selectionRange().start().toTextCursor(document->document());
|
||||
if (symbol.range().contains(astRange))
|
||||
symbolsToCheck << symbol.children().value_or(QList<DocumentSymbol>());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const quint64 id;
|
||||
const QPointer<TextDocument> document;
|
||||
const DocumentUri uri;
|
||||
const QTextCursor cursor;
|
||||
const QPointer<CppEditor::CppEditorWidget> editorWidget;
|
||||
Utils::LinkHandler callback;
|
||||
Utils::optional<DocumentSymbolsResult> docSymbols;
|
||||
Utils::optional<ClangdAstNode> ast;
|
||||
};
|
||||
|
||||
class LocalRefsData {
|
||||
public:
|
||||
LocalRefsData(quint64 id, TextDocument *doc, const QTextCursor &cursor,
|
||||
@@ -700,7 +647,7 @@ public:
|
||||
const CppEditor::ClangdSettings::Data settings;
|
||||
QHash<quint64, ReferencesData> runningFindUsages;
|
||||
ClangdFollowSymbol *followSymbol = nullptr;
|
||||
Utils::optional<SwitchDeclDefData> switchDeclDefData;
|
||||
ClangdSwitchDeclDef *switchDeclDef = nullptr;
|
||||
Utils::optional<LocalRefsData> localRefsData;
|
||||
Utils::optional<QVersionNumber> versionNumber;
|
||||
|
||||
@@ -1017,15 +964,6 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
||||
QTC_CHECK(d->runningFindUsages.isEmpty());
|
||||
});
|
||||
|
||||
connect(documentSymbolCache(), &DocumentSymbolCache::gotSymbols, this,
|
||||
[this](const DocumentUri &uri, const DocumentSymbolsResult &symbols) {
|
||||
if (!d->switchDeclDefData || d->switchDeclDefData->uri != uri)
|
||||
return;
|
||||
d->switchDeclDefData->docSymbols = symbols;
|
||||
if (d->switchDeclDefData->ast)
|
||||
d->handleDeclDefSwitchReplies();
|
||||
});
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
@@ -1684,26 +1622,13 @@ void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &curs
|
||||
|
||||
qCDebug(clangdLog) << "switch decl/dev requested" << document->filePath()
|
||||
<< cursor.blockNumber() << cursor.positionInBlock();
|
||||
d->switchDeclDefData.emplace(++d->nextJobId, document, cursor, editorWidget, callback);
|
||||
|
||||
// Retrieve AST and document symbols.
|
||||
const auto astHandler = [this, id = d->switchDeclDefData->id](const ClangdAstNode &ast,
|
||||
const MessageId &) {
|
||||
qCDebug(clangdLog) << "received ast for decl/def switch";
|
||||
if (!d->switchDeclDefData || d->switchDeclDefData->id != id
|
||||
|| !d->switchDeclDefData->document)
|
||||
return;
|
||||
if (!ast.isValid()) {
|
||||
d->switchDeclDefData.reset();
|
||||
return;
|
||||
}
|
||||
d->switchDeclDefData->ast = ast;
|
||||
if (d->switchDeclDefData->docSymbols)
|
||||
d->handleDeclDefSwitchReplies();
|
||||
|
||||
};
|
||||
d->getAndHandleAst(document, astHandler, AstCallbackMode::SyncIfPossible);
|
||||
documentSymbolCache()->requestSymbols(d->switchDeclDefData->uri, Schedule::Now);
|
||||
if (d->switchDeclDef)
|
||||
delete d->switchDeclDef;
|
||||
d->switchDeclDef = new ClangdSwitchDeclDef(this, document, cursor, editorWidget, callback);
|
||||
connect(d->switchDeclDef, &ClangdSwitchDeclDef::done, this, [this] {
|
||||
delete d->switchDeclDef;
|
||||
d->switchDeclDef = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void ClangdClient::switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit)
|
||||
@@ -1981,35 +1906,6 @@ void ClangdClient::setVirtualRanges(const Utils::FilePath &filePath, const QList
|
||||
d->highlightingData[doc].virtualRanges = {ranges, revision};
|
||||
}
|
||||
|
||||
void ClangdClient::Private::handleDeclDefSwitchReplies()
|
||||
{
|
||||
if (!switchDeclDefData->document) {
|
||||
switchDeclDefData.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the function declaration or definition associated with the cursor.
|
||||
// For instance, the cursor could be somwehere inside a function body or
|
||||
// on a function return type, or ...
|
||||
if (clangdLogAst().isDebugEnabled())
|
||||
switchDeclDefData->ast->print(0);
|
||||
const Utils::optional<ClangdAstNode> functionNode = switchDeclDefData->getFunctionNode();
|
||||
if (!functionNode) {
|
||||
switchDeclDefData.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unfortunately, the AST does not contain the location of the actual function name symbol,
|
||||
// so we have to look for it in the document symbols.
|
||||
const QTextCursor funcNameCursor = switchDeclDefData->cursorForFunctionName(*functionNode);
|
||||
if (!funcNameCursor.isNull()) {
|
||||
q->followSymbol(switchDeclDefData->document.data(), funcNameCursor,
|
||||
switchDeclDefData->editorWidget, std::move(switchDeclDefData->callback),
|
||||
true, false);
|
||||
}
|
||||
switchDeclDefData.reset();
|
||||
}
|
||||
|
||||
Utils::optional<QString> ClangdClient::Private::getContainingFunctionName(
|
||||
const ClangdAstPath &astPath, const Range& range)
|
||||
{
|
||||
|
@@ -51,6 +51,7 @@ namespace Internal {
|
||||
class ClangdAstNode;
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(clangdLog);
|
||||
Q_DECLARE_LOGGING_CATEGORY(clangdLogAst);
|
||||
|
||||
void setupClangdConfigFile();
|
||||
|
||||
|
188
src/plugins/clangcodemodel/clangdswitchdecldef.cpp
Normal file
188
src/plugins/clangcodemodel/clangdswitchdecldef.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangdswitchdecldef.h"
|
||||
|
||||
#include "clangdast.h"
|
||||
#include "clangdclient.h"
|
||||
|
||||
#include <cppeditor/cppeditorwidget.h>
|
||||
#include <languageclient/documentsymbolcache.h>
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <utils/optional.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTextCursor>
|
||||
|
||||
using namespace CppEditor;
|
||||
using namespace LanguageClient;
|
||||
using namespace LanguageServerProtocol;
|
||||
using namespace TextEditor;
|
||||
using namespace Utils;
|
||||
|
||||
namespace ClangCodeModel::Internal {
|
||||
|
||||
class ClangdSwitchDeclDef::Private
|
||||
{
|
||||
public:
|
||||
Private(ClangdSwitchDeclDef * q, ClangdClient *client, TextDocument *doc,
|
||||
const QTextCursor &cursor, CppEditorWidget *editorWidget, const LinkHandler &callback)
|
||||
: q(q), client(client), document(doc), uri(DocumentUri::fromFilePath(doc->filePath())),
|
||||
cursor(cursor), editorWidget(editorWidget), callback(callback)
|
||||
{}
|
||||
|
||||
optional<ClangdAstNode> getFunctionNode() const;
|
||||
QTextCursor cursorForFunctionName(const ClangdAstNode &functionNode) const;
|
||||
void handleDeclDefSwitchReplies();
|
||||
|
||||
ClangdSwitchDeclDef * const q;
|
||||
ClangdClient * const client;
|
||||
const QPointer<TextDocument> document;
|
||||
const DocumentUri uri;
|
||||
const QTextCursor cursor;
|
||||
const QPointer<CppEditorWidget> editorWidget;
|
||||
const LinkHandler callback;
|
||||
optional<ClangdAstNode> ast;
|
||||
optional<DocumentSymbolsResult> docSymbols;
|
||||
};
|
||||
|
||||
ClangdSwitchDeclDef::ClangdSwitchDeclDef(ClangdClient *client, TextDocument *doc,
|
||||
const QTextCursor &cursor, CppEditorWidget *editorWidget, const LinkHandler &callback)
|
||||
: QObject(client), d(new Private(this, client, doc, cursor, editorWidget, callback))
|
||||
{
|
||||
// Abort if the user does something else with the document in the meantime.
|
||||
connect(doc, &TextDocument::contentsChanged, this, &ClangdSwitchDeclDef::done,
|
||||
Qt::QueuedConnection);
|
||||
if (editorWidget) {
|
||||
connect(editorWidget, &CppEditorWidget::cursorPositionChanged,
|
||||
this, &ClangdSwitchDeclDef::done, Qt::QueuedConnection);
|
||||
}
|
||||
connect(qApp, &QApplication::focusChanged,
|
||||
this, &ClangdSwitchDeclDef::done, Qt::QueuedConnection);
|
||||
|
||||
connect(client->documentSymbolCache(), &DocumentSymbolCache::gotSymbols, this,
|
||||
[this](const DocumentUri &uri, const DocumentSymbolsResult &symbols) {
|
||||
if (uri != d->uri)
|
||||
return;
|
||||
d->docSymbols = symbols;
|
||||
if (d->ast)
|
||||
d->handleDeclDefSwitchReplies();
|
||||
});
|
||||
|
||||
|
||||
// Retrieve AST and document symbols.
|
||||
const auto astHandler = [this, self = QPointer(this)]
|
||||
(const ClangdAstNode &ast, const MessageId &) {
|
||||
qCDebug(clangdLog) << "received ast for decl/def switch";
|
||||
if (!self)
|
||||
return;
|
||||
if (!d->document) {
|
||||
emit done();
|
||||
return;
|
||||
}
|
||||
if (!ast.isValid()) {
|
||||
emit done();
|
||||
return;
|
||||
}
|
||||
d->ast = ast;
|
||||
if (d->docSymbols)
|
||||
d->handleDeclDefSwitchReplies();
|
||||
|
||||
};
|
||||
client->getAndHandleAst(doc, astHandler, ClangdClient::AstCallbackMode::SyncIfPossible, {});
|
||||
client->documentSymbolCache()->requestSymbols(d->uri, Schedule::Now);
|
||||
}
|
||||
|
||||
ClangdSwitchDeclDef::~ClangdSwitchDeclDef()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
optional<ClangdAstNode> ClangdSwitchDeclDef::Private::getFunctionNode() const
|
||||
{
|
||||
QTC_ASSERT(ast, return {});
|
||||
|
||||
const ClangdAstPath path = getAstPath(*ast, Range(cursor));
|
||||
for (auto it = path.rbegin(); it != path.rend(); ++it) {
|
||||
if (it->role() == "declaration"
|
||||
&& (it->kind() == "CXXMethod" || it->kind() == "CXXConversion"
|
||||
|| it->kind() == "CXXConstructor" || it->kind() == "CXXDestructor"
|
||||
|| it->kind() == "Function")) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QTextCursor ClangdSwitchDeclDef::Private::cursorForFunctionName(const ClangdAstNode &functionNode) const
|
||||
{
|
||||
QTC_ASSERT(docSymbols, return {});
|
||||
|
||||
const auto symbolList = Utils::get_if<QList<DocumentSymbol>>(&*docSymbols);
|
||||
if (!symbolList)
|
||||
return {};
|
||||
const Range &astRange = functionNode.range();
|
||||
QList symbolsToCheck = *symbolList;
|
||||
while (!symbolsToCheck.isEmpty()) {
|
||||
const DocumentSymbol symbol = symbolsToCheck.takeFirst();
|
||||
if (symbol.range() == astRange)
|
||||
return symbol.selectionRange().start().toTextCursor(document->document());
|
||||
if (symbol.range().contains(astRange))
|
||||
symbolsToCheck << symbol.children().value_or(QList<DocumentSymbol>());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ClangdSwitchDeclDef::Private::handleDeclDefSwitchReplies()
|
||||
{
|
||||
if (!document) {
|
||||
emit q->done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the function declaration or definition associated with the cursor.
|
||||
// For instance, the cursor could be somwehere inside a function body or
|
||||
// on a function return type, or ...
|
||||
if (clangdLogAst().isDebugEnabled())
|
||||
ast->print(0);
|
||||
const Utils::optional<ClangdAstNode> functionNode = getFunctionNode();
|
||||
if (!functionNode) {
|
||||
emit q->done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unfortunately, the AST does not contain the location of the actual function name symbol,
|
||||
// so we have to look for it in the document symbols.
|
||||
const QTextCursor funcNameCursor = cursorForFunctionName(*functionNode);
|
||||
if (!funcNameCursor.isNull()) {
|
||||
client->followSymbol(document.data(), funcNameCursor, editorWidget, callback,
|
||||
true, false);
|
||||
}
|
||||
emit q->done();
|
||||
}
|
||||
|
||||
} // namespace ClangCodeModel::Internal
|
59
src/plugins/clangcodemodel/clangdswitchdecldef.h
Normal file
59
src/plugins/clangcodemodel/clangdswitchdecldef.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utils/link.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace CppEditor { class CppEditorWidget; }
|
||||
namespace TextEditor { class TextDocument; }
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextCursor;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ClangCodeModel::Internal {
|
||||
class ClangdClient;
|
||||
|
||||
class ClangdSwitchDeclDef : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClangdSwitchDeclDef(ClangdClient *client, TextEditor::TextDocument *doc,
|
||||
const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget,
|
||||
const Utils::LinkHandler &callback);
|
||||
~ClangdSwitchDeclDef();
|
||||
|
||||
signals:
|
||||
void done();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
} // namespace ClangCodeModel::Internal
|
Reference in New Issue
Block a user