diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -993,12 +993,24 @@ if (!Code) return Reply(llvm::make_error( "onCodeAction called for non-added file", ErrorCode::InvalidParams)); + + // Checks whether a particular CodeActionKind is included in the response. + auto KindAllowed = [Only(Params.context.only)](llvm::StringRef Kind) { + if (Only.empty()) + return true; + return llvm::any_of(Only, [&](llvm::StringRef Base) { + return Kind.consume_front(Base) && (Kind.empty() || Kind.startswith(".")); + }); + }; + // We provide a code action for Fixes on the specified diagnostics. std::vector FixIts; - for (const Diagnostic &D : Params.context.diagnostics) { - for (auto &F : getFixes(File.file(), D)) { - FixIts.push_back(toCodeAction(F, Params.textDocument.uri)); - FixIts.back().diagnostics = {D}; + if (KindAllowed(CodeAction::QUICKFIX_KIND)) { + for (const Diagnostic &D : Params.context.diagnostics) { + for (auto &F : getFixes(File.file(), D)) { + FixIts.push_back(toCodeAction(F, Params.textDocument.uri)); + FixIts.back().diagnostics = {D}; + } } } @@ -1038,14 +1050,10 @@ } return Reply(llvm::json::Array(Commands)); }; - Server->enumerateTweaks( File.file(), Params.range, - [&](const Tweak &T) { - if (!Opts.TweakFilter(T)) - return false; - // FIXME: also consider CodeActionContext.only - return true; + [this, KindAllowed(std::move(KindAllowed))](const Tweak &T) { + return Opts.TweakFilter(T) && KindAllowed(T.kind()); }, std::move(ConsumeActions)); } diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -863,8 +863,19 @@ llvm::json::Value toJSON(const PublishDiagnosticsParams &); struct CodeActionContext { - /// An array of diagnostics. + /// An array of diagnostics known on the client side overlapping the range + /// provided to the `textDocument/codeAction` request. They are provided so + /// that the server knows which errors are currently presented to the user for + /// the given range. There is no guarantee that these accurately reflect the + /// error state of the resource. The primary parameter to compute code actions + /// is the provided range. std::vector diagnostics; + + /// Requested kind of actions to return. + /// + /// Actions not of this kind are filtered out by the client before being + /// shown. So servers can omit computing them. + std::vector only; }; bool fromJSON(const llvm::json::Value &, CodeActionContext &, llvm::json::Path); diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -599,7 +599,10 @@ bool fromJSON(const llvm::json::Value &Params, CodeActionContext &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); - return O && O.map("diagnostics", R.diagnostics); + if (!O || !O.map("diagnostics", R.diagnostics)) + return false; + O.map("only", R.only); + return true; } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diagnostic &D) { diff --git a/clang-tools-extra/clangd/test/code-action-request.test b/clang-tools-extra/clangd/test/code-action-request.test --- a/clang-tools-extra/clangd/test/code-action-request.test +++ b/clang-tools-extra/clangd/test/code-action-request.test @@ -51,6 +51,47 @@ # CHECK-NEXT: } # CHECK-NEXT: ] --- +{ + "jsonrpc": "2.0", + "id": 2, + "method": "textDocument/codeAction", + "params": { + "textDocument": { "uri": "test:///main.cpp" }, + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 0, "character": 4} + }, + "context": { + "diagnostics": [], + "only": ["quickfix"] + } + } +} +# CHECK: "id": 2, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [] +--- +{ + "jsonrpc": "2.0", + "id": 3, + "method": "textDocument/codeAction", + "params": { + "textDocument": { "uri": "test:///main.cpp" }, + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 0, "character": 4} + }, + "context": { + "diagnostics": [], + "only": ["refactor"] + } + } +} +# CHECK: "id": 3, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +--- {"jsonrpc":"2.0","id":4,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"test:///main.cpp","selection":{"end":{"character":4,"line":0},"start":{"character":0,"line":0}},"tweakID":"ExpandAutoType"}]}} # CHECK: "newText": "int", # CHECK-NEXT: "range": { @@ -64,7 +105,7 @@ # CHECK-NEXT: } # CHECK-NEXT: } --- -{"jsonrpc":"2.0","id":4,"method":"shutdown"} +{"jsonrpc":"2.0","id":5,"method":"shutdown"} --- {"jsonrpc":"2.0","method":"exit"} ---