diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 78d3793b502..c22f1f74456 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -1257,6 +1257,54 @@ private: 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) : Client(clientInterface(project, jsonDbDir)), d(new Private(this, project)) { @@ -1268,6 +1316,7 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir) setActivateDocumentAutomatically(true); setLogTarget(LogTarget::Console); setCompletionAssistProvider(new ClangdCompletionAssistProvider(this)); + setQuickFixAssistProvider(new ClangdQuickFixProvider(this)); if (!project) { QJsonObject initOptions; const QStringList clangOptions = createClangOptions( diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index e79d8a17675..a79e26802d6 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -162,6 +162,8 @@ CompletionAssistProvider *CppEditorDocument::functionHintAssistProvider() const TextEditor::IAssistProvider *CppEditorDocument::quickFixAssistProvider() const { + if (const auto baseProvider = TextDocument::quickFixAssistProvider()) + return baseProvider; return CppEditorPlugin::instance()->quickFixProvider(); } diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index f254abc8df3..9d5d2271a6c 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -53,14 +53,8 @@ class CppQuickFixAssistProcessor : public IAssistProcessor { IAssistProposal *perform(const AssistInterface *interface) override { - QSharedPointer assistInterface(interface); - auto cppInterface = assistInterface.staticCast(); - - QuickFixOperations quickFixes; - for (CppQuickFixFactory *factory : CppQuickFixFactory::cppQuickFixFactories()) - factory->match(*cppInterface, quickFixes); - - return GenericProposal::createProposal(interface, quickFixes); + QSharedPointer dummy(interface); // FIXME: Surely this cannot be our way of doing memory management??? + return GenericProposal::createProposal(interface, quickFixOperations(interface)); } }; @@ -137,5 +131,15 @@ bool CppQuickFixInterface::isCursorOn(const AST *ast) const return currentFile()->isCursorOn(ast); } +QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface) +{ + const auto cppInterface = dynamic_cast(interface); + QTC_ASSERT(cppInterface, return {}); + QuickFixOperations quickFixes; + for (CppQuickFixFactory *factory : CppQuickFixFactory::cppQuickFixFactories()) + factory->match(*cppInterface, quickFixes); + return quickFixes; +} + } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppquickfixassistant.h b/src/plugins/cppeditor/cppquickfixassistant.h index d5e626cb0e0..d6e556c7b19 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.h +++ b/src/plugins/cppeditor/cppquickfixassistant.h @@ -29,6 +29,7 @@ #include #include +#include #include @@ -73,5 +74,7 @@ public: TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override; }; +TextEditor::QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface); + } // Internal } // CppEditor diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp index bd5b702360d..7f94a6e05c8 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.cpp +++ b/src/plugins/cppeditor/cpptoolsreuse.cpp @@ -31,6 +31,7 @@ #include "cppeditorplugin.h" #include "cpphighlighter.h" #include "cppqtstyleindenter.h" +#include "cppquickfixassistant.h" #include "cpprefactoringchanges.h" #include "projectinfo.h" @@ -339,6 +340,11 @@ bool isInCommentOrString(const TextEditor::AssistInterface *interface, return true; } +TextEditor::QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface) +{ + return Internal::quickFixOperations(interface); +} + CppCodeModelSettings *codeModelSettings() { return Internal::CppEditorPlugin::instance()->codeModelSettings(); diff --git a/src/plugins/cppeditor/cpptoolsreuse.h b/src/plugins/cppeditor/cpptoolsreuse.h index e7023b7be78..59ea0a9994e 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.h +++ b/src/plugins/cppeditor/cpptoolsreuse.h @@ -31,6 +31,7 @@ #include "compileroptionsbuilder.h" #include "projectpart.h" +#include #include #include @@ -76,6 +77,8 @@ const CPlusPlus::Macro CPPEDITOR_EXPORT *findCanonicalMacro(const QTextCursor &c bool CPPEDITOR_EXPORT isInCommentOrString(const TextEditor::AssistInterface *interface, CPlusPlus::LanguageFeatures features); +TextEditor::QuickFixOperations CPPEDITOR_EXPORT +quickFixOperations(const TextEditor::AssistInterface *interface); enum class CacheUsage { ReadWrite, ReadOnly }; diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 97c7c2676b7..bc63a2486be 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -1079,6 +1079,12 @@ void Client::setCompletionAssistProvider(LanguageClientCompletionAssistProvider m_clientProviders.completionAssistProvider = provider; } +void Client::setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider) +{ + delete m_clientProviders.quickFixAssistProvider; + m_clientProviders.quickFixAssistProvider = provider; +} + void Client::start() { LanguageClientManager::addClient(this); diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index cde987670b8..a3fb2e6988e 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -192,6 +192,7 @@ public: LanguageServerProtocol::SymbolStringifier symbolStringifier() const; void setSnippetsGroup(const QString &group); void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider); + void setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider); // logging enum class LogTarget { Console, Ui }; diff --git a/src/plugins/languageclient/languageclientquickfix.cpp b/src/plugins/languageclient/languageclientquickfix.cpp index 93d85b15f21..ed3d87df38a 100644 --- a/src/plugins/languageclient/languageclientquickfix.cpp +++ b/src/plugins/languageclient/languageclientquickfix.cpp @@ -30,7 +30,6 @@ #include #include -#include #include @@ -74,22 +73,6 @@ private: QPointer 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 m_assistInterface; - Client *m_client = nullptr; // not owned - Utils::optional m_currentRequest; -}; - IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInterface *interface) { m_assistInterface = QSharedPointer(interface); @@ -109,6 +92,8 @@ IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInte auto uri = DocumentUri::fromFilePath(interface->filePath()); params.setTextDocument(TextDocumentIdentifier(uri)); CodeActionParams::CodeActionContext context; + if (!m_onlyKinds.isEmpty()) + context.setOnly(m_onlyKinds); context.setDiagnostics(m_client->diagnosticsAt(uri, cursor)); params.setContext(context); @@ -131,8 +116,12 @@ void LanguageClientQuickFixAssistProcessor::cancel() } } -void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse( - const CodeActionRequest::Response &response) +void LanguageClientQuickFixAssistProcessor::setOnlyKinds(const QList &only) +{ + m_onlyKinds = only; +} + +void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse(const CodeActionRequest::Response &response) { m_currentRequest.reset(); if (const Utils::optional &error = response.error()) @@ -150,6 +139,11 @@ void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse( } } m_client->removeAssistProcessor(this); + handleProposalReady(ops); +} + +void LanguageClientQuickFixAssistProcessor::handleProposalReady(const QuickFixOperations &ops) +{ setAsyncProposalAvailable(GenericProposal::createProposal(m_assistInterface.data(), ops)); } diff --git a/src/plugins/languageclient/languageclientquickfix.h b/src/plugins/languageclient/languageclientquickfix.h index 955f12bc87e..988fb667a25 100644 --- a/src/plugins/languageclient/languageclientquickfix.h +++ b/src/plugins/languageclient/languageclientquickfix.h @@ -28,12 +28,15 @@ #include "languageclient_global.h" #include +#include #include #include #include +namespace TextEditor { class IAssistProposal; } + namespace LanguageClient { class Client; @@ -56,8 +59,34 @@ public: IAssistProvider::RunType runType() const override; TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override; +protected: + Client *client() const { return m_client; } + private: 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 &only); + +private: + void handleCodeActionResponse(const LanguageServerProtocol::CodeActionRequest::Response &response); + virtual void handleProposalReady(const TextEditor::QuickFixOperations &ops); + + QSharedPointer m_assistInterface; + Client *m_client = nullptr; // not owned + Utils::optional m_currentRequest; + QList m_onlyKinds; +}; + + } // namespace LanguageClient