From 5d615e916fec7b75eb5312d7442c37dc59967856 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 17 May 2022 12:34:00 +0200 Subject: [PATCH] LanguageClient: Improve refactoring markers Clicking the lightbulb should bring up a menu if and only if there is more than one possible action for this location. Amends 089e1edcbf. Change-Id: I45348ed4fbf9b3f32e19bbe17f0c2c030ecd24ed Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 7 +- src/plugins/languageclient/client.cpp | 7 +- .../languageclient/languageclientutils.cpp | 75 +++++++++++-------- .../languageclient/languageclientutils.h | 2 +- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 8dc641e3c42..3aad007768c 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -1693,12 +1693,11 @@ void ClangdClient::handleDiagnostics(const PublishDiagnosticsParams ¶ms) return; for (const Diagnostic &diagnostic : params.diagnostics()) { const ClangdDiagnostic clangdDiagnostic(diagnostic); - const auto codeActions = clangdDiagnostic.codeActions(); + auto codeActions = clangdDiagnostic.codeActions(); if (codeActions && !codeActions->isEmpty()) { - for (CodeAction action : *codeActions) { + for (CodeAction &action : *codeActions) action.setDiagnostics({diagnostic}); - LanguageClient::updateCodeActionRefactoringMarker(this, action, uri); - } + LanguageClient::updateCodeActionRefactoringMarker(this, *codeActions, uri); } else { // We know that there's only one kind of diagnostic for which clangd has // a quickfix tweak, so let's not be wasteful. diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index b2be6a03fbc..10c42e0a0bb 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -955,13 +955,14 @@ void Client::handleCodeActionResponse(const CodeActionRequest::Response &respons log(*error); if (const Utils::optional &result = response.result()) { if (auto list = Utils::get_if>>(&*result)) { + QList codeActions; for (const Utils::variant &item : *list) { if (auto action = Utils::get_if(&item)) - updateCodeActionRefactoringMarker(this, *action, uri); - else if (auto command = Utils::get_if(&item)) { + codeActions << *action; + else if (auto command = Utils::get_if(&item)) Q_UNUSED(command) // todo - } } + updateCodeActionRefactoringMarker(this, codeActions, uri); } } } diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index a9e714c0f6b..aa5d540f8da 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -149,7 +149,7 @@ QTextCursor endOfLineCursor(const QTextCursor &cursor) } void updateCodeActionRefactoringMarker(Client *client, - const CodeAction &action, + const QList &actions, const DocumentUri &uri) { TextDocument* doc = TextDocument::textDocumentForFilePath(uri.toFilePath()); @@ -159,46 +159,59 @@ void updateCodeActionRefactoringMarker(Client *client, if (editors.isEmpty()) return; - const QList &diagnostics = action.diagnostics().value_or(QList()); - QHash markersAtBlock; - RefactorMarker marker; - marker.type = client->id(); - marker.tooltip = LanguageClientManager::tr("Show available quick fixes"); - - auto addMarkerForCursor = [&](const QTextCursor &cursor) { - if (!markersAtBlock.contains(cursor.blockNumber())) { - marker.cursor = cursor; - marker.callback = [=](TextEditorWidget *editor) { + const auto addMarkerForCursor = [&](const CodeAction &action, const Range &range) { + const QTextCursor cursor = endOfLineCursor(range.start().toTextCursor(doc->document())); + const auto it = markersAtBlock.find(cursor.blockNumber()); + if (it != markersAtBlock.end()) { + it->tooltip = LanguageClientManager::tr("Show available quick fixes"); + it->callback = [cursor](TextEditorWidget *editor) { editor->setTextCursor(cursor); editor->invokeAssist(TextEditor::QuickFix); }; - markersAtBlock[cursor.blockNumber()] = marker; + return; } + RefactorMarker marker; + marker.type = client->id(); + marker.cursor = cursor; + if (action.isValid()) + marker.tooltip = action.title(); + if (action.edit()) { + marker.callback = [client, edit = action.edit()](const TextEditorWidget *) { + applyWorkspaceEdit(client, *edit); + }; + } else if (action.command()) { + marker.callback = [command = action.command(), + client = QPointer(client)](const TextEditorWidget *) { + if (client) + client->executeCommand(*command); + }; + } + markersAtBlock[cursor.blockNumber()] = marker; }; - if (Utils::optional edit = action.edit()) { - if (diagnostics.isEmpty()) { - QList edits; - if (optional> documentChanges = edit->documentChanges()) { - QList changesForUri = Utils::filtered( - *documentChanges, [uri](const TextDocumentEdit &edit) { - return edit.textDocument().uri() == uri; - }); - for (const TextDocumentEdit &edit : changesForUri) - edits << edit.edits(); - } else if (optional localChanges = edit->changes()) { - edits = (*localChanges)[uri]; - } - for (const TextEdit &edit : qAsConst(edits)) { - addMarkerForCursor( - endOfLineCursor(edit.range().start().toTextCursor(doc->document()))); + for (const CodeAction &action : actions) { + const QList &diagnostics = action.diagnostics().value_or(QList()); + if (Utils::optional edit = action.edit()) { + if (diagnostics.isEmpty()) { + QList edits; + if (optional> documentChanges = edit->documentChanges()) { + QList changesForUri = Utils::filtered( + *documentChanges, [uri](const TextDocumentEdit &edit) { + return edit.textDocument().uri() == uri; + }); + for (const TextDocumentEdit &edit : changesForUri) + edits << edit.edits(); + } else if (optional localChanges = edit->changes()) { + edits = (*localChanges)[uri]; + } + for (const TextEdit &edit : qAsConst(edits)) + addMarkerForCursor(action, edit.range()); } } + for (const Diagnostic &diagnostic : diagnostics) + addMarkerForCursor(action, diagnostic.range()); } - for (const Diagnostic &diagnostic : diagnostics) - addMarkerForCursor( - endOfLineCursor(diagnostic.range().start().toTextCursor(doc->document()))); const RefactorMarkers markers = markersAtBlock.values(); for (BaseTextEditor *editor : editors) { if (TextEditorWidget *editorWidget = editor->editorWidget()) diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h index f97c9a9930d..24a13e6fbe9 100644 --- a/src/plugins/languageclient/languageclientutils.h +++ b/src/plugins/languageclient/languageclientutils.h @@ -59,7 +59,7 @@ void LANGUAGECLIENT_EXPORT applyTextEdit(TextEditor::TextDocumentManipulatorInte bool newTextIsSnippet = false); void LANGUAGECLIENT_EXPORT updateCodeActionRefactoringMarker(Client *client, - const LanguageServerProtocol::CodeAction &action, + const QList &actions, const LanguageServerProtocol::DocumentUri &uri); void updateEditorToolBar(Core::IEditor *editor); const QIcon LANGUAGECLIENT_EXPORT symbolIcon(int type);