VitaNuo updated this revision to Diff 544751.
VitaNuo marked 8 inline comments as done.
VitaNuo added a comment.

Address review comments. Update the action to trigger when source action is 
requested explicitly or from the preamble range. Add unit test.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153769/new/

https://reviews.llvm.org/D153769

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/ClangdServer.h
  clang-tools-extra/clangd/Protocol.cpp
  clang-tools-extra/clangd/Protocol.h
  clang-tools-extra/clangd/refactor/Tweak.cpp
  clang-tools-extra/clangd/refactor/Tweak.h
  clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
  clang-tools-extra/clangd/refactor/tweaks/OrganizeImports.cpp
  clang-tools-extra/clangd/test/code-action-request.test
  clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
  clang-tools-extra/clangd/test/request-reply.test
  clang-tools-extra/clangd/test/tweaks-format.test
  clang-tools-extra/clangd/tool/Check.cpp
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/ClangdTests.cpp
  clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
  clang-tools-extra/clangd/unittests/tweaks/OrganizeImportsTests.cpp
  clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp

Index: clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp
+++ clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp
@@ -8,6 +8,7 @@
 
 #include "TweakTesting.h"
 
+#include "Protocol.h"
 #include "SourceCode.h"
 #include "TestTU.h"
 #include "refactor/Tweak.h"
@@ -65,18 +66,19 @@
 applyTweak(ParsedAST &AST, llvm::Annotations::Range Range, StringRef TweakID,
            const SymbolIndex *Index, llvm::vfs::FileSystem *FS) {
   std::optional<llvm::Expected<Tweak::Effect>> Result;
-  SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Range.Begin,
-                            Range.End, [&](SelectionTree ST) {
-                              Tweak::Selection S(Index, AST, Range.Begin,
-                                                 Range.End, std::move(ST), FS);
-                              if (auto T = prepareTweak(TweakID, S, nullptr)) {
-                                Result = (*T)->apply(S);
-                                return true;
-                              } else {
-                                llvm::consumeError(T.takeError());
-                                return false;
-                              }
-                            });
+  SelectionTree::createEach(
+      AST.getASTContext(), AST.getTokens(), Range.Begin, Range.End,
+      [&](SelectionTree ST) {
+        Tweak::Selection S(Index, AST, Range.Begin, Range.End, std::move(ST),
+                           FS, {std::string{CodeAction::SOURCE_KIND}});
+        if (auto T = prepareTweak(TweakID, S, nullptr)) {
+          Result = (*T)->apply(S);
+          return true;
+        } else {
+          llvm::consumeError(T.takeError());
+          return false;
+        }
+      });
   return Result;
 }
 
Index: clang-tools-extra/clangd/unittests/tweaks/OrganizeImportsTests.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/unittests/tweaks/OrganizeImportsTests.cpp
@@ -0,0 +1,84 @@
+//===--OrganizeImportsTest.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TweakTesting.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+TWEAK_TEST(OrganizeImports);
+
+TEST_F(OrganizeImportsTest, All) {
+  Header = "void foo();";
+  struct {
+    llvm::StringRef Code;
+    llvm::StringRef Header;
+    llvm::StringRef Expected;
+  } Cases[] = {{// Remove unused include.
+                R"cpp(
+#include "TestTU.h"
+void foo() {}
+void b^ar() {
+    foo();
+}
+)cpp",
+                R"cpp(
+#pragma once
+)cpp",
+                R"cpp(
+void foo() {}
+void bar() {
+    foo();
+}
+)cpp"},
+               {// Add missing include.
+                R"cpp(
+void b^ar() {
+    foo();
+}
+)cpp",
+                R"cpp(
+#pragma once
+void foo();
+)cpp",
+                R"cpp(
+#include "TestTU.h"
+void bar() {
+    foo();
+}
+)cpp"},
+               {// Replace unused include with missing.
+                R"cpp(
+#include "foo.h"
+void b^ar() {
+    foo();
+}
+)cpp",
+                R"cpp(
+#pragma once
+void foo();
+)cpp",
+                R"cpp(
+#include "TestTU.h"
+void bar() {
+    foo();
+}
+)cpp"}};
+  for (auto C : Cases) {
+    Header = C.Header;
+    ExtraFiles["foo.h"] = "#pragma once";
+    EXPECT_EQ(C.Expected, apply(C.Code)) << C.Expected;
+  }
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
+++ clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
@@ -49,8 +49,8 @@
   auto Tree =
       SelectionTree::createRight(AST.getASTContext(), AST.getTokens(), 0, 0);
   auto Actual = prepareTweak(
-      TweakID, Tweak::Selection(nullptr, AST, 0, 0, std::move(Tree), nullptr),
-      &Set);
+      TweakID,
+      Tweak::Selection(nullptr, AST, 0, 0, std::move(Tree), nullptr, {}), &Set);
   ASSERT_TRUE(bool(Actual));
   EXPECT_EQ(Actual->get()->id(), TweakID);
 }
Index: clang-tools-extra/clangd/unittests/ClangdTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -1303,12 +1303,13 @@
 
   // Ensure that disabled formatting is respected.
   Notification N;
-  Server.applyTweak(FooCpp, {}, TweakID, [&](llvm::Expected<Tweak::Effect> E) {
-    ASSERT_TRUE(static_cast<bool>(E));
-    EXPECT_THAT(llvm::cantFail(E->ApplyEdits.lookup(FooCpp).apply()),
-                NewContents);
-    N.notify();
-  });
+  Server.applyTweak(
+      FooCpp, {}, TweakID, {}, [&](llvm::Expected<Tweak::Effect> E) {
+        ASSERT_TRUE(static_cast<bool>(E));
+        EXPECT_THAT(llvm::cantFail(E->ApplyEdits.lookup(FooCpp).apply()),
+                    NewContents);
+        N.notify();
+      });
   N.wait();
 }
 
Index: clang-tools-extra/clangd/unittests/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/unittests/CMakeLists.txt
+++ clang-tools-extra/clangd/unittests/CMakeLists.txt
@@ -128,6 +128,7 @@
   tweaks/MemberwiseConstructorTests.cpp
   tweaks/ObjCLocalizeStringLiteralTests.cpp
   tweaks/ObjCMemberwiseInitializerTests.cpp
+  tweaks/OrganizeImportsTests.cpp
   tweaks/PopulateSwitchTests.cpp
   tweaks/RawStringLiteralTests.cpp
   tweaks/RemoveUsingNamespaceTests.cpp
Index: clang-tools-extra/clangd/tool/Check.cpp
===================================================================
--- clang-tools-extra/clangd/tool/Check.cpp
+++ clang-tools-extra/clangd/tool/Check.cpp
@@ -402,7 +402,7 @@
       auto Tree = SelectionTree::createRight(AST->getASTContext(),
                                              AST->getTokens(), Start, End);
       Tweak::Selection Selection(&Index, *AST, Start, End, std::move(Tree),
-                                 nullptr);
+                                 nullptr, {});
       // FS is only populated when applying a tweak, not during prepare as
       // prepare should not do any I/O to be fast.
       auto Tweaks =
Index: clang-tools-extra/clangd/test/tweaks-format.test
===================================================================
--- clang-tools-extra/clangd/test/tweaks-format.test
+++ clang-tools-extra/clangd/test/tweaks-format.test
@@ -5,7 +5,7 @@
 ---
 {"jsonrpc":"2.0","id":5,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///main.cc"},"range":{"start":{"line":0,"character":11},"end":{"line":0,"character":11}},"context":{"diagnostics":[]}}}
 ---
-{"jsonrpc":"2.0","id":6,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"test:///main.cc","selection":{"end":{"character":11,"line":0},"start":{"character":11,"line":0}},"tweakID":"SwapIfBranches"}]}}
+{"jsonrpc":"2.0","id":6,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"test:///main.cc","requestedActionKinds":[],"selection":{"end":{"character":11,"line":0},"start":{"character":11,"line":0}},"tweakID":"SwapIfBranches"}]}}
 #      CHECK:    "newText": "\n  ",
 # CHECK-NEXT:    "range": {
 # CHECK-NEXT:      "end": {
Index: clang-tools-extra/clangd/test/request-reply.test
===================================================================
--- clang-tools-extra/clangd/test/request-reply.test
+++ clang-tools-extra/clangd/test/request-reply.test
@@ -3,7 +3,7 @@
 ---
 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"auto i = 0;"}}}
 ---
-{"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":"ExpandDeducedType"}]}}
+{"jsonrpc":"2.0","id":4,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"test:///main.cpp","requestedActionKinds":[],"selection":{"end":{"character":4,"line":0},"start":{"character":0,"line":0}},"tweakID":"ExpandDeducedType"}]}}
 #      CHECK:  "id": 0,
 #      CHECK:  "method": "workspace/applyEdit",
 #      CHECK:  "newText": "int",
@@ -25,7 +25,7 @@
 # CHECK-NEXT:  },
 # CHECK-NEXT:  "id": 4,
 ---
-{"jsonrpc":"2.0","id":5,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"test:///main.cpp","selection":{"end":{"character":4,"line":0},"start":{"character":0,"line":0}},"tweakID":"ExpandDeducedType"}]}}
+{"jsonrpc":"2.0","id":5,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"test:///main.cpp","requestedActionKinds":[],"selection":{"end":{"character":4,"line":0},"start":{"character":0,"line":0}},"tweakID":"ExpandDeducedType"}]}}
 #      CHECK:  "id": 1,
 #      CHECK:  "method": "workspace/applyEdit",
 ---
Index: clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
===================================================================
--- clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
+++ clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
@@ -485,6 +485,27 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyFix",
 # CHECK-NEXT:      "title": "Apply fix: fix all includes"
+# CHECK-NEXT:    },
+# CHECK-NEXT: {
+# CHECK-NEXT:      "arguments": [
+# CHECK-NEXT:        {
+# CHECK-NEXT:          "file": "file:///clangd-test/simple.cpp",
+# CHECK-NEXT:          "requestedActionKinds": [],
+# CHECK-NEXT:          "selection": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": 17,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": 0,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "tweakID": "OrganizeImports"
+# CHECK-NEXT:        }
+# CHECK-NEXT:      ],
+# CHECK-NEXT:      "command": "clangd.applyTweak",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
Index: clang-tools-extra/clangd/test/code-action-request.test
===================================================================
--- clang-tools-extra/clangd/test/code-action-request.test
+++ clang-tools-extra/clangd/test/code-action-request.test
@@ -33,6 +33,7 @@
 # CHECK-NEXT:      "arguments": [
 # CHECK-NEXT:        {
 # CHECK-NEXT:          "file": "file://{{.*}}/clangd-test/main.cpp",
+# CHECK-NEXT:          "requestedActionKinds": [],
 # CHECK-NEXT:          "selection": {
 # CHECK-NEXT:            "end": {
 # CHECK-NEXT:              "character": 4,
@@ -92,7 +93,7 @@
 # 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":"ExpandDeducedType"}]}}
+{"jsonrpc":"2.0","id":4,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"test:///main.cpp","requestedActionKinds": [],"selection":{"end":{"character":4,"line":0},"start":{"character":0,"line":0}},"tweakID":"ExpandDeducedType"}]}}
 #      CHECK:    "newText": "int",
 # CHECK-NEXT:    "range": {
 # CHECK-NEXT:      "end": {
Index: clang-tools-extra/clangd/refactor/tweaks/OrganizeImports.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/refactor/tweaks/OrganizeImports.cpp
@@ -0,0 +1,111 @@
+//===--- OrganizeImports.cpp -------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeCleaner.h"
+#include "Protocol.h"
+#include "SourceCode.h"
+#include "clang-include-cleaner/IncludeSpeller.h"
+#include "clang-include-cleaner/Types.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <algorithm>
+#include <cassert>
+#include <climits>
+#include <string>
+
+namespace clang::clangd {
+namespace {
+
+// Tweak for applying IWYU-related changes (removing unused and adding missing
+// includes) in batch. The tweak can be triggered via the "Organize Imports"
+// source action (VS Code). The set of changes applied fully corresponds to the
+// findings from the clang-include-cleaner tool.
+class OrganizeImports : public Tweak {
+public:
+  const char *id() const override;
+
+  bool prepare(const Selection &Inputs) override;
+  Expected<Effect> apply(const Selection &Inputs) override;
+  std::string title() const override {
+    return "Remove unused and add missing includes";
+  }
+  llvm::StringLiteral kind() const override { return CodeAction::SOURCE_KIND; }
+};
+REGISTER_TWEAK(OrganizeImports)
+
+bool OrganizeImports::prepare(const Tweak::Selection &Inputs) {
+  if (std::find(Inputs.RequestedActionKinds.begin(),
+                Inputs.RequestedActionKinds.end(),
+                CodeAction::SOURCE_KIND) != Inputs.RequestedActionKinds.end())
+    return true;
+  const auto &Includes = Inputs.AST->getIncludeStructure().MainFileIncludes;
+  if (Includes.empty())
+    return false;
+  Range PreambleRange;
+  PreambleRange.start =
+      rangeTillEOL(Inputs.Code, Includes.front().HashOffset).start;
+  PreambleRange.end = rangeTillEOL(Inputs.Code, Includes.back().HashOffset).end;
+  Range SelectionRange =
+      Range{offsetToPosition(Inputs.Code, Inputs.SelectionBegin),
+            offsetToPosition(Inputs.Code, Inputs.SelectionEnd)};
+  return SelectionRange.start <= PreambleRange.end &&
+         PreambleRange.start <= SelectionRange.end;
+}
+
+Expected<Tweak::Effect> OrganizeImports::apply(const Selection &Inputs) {
+  IncludeCleanerFindings Findings = computeIncludeCleanerFindings(*Inputs.AST);
+  const auto MainFilePath = Inputs.AST->tuPath();
+  tooling::Replacements Replacements;
+  for (const auto *Inc : Findings.UnusedIncludes)
+    if (auto Err = Replacements.add(
+            tooling::Replacement{MainFilePath, UINT_MAX, 1, Inc->Written}))
+      return Err;
+
+  const auto &SM = Inputs.AST->getSourceManager();
+  llvm::DenseSet<include_cleaner::Header> Providers;
+  for (const auto &Missing : Findings.MissingIncludes) {
+    assert(!Missing.Providers.empty());
+    Providers.insert(Missing.Providers[0]);
+  }
+
+  for (const auto &P : Providers) {
+    std::string Spelling = include_cleaner::spellHeader(
+        {P, Inputs.AST->getPreprocessor().getHeaderSearchInfo(),
+         SM.getFileEntryForID(SM.getMainFileID())});
+    if (auto Err = Replacements.add(tooling::Replacement{
+            MainFilePath, UINT_MAX, 0, "#include " + Spelling}))
+      return Err;
+  }
+
+  if (Replacements.empty())
+    return Tweak::Effect{"No edits to apply.", {}};
+
+  auto FileStyle =
+      format::getStyle(format::DefaultFormatStyle, MainFilePath,
+                       format::DefaultFallbackStyle, Inputs.Code, Inputs.FS);
+  if (!FileStyle) {
+    elog("Couldn't get style for {0}: {1}", MainFilePath,
+         FileStyle.takeError());
+    FileStyle = format::getLLVMStyle();
+  }
+  auto Final =
+      format::cleanupAroundReplacements(Inputs.Code, Replacements, *FileStyle);
+  if (!Final)
+    return Final.takeError();
+  return Effect::mainFileEdit(SM, *Final);
+}
+
+} // namespace
+} // namespace clang::clangd
Index: clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -24,6 +24,7 @@
   MemberwiseConstructor.cpp
   ObjCLocalizeStringLiteral.cpp
   ObjCMemberwiseInitializer.cpp
+  OrganizeImports.cpp
   PopulateSwitch.cpp
   RawStringLiteral.cpp
   RemoveUsingNamespace.cpp
Index: clang-tools-extra/clangd/refactor/Tweak.h
===================================================================
--- clang-tools-extra/clangd/refactor/Tweak.h
+++ clang-tools-extra/clangd/refactor/Tweak.h
@@ -49,7 +49,8 @@
   struct Selection {
     Selection(const SymbolIndex *Index, ParsedAST &AST, unsigned RangeBegin,
               unsigned RangeEnd, SelectionTree ASTSelection,
-              llvm::vfs::FileSystem *VFS);
+              llvm::vfs::FileSystem *VFS,
+              const std::vector<std::string> &RequestedActionKinds);
     /// The text of the active document.
     llvm::StringRef Code;
     /// The Index for handling codebase related queries.
@@ -68,6 +69,9 @@
     /// File system used to access source code (for cross-file tweaks).
     /// This is only populated when applying a tweak, not during prepare.
     llvm::vfs::FileSystem *FS = nullptr;
+    /// Requested code action kinds from the `only` field of
+    /// code action request context.
+    std::vector<std::string> RequestedActionKinds;
     // FIXME: provide a way to get sources and ASTs for other files.
   };
 
Index: clang-tools-extra/clangd/refactor/Tweak.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/Tweak.cpp
+++ clang-tools-extra/clangd/refactor/Tweak.cpp
@@ -57,12 +57,13 @@
 }
 } // namespace
 
-Tweak::Selection::Selection(const SymbolIndex *Index, ParsedAST &AST,
-                            unsigned RangeBegin, unsigned RangeEnd,
-                            SelectionTree ASTSelection,
-                            llvm::vfs::FileSystem *FS)
+Tweak::Selection::Selection(
+    const SymbolIndex *Index, ParsedAST &AST, unsigned RangeBegin,
+    unsigned RangeEnd, SelectionTree ASTSelection, llvm::vfs::FileSystem *FS,
+    const std::vector<std::string> &RequestedActionKinds)
     : Index(Index), AST(&AST), SelectionBegin(RangeBegin),
-      SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)), FS(FS) {
+      SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)), FS(FS),
+      RequestedActionKinds(RequestedActionKinds) {
   auto &SM = AST.getSourceManager();
   Code = SM.getBufferData(SM.getMainFileID());
   Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin);
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -28,6 +28,7 @@
 #include "support/MemoryTree.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/raw_ostream.h"
 #include <bitset>
@@ -1035,6 +1036,9 @@
   Range selection;
   /// ID of the tweak that should be executed. Corresponds to Tweak::id().
   std::string tweakID;
+  /// Code action kinds requested by the client via the `only` field in
+  /// the context.
+  std::vector<std::string> requestedActionKinds;
 };
 bool fromJSON(const llvm::json::Value &, TweakArgs &, llvm::json::Path);
 llvm::json::Value toJSON(const TweakArgs &A);
@@ -1070,6 +1074,7 @@
   const static llvm::StringLiteral QUICKFIX_KIND;
   const static llvm::StringLiteral REFACTOR_KIND;
   const static llvm::StringLiteral INFO_KIND;
+  const static llvm::StringLiteral SOURCE_KIND;
 
   /// The diagnostics that this code action resolves.
   std::optional<std::vector<Diagnostic>> diagnostics;
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -869,6 +869,7 @@
 const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix";
 const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor";
 const llvm::StringLiteral CodeAction::INFO_KIND = "info";
+const llvm::StringLiteral CodeAction::SOURCE_KIND = "source";
 
 llvm::json::Value toJSON(const CodeAction &CA) {
   auto CodeAction = llvm::json::Object{{"title", CA.title}};
@@ -928,12 +929,15 @@
               llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
   return O && O.map("file", A.file) && O.map("selection", A.selection) &&
-         O.map("tweakID", A.tweakID);
+         O.map("tweakID", A.tweakID) &&
+         O.map("requestedActionKinds", A.requestedActionKinds);
 }
 
 llvm::json::Value toJSON(const TweakArgs &A) {
-  return llvm::json::Object{
-      {"tweakID", A.tweakID}, {"selection", A.selection}, {"file", A.file}};
+  return llvm::json::Object{{"tweakID", A.tweakID},
+                            {"selection", A.selection},
+                            {"file", A.file},
+                            {"requestedActionKinds", A.requestedActionKinds}};
 }
 
 llvm::json::Value toJSON(const ApplyWorkspaceEditParams &Params) {
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -395,6 +395,7 @@
 
   /// Apply the code tweak with a specified \p ID.
   void applyTweak(PathRef File, Range Sel, StringRef ID,
+                  const std::vector<std::string> &ActionKinds,
                   Callback<Tweak::Effect> CB);
 
   /// Called when an event occurs for a watched file in the workspace.
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -52,8 +52,8 @@
 #include <optional>
 #include <string>
 #include <type_traits>
-#include <vector>
 #include <utility>
+#include <vector>
 
 namespace clang {
 namespace clangd {
@@ -627,7 +627,8 @@
 // vector of pointers because GCC doesn't like non-copyable Selection.
 static llvm::Expected<std::vector<std::unique_ptr<Tweak::Selection>>>
 tweakSelection(const Range &Sel, const InputsAndAST &AST,
-               llvm::vfs::FileSystem *FS) {
+               llvm::vfs::FileSystem *FS,
+               const std::vector<std::string> &RequestedActionKinds) {
   auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start);
   if (!Begin)
     return Begin.takeError();
@@ -635,13 +636,14 @@
   if (!End)
     return End.takeError();
   std::vector<std::unique_ptr<Tweak::Selection>> Result;
-  SelectionTree::createEach(
-      AST.AST.getASTContext(), AST.AST.getTokens(), *Begin, *End,
-      [&](SelectionTree T) {
-        Result.push_back(std::make_unique<Tweak::Selection>(
-            AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T), FS));
-        return false;
-      });
+  SelectionTree::createEach(AST.AST.getASTContext(), AST.AST.getTokens(),
+                            *Begin, *End, [&](SelectionTree T) {
+                              Result.push_back(
+                                  std::make_unique<Tweak::Selection>(
+                                      AST.Inputs.Index, AST.AST, *Begin, *End,
+                                      std::move(T), FS, RequestedActionKinds));
+                              return false;
+                            });
   assert(!Result.empty() && "Expected at least one SelectionTree");
   return std::move(Result);
 }
@@ -679,7 +681,8 @@
     }
 
     // Collect Tweaks
-    auto Selections = tweakSelection(Params.Selection, *InpAST, /*FS=*/nullptr);
+    auto Selections = tweakSelection(Params.Selection, *InpAST, /*FS=*/nullptr,
+                                     Params.RequestedActionKinds);
     if (!Selections)
       return CB(Selections.takeError());
     // Don't allow a tweak to fire more than once across ambiguous selections.
@@ -710,7 +713,8 @@
                     Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
       return CB(InpAST.takeError());
-    auto Selections = tweakSelection(Sel, *InpAST, /*FS=*/nullptr);
+    auto Selections = tweakSelection(Sel, *InpAST, /*FS=*/nullptr,
+                                     /*RequestedActionKinds=*/{});
     if (!Selections)
       return CB(Selections.takeError());
     std::vector<TweakRef> Res;
@@ -735,6 +739,7 @@
 }
 
 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
+                              const std::vector<std::string> &ActionKinds,
                               Callback<Tweak::Effect> CB) {
   // Tracks number of times a tweak has been attempted.
   static constexpr trace::Metric TweakAttempt(
@@ -744,12 +749,12 @@
       "tweak_failed", trace::Metric::Counter, "tweak_id");
   TweakAttempt.record(1, TweakID);
   auto Action = [File = File.str(), Sel, TweakID = TweakID.str(),
-                 CB = std::move(CB),
+                 CB = std::move(CB), ActionKinds,
                  this](Expected<InputsAndAST> InpAST) mutable {
     if (!InpAST)
       return CB(InpAST.takeError());
     auto FS = DirtyFS->view(std::nullopt);
-    auto Selections = tweakSelection(Sel, *InpAST, FS.get());
+    auto Selections = tweakSelection(Sel, *InpAST, FS.get(), ActionKinds);
     if (!Selections)
       return CB(Selections.takeError());
     std::optional<llvm::Expected<Tweak::Effect>> Effect;
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -77,7 +77,8 @@
 /// Transforms a tweak into a code action that would apply it if executed.
 /// EXPECTS: T.prepare() was called and returned true.
 CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File,
-                        Range Selection) {
+                        Range Selection,
+                        const std::vector<std::string> &RequestedActionKinds) {
   CodeAction CA;
   CA.title = T.Title;
   CA.kind = T.Kind.str();
@@ -93,6 +94,7 @@
   Args.file = File;
   Args.tweakID = T.ID;
   Args.selection = Selection;
+  Args.requestedActionKinds = RequestedActionKinds;
   CA.command->argument = std::move(Args);
   return CA;
 }
@@ -648,7 +650,8 @@
           ? llvm::json::Object{{"codeActionKinds",
                                 {CodeAction::QUICKFIX_KIND,
                                  CodeAction::REFACTOR_KIND,
-                                 CodeAction::INFO_KIND}}}
+                                 CodeAction::INFO_KIND,
+                                 CodeAction::SOURCE_KIND}}}
           : llvm::json::Value(true);
 
   std::vector<llvm::StringRef> Commands;
@@ -805,7 +808,7 @@
     return applyEdit(std::move(WE), "Tweak applied.", std::move(Reply));
   };
   Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID,
-                     std::move(Action));
+                     Args.requestedActionKinds, std::move(Action));
 }
 
 void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success,
@@ -1023,13 +1026,10 @@
   Inputs.File = File.file();
   Inputs.Selection = Params.range;
   Inputs.RequestedActionKinds = Params.context.only;
-  Inputs.TweakFilter = [this](const Tweak &T) {
-    return Opts.TweakFilter(T);
-  };
-  auto CB = [this,
-             Reply = std::move(Reply),
-             ToLSPDiags = std::move(ToLSPDiags), File,
-             Selection = Params.range](
+  Inputs.TweakFilter = [this](const Tweak &T) { return Opts.TweakFilter(T); };
+  auto CB = [this, Reply = std::move(Reply), ToLSPDiags = std::move(ToLSPDiags),
+             File, Selection = Params.range,
+             ActionKinds = Inputs.RequestedActionKinds](
                 llvm::Expected<ClangdServer::CodeActionResult> Fixits) mutable {
     if (!Fixits)
       return Reply(Fixits.takeError());
@@ -1038,13 +1038,12 @@
     for (const auto &QF : Fixits->QuickFixes) {
       CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
                                  SupportsChangeAnnotation));
-      if (auto It = ToLSPDiags.find(QF.Diag);
-          It != ToLSPDiags.end()) {
+      if (auto It = ToLSPDiags.find(QF.Diag); It != ToLSPDiags.end()) {
         CAs.back().diagnostics = {It->second};
       }
     }
     for (const auto &TR : Fixits->TweakRefs)
-      CAs.push_back(toCodeAction(TR, File, Selection));
+      CAs.push_back(toCodeAction(TR, File, Selection, ActionKinds));
 
     // If there's exactly one quick-fix, call it "preferred".
     // We never consider refactorings etc as preferred.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D153769: [clang... Viktoriia Bakalova via Phabricator via cfe-commits

Reply via email to