arphaman created this revision.
Herald added subscribers: ilya-biryukov, mgorny.

This patch adds support for editor commands that allow refactoring to be used 
in editor clients like libclang or clangd.
An editor command can be bound to an refactoring action rule. Once it is bound, 
it's available in editors that use the supported editor clients.

I plan on sending out a follow-up patch for clangd support tomorrow.


Repository:
  rL LLVM

https://reviews.llvm.org/D38985

Files:
  include/clang/Tooling/Refactoring/EditorCommandRegistry.def
  include/clang/Tooling/Refactoring/EditorCommands.h
  include/clang/Tooling/Refactoring/RefactoringActionRule.h
  include/clang/module.modulemap
  lib/Tooling/Refactoring/CMakeLists.txt
  lib/Tooling/Refactoring/EditorCommand.cpp
  lib/Tooling/Refactoring/Extract.cpp
  unittests/Tooling/RefactoringActionRulesTest.cpp

Index: unittests/Tooling/RefactoringActionRulesTest.cpp
===================================================================
--- unittests/Tooling/RefactoringActionRulesTest.cpp
+++ unittests/Tooling/RefactoringActionRulesTest.cpp
@@ -10,6 +10,8 @@
 #include "ReplacementTest.h"
 #include "RewriterTestContext.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/EditorCommands.h"
+#include "clang/Tooling/Refactoring/RefactoringAction.h"
 #include "clang/Tooling/Refactoring/RefactoringActionRules.h"
 #include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
 #include "clang/Tooling/Refactoring/Rename/SymbolName.h"
@@ -219,4 +221,23 @@
             SourceRange(Cursor, Cursor.getLocWithOffset(strlen("test"))));
 }
 
+TEST_F(RefactoringActionRulesTest, EditorCommandBinding) {
+  std::vector<std::unique_ptr<RefactoringAction>> Actions =
+      createRefactoringActions();
+  for (auto &Action : Actions) {
+    if (Action->getCommand() == "extract") {
+      std::vector<std::unique_ptr<RefactoringActionRule>> Rules =
+          Action->createActiveActionRules();
+      ASSERT_FALSE(Rules.empty());
+      const EditorCommand *Cmd = Rules[0]->getEditorCommand();
+      ASSERT_TRUE(Cmd);
+      EXPECT_EQ(Cmd->getName(), "ExtractFunction");
+      EXPECT_EQ(Cmd->getTitle(), "Extract Function");
+      return;
+    }
+  }
+  // Never found 'extract'?
+  ASSERT_TRUE(false);
+}
+
 } // end anonymous namespace
Index: lib/Tooling/Refactoring/Extract.cpp
===================================================================
--- lib/Tooling/Refactoring/Extract.cpp
+++ lib/Tooling/Refactoring/Extract.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
 #include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactoring/EditorCommands.h"
 #include "clang/Tooling/Refactoring/RefactoringAction.h"
 #include "clang/Tooling/Refactoring/RefactoringActionRules.h"
 #include "clang/Tooling/Refactoring/RefactoringOptions.h"
@@ -214,9 +215,10 @@
   /// action.
   RefactoringActionRules createActionRules() const override {
     RefactoringActionRules Rules;
-    Rules.push_back(createRefactoringActionRule<ExtractFunction>(
-        ExtractableCodeSelectionRequirement(),
-        OptionRequirement<DeclNameOption>()));
+    Rules.push_back(EditorCommand::ExtractFunction().bind(
+        createRefactoringActionRule<ExtractFunction>(
+            ExtractableCodeSelectionRequirement(),
+            OptionRequirement<DeclNameOption>())));
     return Rules;
   }
 };
Index: lib/Tooling/Refactoring/EditorCommand.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Refactoring/EditorCommand.cpp
@@ -0,0 +1,62 @@
+//===--- EditorCommand.cpp - refactoring editor commands ------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/EditorCommands.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRule.h"
+
+namespace clang {
+namespace tooling {
+
+#define REFACTORING_EDITOR_COMMAND(Name, Title)                                \
+  EditorCommand &EditorCommand::Name() {                                       \
+    static std::unique_ptr<EditorCommand> Command(                             \
+        new EditorCommand(#Name, Title));                                      \
+    return *Command;                                                           \
+  }
+#include "clang/Tooling/Refactoring/EditorCommandRegistry.def"
+
+class BoundEditorRefactoringActionRule : public RefactoringActionRule {
+public:
+  BoundEditorRefactoringActionRule(std::unique_ptr<RefactoringActionRule> Rule,
+                                   const EditorCommand &Command)
+      : Rule(std::move(Rule)), Command(Command) {}
+
+  void invoke(RefactoringResultConsumer &Consumer,
+              RefactoringRuleContext &Context) override {
+    Rule->invoke(Consumer, Context);
+  }
+
+  bool hasSelectionRequirement() override {
+    return Rule->hasSelectionRequirement();
+  }
+
+  void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) override {
+    return Rule->visitRefactoringOptions(Visitor);
+  }
+
+  const EditorCommand *getEditorCommand() override { return &Command; }
+
+private:
+  std::unique_ptr<RefactoringActionRule> Rule;
+  const EditorCommand &Command;
+};
+
+std::unique_ptr<RefactoringActionRule>
+EditorCommand::bind(std::unique_ptr<RefactoringActionRule> Rule) {
+  if (IsBound) {
+    assert(false && "one editor command is bound to multiple rules");
+    return Rule;
+  }
+  IsBound = true;
+  return llvm::make_unique<BoundEditorRefactoringActionRule>(std::move(Rule),
+                                                             *this);
+}
+
+} // end namespace tooling
+} // end namespace clang
Index: lib/Tooling/Refactoring/CMakeLists.txt
===================================================================
--- lib/Tooling/Refactoring/CMakeLists.txt
+++ lib/Tooling/Refactoring/CMakeLists.txt
@@ -4,6 +4,7 @@
   ASTSelection.cpp
   ASTSelectionRequirements.cpp
   AtomicChange.cpp
+  EditorCommand.cpp
   Extract.cpp
   RefactoringActions.cpp
   Rename/RenamingAction.cpp
Index: include/clang/module.modulemap
===================================================================
--- include/clang/module.modulemap
+++ include/clang/module.modulemap
@@ -147,6 +147,7 @@
   // matchers (and thus the AST), which clang-format should not have.
   exclude header "Tooling/RefactoringCallbacks.h"
 
+  textual header "Tooling/Refactoring/EditorCommandRegistry.def"
   textual header "Tooling/Refactoring/RefactoringActionRegistry.def"
 }
 
Index: include/clang/Tooling/Refactoring/RefactoringActionRule.h
===================================================================
--- include/clang/Tooling/Refactoring/RefactoringActionRule.h
+++ include/clang/Tooling/Refactoring/RefactoringActionRule.h
@@ -16,6 +16,7 @@
 namespace clang {
 namespace tooling {
 
+class EditorCommand;
 class RefactoringOptionVisitor;
 class RefactoringResultConsumer;
 class RefactoringRuleContext;
@@ -52,6 +53,9 @@
   /// requirements that use options, the options from the first requirement
   /// are visited before the options in the second requirement.
   virtual void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) = 0;
+
+  /// Returns the editor command that's was bound to this rule.
+  virtual const EditorCommand *getEditorCommand() { return nullptr; }
 };
 
 } // end namespace tooling
Index: include/clang/Tooling/Refactoring/EditorCommands.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/Refactoring/EditorCommands.h
@@ -0,0 +1,58 @@
+//===--- EditorCommands.h - Clang refactoring library ---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_EDITOR_COMMANDS_H
+#define LLVM_CLANG_TOOLING_REFACTOR_EDITOR_COMMANDS_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+
+namespace clang {
+namespace tooling {
+
+class RefactoringActionRule;
+
+/// Editor commands allow refactoring rules to be bound to commands that can
+/// be used from an editor that integrates with Clang's refactoring engine.
+///
+/// Once an editor command is defined in the EditorCommandRegistry.def file,
+/// it's accessible in the following manner:
+///
+/// \code
+/// EditorCommand::Name().bind(createRefactoringActionRule<...>(...))
+/// \code
+class EditorCommand {
+public:
+  StringRef getName() const { return Name; }
+
+  StringRef getTitle() const { return Title; }
+
+  /// Binds the editor command to a particular refactoring action rule.
+  ///
+  /// Once the editor command is bound, it can't be unbound or rebound to
+  /// another rule.
+  std::unique_ptr<RefactoringActionRule>
+  bind(std::unique_ptr<RefactoringActionRule> Rule);
+
+#define REFACTORING_EDITOR_COMMAND(Name, Title) static EditorCommand &Name();
+#include "clang/Tooling/Refactoring/EditorCommandRegistry.def"
+
+private:
+  EditorCommand(StringRef Name, StringRef Title) : Name(Name), Title(Title) {}
+
+  StringRef Name;
+  StringRef Title;
+  bool IsBound = false;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_EDITOR_COMMANDS_H
Index: include/clang/Tooling/Refactoring/EditorCommandRegistry.def
===================================================================
--- /dev/null
+++ include/clang/Tooling/Refactoring/EditorCommandRegistry.def
@@ -0,0 +1,7 @@
+#ifndef REFACTORING_EDITOR_COMMAND
+#define REFACTORING_EDITOR_COMMAND(Name, Title)
+#endif
+
+REFACTORING_EDITOR_COMMAND(ExtractFunction, "Extract Function")
+
+#undef REFACTORING_EDITOR_COMMAND
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to