forked from qt-creator/qt-creator
ClangCodeModel: Make clangd refactoring actions available
Introduce an assist processor that merges our built-in quickfixes with refactoring actions from clangd ("tweaks"). For now, we make it clear which ones are coming from clangd, and we do not filter duplicate functionality. In the future, we might want to disable redundant built-in actions if clangd is enabled for the respective file. Change-Id: I04842132798c8635dfddf8cfc98cc7a6313fac09 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -1257,6 +1257,54 @@ private:
|
|||||||
ClangdClient * const m_client;
|
ClangdClient * const m_client;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ClangdQuickFixProcessor : public LanguageClientQuickFixAssistProcessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClangdQuickFixProcessor(LanguageClient::Client *client)
|
||||||
|
: LanguageClientQuickFixAssistProcessor(client)
|
||||||
|
{
|
||||||
|
// Fixes are already provided inline with the diagnostics.
|
||||||
|
setOnlyKinds({CodeActionKinds::Refactor});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IAssistProposal *perform(const AssistInterface *interface) override
|
||||||
|
{
|
||||||
|
m_interface = interface;
|
||||||
|
|
||||||
|
// Step 1: Collect clangd code actions asynchronously
|
||||||
|
LanguageClientQuickFixAssistProcessor::perform(interface);
|
||||||
|
|
||||||
|
// Step 2: Collect built-in quickfixes synchronously
|
||||||
|
m_builtinOps = CppEditor::quickFixOperations(interface);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleProposalReady(const QuickFixOperations &ops) override
|
||||||
|
{
|
||||||
|
// Step 3: Merge the results upon callback from clangd.
|
||||||
|
for (const auto &op : ops)
|
||||||
|
op->setDescription("clangd: " + op->description());
|
||||||
|
setAsyncProposalAvailable(GenericProposal::createProposal(m_interface, ops + m_builtinOps));
|
||||||
|
}
|
||||||
|
|
||||||
|
QuickFixOperations m_builtinOps;
|
||||||
|
const AssistInterface *m_interface = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClangdQuickFixProvider : public LanguageClientQuickFixProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClangdQuickFixProvider(ClangdClient *client) : LanguageClientQuickFixProvider(client) {};
|
||||||
|
|
||||||
|
private:
|
||||||
|
IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override
|
||||||
|
{
|
||||||
|
return new ClangdQuickFixProcessor(client());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
||||||
: Client(clientInterface(project, jsonDbDir)), d(new Private(this, project))
|
: Client(clientInterface(project, jsonDbDir)), d(new Private(this, project))
|
||||||
{
|
{
|
||||||
@@ -1268,6 +1316,7 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
|||||||
setActivateDocumentAutomatically(true);
|
setActivateDocumentAutomatically(true);
|
||||||
setLogTarget(LogTarget::Console);
|
setLogTarget(LogTarget::Console);
|
||||||
setCompletionAssistProvider(new ClangdCompletionAssistProvider(this));
|
setCompletionAssistProvider(new ClangdCompletionAssistProvider(this));
|
||||||
|
setQuickFixAssistProvider(new ClangdQuickFixProvider(this));
|
||||||
if (!project) {
|
if (!project) {
|
||||||
QJsonObject initOptions;
|
QJsonObject initOptions;
|
||||||
const QStringList clangOptions = createClangOptions(
|
const QStringList clangOptions = createClangOptions(
|
||||||
|
@@ -162,6 +162,8 @@ CompletionAssistProvider *CppEditorDocument::functionHintAssistProvider() const
|
|||||||
|
|
||||||
TextEditor::IAssistProvider *CppEditorDocument::quickFixAssistProvider() const
|
TextEditor::IAssistProvider *CppEditorDocument::quickFixAssistProvider() const
|
||||||
{
|
{
|
||||||
|
if (const auto baseProvider = TextDocument::quickFixAssistProvider())
|
||||||
|
return baseProvider;
|
||||||
return CppEditorPlugin::instance()->quickFixProvider();
|
return CppEditorPlugin::instance()->quickFixProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -53,14 +53,8 @@ class CppQuickFixAssistProcessor : public IAssistProcessor
|
|||||||
{
|
{
|
||||||
IAssistProposal *perform(const AssistInterface *interface) override
|
IAssistProposal *perform(const AssistInterface *interface) override
|
||||||
{
|
{
|
||||||
QSharedPointer<const AssistInterface> assistInterface(interface);
|
QSharedPointer<const AssistInterface> dummy(interface); // FIXME: Surely this cannot be our way of doing memory management???
|
||||||
auto cppInterface = assistInterface.staticCast<const CppQuickFixInterface>();
|
return GenericProposal::createProposal(interface, quickFixOperations(interface));
|
||||||
|
|
||||||
QuickFixOperations quickFixes;
|
|
||||||
for (CppQuickFixFactory *factory : CppQuickFixFactory::cppQuickFixFactories())
|
|
||||||
factory->match(*cppInterface, quickFixes);
|
|
||||||
|
|
||||||
return GenericProposal::createProposal(interface, quickFixes);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,5 +131,15 @@ bool CppQuickFixInterface::isCursorOn(const AST *ast) const
|
|||||||
return currentFile()->isCursorOn(ast);
|
return currentFile()->isCursorOn(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface)
|
||||||
|
{
|
||||||
|
const auto cppInterface = dynamic_cast<const CppQuickFixInterface *>(interface);
|
||||||
|
QTC_ASSERT(cppInterface, return {});
|
||||||
|
QuickFixOperations quickFixes;
|
||||||
|
for (CppQuickFixFactory *factory : CppQuickFixFactory::cppQuickFixFactories())
|
||||||
|
factory->match(*cppInterface, quickFixes);
|
||||||
|
return quickFixes;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace CppEditor
|
} // namespace CppEditor
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <texteditor/codeassist/assistinterface.h>
|
#include <texteditor/codeassist/assistinterface.h>
|
||||||
#include <texteditor/codeassist/iassistprovider.h>
|
#include <texteditor/codeassist/iassistprovider.h>
|
||||||
|
#include <texteditor/quickfix.h>
|
||||||
|
|
||||||
#include <cplusplus/LookupContext.h>
|
#include <cplusplus/LookupContext.h>
|
||||||
|
|
||||||
@@ -73,5 +74,7 @@ public:
|
|||||||
TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override;
|
TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TextEditor::QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface);
|
||||||
|
|
||||||
} // Internal
|
} // Internal
|
||||||
} // CppEditor
|
} // CppEditor
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include "cppeditorplugin.h"
|
#include "cppeditorplugin.h"
|
||||||
#include "cpphighlighter.h"
|
#include "cpphighlighter.h"
|
||||||
#include "cppqtstyleindenter.h"
|
#include "cppqtstyleindenter.h"
|
||||||
|
#include "cppquickfixassistant.h"
|
||||||
#include "cpprefactoringchanges.h"
|
#include "cpprefactoringchanges.h"
|
||||||
#include "projectinfo.h"
|
#include "projectinfo.h"
|
||||||
|
|
||||||
@@ -339,6 +340,11 @@ bool isInCommentOrString(const TextEditor::AssistInterface *interface,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextEditor::QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface)
|
||||||
|
{
|
||||||
|
return Internal::quickFixOperations(interface);
|
||||||
|
}
|
||||||
|
|
||||||
CppCodeModelSettings *codeModelSettings()
|
CppCodeModelSettings *codeModelSettings()
|
||||||
{
|
{
|
||||||
return Internal::CppEditorPlugin::instance()->codeModelSettings();
|
return Internal::CppEditorPlugin::instance()->codeModelSettings();
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include "compileroptionsbuilder.h"
|
#include "compileroptionsbuilder.h"
|
||||||
#include "projectpart.h"
|
#include "projectpart.h"
|
||||||
|
|
||||||
|
#include <texteditor/quickfix.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
#include <cplusplus/ASTVisitor.h>
|
#include <cplusplus/ASTVisitor.h>
|
||||||
@@ -76,6 +77,8 @@ const CPlusPlus::Macro CPPEDITOR_EXPORT *findCanonicalMacro(const QTextCursor &c
|
|||||||
|
|
||||||
bool CPPEDITOR_EXPORT isInCommentOrString(const TextEditor::AssistInterface *interface,
|
bool CPPEDITOR_EXPORT isInCommentOrString(const TextEditor::AssistInterface *interface,
|
||||||
CPlusPlus::LanguageFeatures features);
|
CPlusPlus::LanguageFeatures features);
|
||||||
|
TextEditor::QuickFixOperations CPPEDITOR_EXPORT
|
||||||
|
quickFixOperations(const TextEditor::AssistInterface *interface);
|
||||||
|
|
||||||
enum class CacheUsage { ReadWrite, ReadOnly };
|
enum class CacheUsage { ReadWrite, ReadOnly };
|
||||||
|
|
||||||
|
@@ -1079,6 +1079,12 @@ void Client::setCompletionAssistProvider(LanguageClientCompletionAssistProvider
|
|||||||
m_clientProviders.completionAssistProvider = provider;
|
m_clientProviders.completionAssistProvider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider)
|
||||||
|
{
|
||||||
|
delete m_clientProviders.quickFixAssistProvider;
|
||||||
|
m_clientProviders.quickFixAssistProvider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
void Client::start()
|
void Client::start()
|
||||||
{
|
{
|
||||||
LanguageClientManager::addClient(this);
|
LanguageClientManager::addClient(this);
|
||||||
|
@@ -192,6 +192,7 @@ public:
|
|||||||
LanguageServerProtocol::SymbolStringifier symbolStringifier() const;
|
LanguageServerProtocol::SymbolStringifier symbolStringifier() const;
|
||||||
void setSnippetsGroup(const QString &group);
|
void setSnippetsGroup(const QString &group);
|
||||||
void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider);
|
void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider);
|
||||||
|
void setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider);
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
enum class LogTarget { Console, Ui };
|
enum class LogTarget { Console, Ui };
|
||||||
|
@@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
#include <texteditor/codeassist/assistinterface.h>
|
#include <texteditor/codeassist/assistinterface.h>
|
||||||
#include <texteditor/codeassist/genericproposal.h>
|
#include <texteditor/codeassist/genericproposal.h>
|
||||||
#include <texteditor/codeassist/iassistprocessor.h>
|
|
||||||
#include <texteditor/quickfix.h>
|
#include <texteditor/quickfix.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -74,22 +73,6 @@ private:
|
|||||||
QPointer<Client> m_client;
|
QPointer<Client> m_client;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LanguageClientQuickFixAssistProcessor : public IAssistProcessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit LanguageClientQuickFixAssistProcessor(Client *client) : m_client(client) {}
|
|
||||||
bool running() override { return m_currentRequest.has_value(); }
|
|
||||||
IAssistProposal *perform(const AssistInterface *interface) override;
|
|
||||||
void cancel() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void handleCodeActionResponse(const CodeActionRequest::Response &response);
|
|
||||||
|
|
||||||
QSharedPointer<const AssistInterface> m_assistInterface;
|
|
||||||
Client *m_client = nullptr; // not owned
|
|
||||||
Utils::optional<MessageId> m_currentRequest;
|
|
||||||
};
|
|
||||||
|
|
||||||
IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInterface *interface)
|
IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInterface *interface)
|
||||||
{
|
{
|
||||||
m_assistInterface = QSharedPointer<const AssistInterface>(interface);
|
m_assistInterface = QSharedPointer<const AssistInterface>(interface);
|
||||||
@@ -109,6 +92,8 @@ IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInte
|
|||||||
auto uri = DocumentUri::fromFilePath(interface->filePath());
|
auto uri = DocumentUri::fromFilePath(interface->filePath());
|
||||||
params.setTextDocument(TextDocumentIdentifier(uri));
|
params.setTextDocument(TextDocumentIdentifier(uri));
|
||||||
CodeActionParams::CodeActionContext context;
|
CodeActionParams::CodeActionContext context;
|
||||||
|
if (!m_onlyKinds.isEmpty())
|
||||||
|
context.setOnly(m_onlyKinds);
|
||||||
context.setDiagnostics(m_client->diagnosticsAt(uri, cursor));
|
context.setDiagnostics(m_client->diagnosticsAt(uri, cursor));
|
||||||
params.setContext(context);
|
params.setContext(context);
|
||||||
|
|
||||||
@@ -131,8 +116,12 @@ void LanguageClientQuickFixAssistProcessor::cancel()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse(
|
void LanguageClientQuickFixAssistProcessor::setOnlyKinds(const QList<CodeActionKind> &only)
|
||||||
const CodeActionRequest::Response &response)
|
{
|
||||||
|
m_onlyKinds = only;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse(const CodeActionRequest::Response &response)
|
||||||
{
|
{
|
||||||
m_currentRequest.reset();
|
m_currentRequest.reset();
|
||||||
if (const Utils::optional<CodeActionRequest::Response::Error> &error = response.error())
|
if (const Utils::optional<CodeActionRequest::Response::Error> &error = response.error())
|
||||||
@@ -150,6 +139,11 @@ void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_client->removeAssistProcessor(this);
|
m_client->removeAssistProcessor(this);
|
||||||
|
handleProposalReady(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageClientQuickFixAssistProcessor::handleProposalReady(const QuickFixOperations &ops)
|
||||||
|
{
|
||||||
setAsyncProposalAvailable(GenericProposal::createProposal(m_assistInterface.data(), ops));
|
setAsyncProposalAvailable(GenericProposal::createProposal(m_assistInterface.data(), ops));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,12 +28,15 @@
|
|||||||
#include "languageclient_global.h"
|
#include "languageclient_global.h"
|
||||||
|
|
||||||
#include <texteditor/codeassist/iassistprovider.h>
|
#include <texteditor/codeassist/iassistprovider.h>
|
||||||
|
#include <texteditor/codeassist/iassistprocessor.h>
|
||||||
#include <texteditor/quickfix.h>
|
#include <texteditor/quickfix.h>
|
||||||
|
|
||||||
#include <languageserverprotocol/languagefeatures.h>
|
#include <languageserverprotocol/languagefeatures.h>
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace TextEditor { class IAssistProposal; }
|
||||||
|
|
||||||
namespace LanguageClient {
|
namespace LanguageClient {
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
@@ -56,8 +59,34 @@ public:
|
|||||||
IAssistProvider::RunType runType() const override;
|
IAssistProvider::RunType runType() const override;
|
||||||
TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override;
|
TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Client *client() const { return m_client; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Client *m_client = nullptr; // not owned
|
Client *m_client = nullptr; // not owned
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LANGUAGECLIENT_EXPORT LanguageClientQuickFixAssistProcessor
|
||||||
|
: public TextEditor::IAssistProcessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LanguageClientQuickFixAssistProcessor(Client *client) : m_client(client) {}
|
||||||
|
bool running() override { return m_currentRequest.has_value(); }
|
||||||
|
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
|
||||||
|
void cancel() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setOnlyKinds(const QList<LanguageServerProtocol::CodeActionKind> &only);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleCodeActionResponse(const LanguageServerProtocol::CodeActionRequest::Response &response);
|
||||||
|
virtual void handleProposalReady(const TextEditor::QuickFixOperations &ops);
|
||||||
|
|
||||||
|
QSharedPointer<const TextEditor::AssistInterface> m_assistInterface;
|
||||||
|
Client *m_client = nullptr; // not owned
|
||||||
|
Utils::optional<LanguageServerProtocol::MessageId> m_currentRequest;
|
||||||
|
QList<LanguageServerProtocol::CodeActionKind> m_onlyKinds;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
Reference in New Issue
Block a user