ClangCodeModel: Allow more than one in-flight "follow symbol"

The original code was written with only the interactive case in mind, but
nowadays we also start "follow symbol" operations internally as part of
e.g. quickfixes.

Change-Id: I95928297fab16f9b0469bfd66ad687447b902fd9
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2024-05-24 13:52:27 +02:00
parent b0354eaf16
commit 0e301004b8
10 changed files with 71 additions and 19 deletions

View File

@@ -349,7 +349,7 @@ public:
ClangdClient * const q; ClangdClient * const q;
const CppEditor::ClangdSettings::Data settings; const CppEditor::ClangdSettings::Data settings;
ClangdFollowSymbol *followSymbol = nullptr; QList<ClangdFollowSymbol *> followSymbolOps;
ClangdSwitchDeclDef *switchDeclDef = nullptr; ClangdSwitchDeclDef *switchDeclDef = nullptr;
ClangdFindLocalReferences *findLocalRefs = nullptr; ClangdFindLocalReferences *findLocalRefs = nullptr;
std::optional<QVersionNumber> versionNumber; std::optional<QVersionNumber> versionNumber;
@@ -501,8 +501,8 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
ClangdClient::~ClangdClient() ClangdClient::~ClangdClient()
{ {
if (d->followSymbol) for (ClangdFollowSymbol * const followSymbol : std::as_const(d->followSymbolOps))
d->followSymbol->clear(); followSymbol->clear();
delete d; delete d;
} }
@@ -990,7 +990,7 @@ MessageId ClangdClient::requestSymbolInfo(const Utils::FilePath &filePath, const
#ifdef WITH_TESTS #ifdef WITH_TESTS
ClangdFollowSymbol *ClangdClient::currentFollowSymbolOperation() ClangdFollowSymbol *ClangdClient::currentFollowSymbolOperation()
{ {
return d->followSymbol; return d->followSymbolOps.isEmpty() ? nullptr : d->followSymbolOps.first();
} }
#endif #endif
@@ -1005,8 +1005,20 @@ void ClangdClient::followSymbol(TextDocument *document,
{ {
QTC_ASSERT(documentOpen(document), openDocument(document)); QTC_ASSERT(documentOpen(document), openDocument(document));
if (d->followSymbol) const ClangdFollowSymbol::Origin origin
d->followSymbol->cancel(); = CppEditor::CppCodeModelSettings::isInteractiveFollowSymbol()
? ClangdFollowSymbol::Origin::User
: ClangdFollowSymbol::Origin::Code;
if (origin == ClangdFollowSymbol::Origin::User) {
for (auto it = d->followSymbolOps.begin(); it != d->followSymbolOps.end(); ) {
if ((*it)->isInteractive()) {
(*it)->cancel();
it = d->followSymbolOps.erase(it);
} else {
++it;
}
}
}
const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document); const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document);
if (followTo == FollowTo::SymbolDef && !resolveTarget) { if (followTo == FollowTo::SymbolDef && !resolveTarget) {
@@ -1020,14 +1032,13 @@ 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();
auto clangdFollowSymbol = new ClangdFollowSymbol(this, adjustedCursor, editorWidget, document, auto clangdFollowSymbol = new ClangdFollowSymbol(this, origin, adjustedCursor, editorWidget,
callback, followTo, openInSplit); document, callback, followTo, openInSplit);
connect(clangdFollowSymbol, &ClangdFollowSymbol::done, this, [this, clangdFollowSymbol] { connect(clangdFollowSymbol, &ClangdFollowSymbol::done, this, [this, clangdFollowSymbol] {
clangdFollowSymbol->deleteLater(); clangdFollowSymbol->deleteLater();
if (clangdFollowSymbol == d->followSymbol) d->followSymbolOps.removeOne(clangdFollowSymbol);
d->followSymbol = nullptr;
}); });
d->followSymbol = clangdFollowSymbol; d->followSymbolOps << clangdFollowSymbol;
} }
void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor, void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor,

View File

@@ -73,10 +73,10 @@ private:
class ClangdFollowSymbol::Private class ClangdFollowSymbol::Private
{ {
public: public:
Private(ClangdFollowSymbol *q, ClangdClient *client, const QTextCursor &cursor, Private(ClangdFollowSymbol *q, ClangdClient *client, Origin origin, const QTextCursor &cursor,
CppEditorWidget *editorWidget, const FilePath &filePath, const LinkHandler &callback, CppEditorWidget *editorWidget, const FilePath &filePath, const LinkHandler &callback,
bool openInSplit) bool openInSplit)
: q(q), client(client), cursor(cursor), editorWidget(editorWidget), : q(q), client(client), origin(origin), cursor(cursor), editorWidget(editorWidget),
uri(client->hostPathToServerUri(filePath)), callback(callback), uri(client->hostPathToServerUri(filePath)), callback(callback),
virtualFuncAssistProvider(q), virtualFuncAssistProvider(q),
docRevision(editorWidget ? editorWidget->textDocument()->document()->revision() : -1), docRevision(editorWidget ? editorWidget->textDocument()->document()->revision() : -1),
@@ -94,6 +94,7 @@ public:
ClangdFollowSymbol * const q; ClangdFollowSymbol * const q;
ClangdClient * const client; ClangdClient * const client;
const Origin origin;
const QTextCursor cursor; const QTextCursor cursor;
const QPointer<CppEditor::CppEditorWidget> editorWidget; const QPointer<CppEditor::CppEditorWidget> editorWidget;
const DocumentUri uri; const DocumentUri uri;
@@ -117,11 +118,11 @@ public:
bool done = false; bool done = false;
}; };
ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor, ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, Origin origin,
CppEditorWidget *editorWidget, TextDocument *document, const LinkHandler &callback, const QTextCursor &cursor, CppEditorWidget *editorWidget, TextDocument *document,
FollowTo followTo, bool openInSplit) const LinkHandler &callback, FollowTo followTo, bool openInSplit)
: QObject(client), : QObject(client),
d(new Private(this, client, cursor, editorWidget, document->filePath(), callback, d(new Private(this, client, origin, cursor, editorWidget, document->filePath(), callback,
openInSplit)) openInSplit))
{ {
// Abort if the user does something else with the document in the meantime. // Abort if the user does something else with the document in the meantime.
@@ -193,6 +194,11 @@ void ClangdFollowSymbol::clear()
d->pendingGotoDefRequests.clear(); d->pendingGotoDefRequests.clear();
} }
bool ClangdFollowSymbol::isInteractive() const
{
return d->origin == Origin::User;
}
void ClangdFollowSymbol::emitDone(const Link &link) void ClangdFollowSymbol::emitDone(const Link &link)
{ {
if (d->done) if (d->done)

View File

@@ -23,7 +23,9 @@ class ClangdFollowSymbol : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
ClangdFollowSymbol(ClangdClient *client, const QTextCursor &cursor, enum class Origin { User, Code };
ClangdFollowSymbol(ClangdClient *client, Origin origin, const QTextCursor &cursor,
CppEditor::CppEditorWidget *editorWidget, CppEditor::CppEditorWidget *editorWidget,
TextEditor::TextDocument *document, const Utils::LinkHandler &callback, TextEditor::TextDocument *document, const Utils::LinkHandler &callback,
FollowTo followTo, bool openInSplit); FollowTo followTo, bool openInSplit);
@@ -31,6 +33,8 @@ public:
void cancel(); void cancel();
void clear(); void clear();
bool isInteractive() const;
signals: signals:
void done(); void done();

View File

@@ -83,6 +83,7 @@ bool operator==(const CppEditor::CppCodeModelSettings &s1,
&& s1.useBuiltinPreprocessor == s2.useBuiltinPreprocessor && s1.useBuiltinPreprocessor == s2.useBuiltinPreprocessor
&& s1.indexerFileSizeLimitInMb == s2.indexerFileSizeLimitInMb && s1.indexerFileSizeLimitInMb == s2.indexerFileSizeLimitInMb
&& s1.m_categorizeFindReferences == s2.m_categorizeFindReferences && s1.m_categorizeFindReferences == s2.m_categorizeFindReferences
&& s1.interactiveFollowSymbol == s2.interactiveFollowSymbol
&& s1.ignoreFiles == s2.ignoreFiles && s1.ignorePattern == s2.ignorePattern; && s1.ignoreFiles == s2.ignoreFiles && s1.ignorePattern == s2.ignorePattern;
} }
@@ -204,6 +205,16 @@ void CppCodeModelSettings::setCategorizeFindReferences(bool categorize)
globalInstance().m_categorizeFindReferences = categorize; globalInstance().m_categorizeFindReferences = categorize;
} }
bool CppCodeModelSettings::isInteractiveFollowSymbol()
{
return globalInstance().interactiveFollowSymbol;
}
void CppCodeModelSettings::setInteractiveFollowSymbol(bool interactive)
{
globalInstance().interactiveFollowSymbol = interactive;
}
CppCodeModelProjectSettings::CppCodeModelProjectSettings(ProjectExplorer::Project *project) CppCodeModelProjectSettings::CppCodeModelProjectSettings(ProjectExplorer::Project *project)
: m_project(project) : m_project(project)
{ {

View File

@@ -55,6 +55,9 @@ public:
static bool categorizeFindReferences(); static bool categorizeFindReferences();
static void setCategorizeFindReferences(bool categorize); static void setCategorizeFindReferences(bool categorize);
static bool isInteractiveFollowSymbol();
static void setInteractiveFollowSymbol(bool interactive);
QString ignorePattern; QString ignorePattern;
PCHUsage pchUsage = PchUse_BuildSystem; PCHUsage pchUsage = PchUse_BuildSystem;
int indexerFileSizeLimitInMb = 5; int indexerFileSizeLimitInMb = 5;
@@ -63,7 +66,10 @@ public:
bool useBuiltinPreprocessor = true; bool useBuiltinPreprocessor = true;
bool ignoreFiles = false; bool ignoreFiles = false;
bool enableIndexing = true; bool enableIndexing = true;
bool m_categorizeFindReferences = false; // Ephemeral!
// Ephemeral!
bool m_categorizeFindReferences = false;
bool interactiveFollowSymbol = true;
private: private:
CppCodeModelSettings(Utils::QtcSettings *s) { fromSettings(s); } CppCodeModelSettings(Utils::QtcSettings *s) { fromSettings(s); }
@@ -76,6 +82,14 @@ private:
namespace Internal { namespace Internal {
void setupCppCodeModelSettingsPage(); void setupCppCodeModelSettingsPage();
void setupCppCodeModelProjectSettingsPanel(); void setupCppCodeModelProjectSettingsPanel();
class NonInteractiveFollowSymbolMarker
{
public:
NonInteractiveFollowSymbolMarker() { CppCodeModelSettings::setInteractiveFollowSymbol(false); }
~NonInteractiveFollowSymbolMarker() { CppCodeModelSettings::setInteractiveFollowSymbol(true); }
};
} // namespace Internal } // namespace Internal
} // namespace CppEditor } // namespace CppEditor

View File

@@ -623,6 +623,7 @@ void CppEditorWidget::renameUsages(const QString &replacement, QTextCursor curso
const CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this, textDocument()}; const CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this, textDocument()};
CppModelManager::globalRename(cursorInEditor, replacement); CppModelManager::globalRename(cursorInEditor, replacement);
}; };
NonInteractiveFollowSymbolMarker niMarker;
CppModelManager::followSymbol(CursorInEditor{cursor, CppModelManager::followSymbol(CursorInEditor{cursor,
textDocument()->filePath(), textDocument()->filePath(),
this, this,

View File

@@ -370,6 +370,7 @@ private:
if (!link.hasValidTarget()) if (!link.hasValidTarget())
collectOperations(interface, result); collectOperations(interface, result);
}; };
NonInteractiveFollowSymbolMarker niMarker;
CppModelManager::followSymbol(cursorInEditor, followSymbolFallback, false, false, CppModelManager::followSymbol(cursorInEditor, followSymbolFallback, false, false,
FollowSymbolMode::Exact, FollowSymbolMode::Exact,
CppModelManager::Backend::Builtin); CppModelManager::Backend::Builtin);

View File

@@ -294,6 +294,7 @@ private:
// Force queued execution, as the built-in editor can run the callback synchronously. // Force queued execution, as the built-in editor can run the callback synchronously.
const auto followSymbol = [cursorInEditor, callback] { const auto followSymbol = [cursorInEditor, callback] {
NonInteractiveFollowSymbolMarker niMarker;
CppModelManager::followSymbol( CppModelManager::followSymbol(
cursorInEditor, callback, true, false, FollowSymbolMode::Exact); cursorInEditor, callback, true, false, FollowSymbolMode::Exact);
}; };

View File

@@ -246,6 +246,8 @@ private:
(const Link &link) { (const Link &link) {
moveComments(link, symbolLoc, comments); moveComments(link, symbolLoc, comments);
}; };
NonInteractiveFollowSymbolMarker niMarker;
CppCodeModelSettings::setInteractiveFollowSymbol(false);
CppModelManager::followSymbol(cursorInEditor, callback, true, false, CppModelManager::followSymbol(cursorInEditor, callback, true, false,
FollowSymbolMode::Exact); FollowSymbolMode::Exact);
} }

View File

@@ -120,6 +120,7 @@ private:
// Force queued execution, as the built-in editor can run the callback synchronously. // Force queued execution, as the built-in editor can run the callback synchronously.
const auto followSymbol = [cursorInEditor, callback] { const auto followSymbol = [cursorInEditor, callback] {
NonInteractiveFollowSymbolMarker niMarker;
CppModelManager::followSymbol( CppModelManager::followSymbol(
cursorInEditor, callback, true, false, FollowSymbolMode::Exact); cursorInEditor, callback, true, false, FollowSymbolMode::Exact);
}; };