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 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 + +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 + bind(std::unique_ptr 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/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/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: 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: 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 Command( \ + new EditorCommand(#Name, Title)); \ + return *Command; \ + } +#include "clang/Tooling/Refactoring/EditorCommandRegistry.def" + +class BoundEditorRefactoringActionRule : public RefactoringActionRule { +public: + BoundEditorRefactoringActionRule(std::unique_ptr 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 Rule; + const EditorCommand &Command; +}; + +std::unique_ptr +EditorCommand::bind(std::unique_ptr Rule) { + if (IsBound) { + assert(false && "one editor command is bound to multiple rules"); + return Rule; + } + IsBound = true; + return llvm::make_unique(std::move(Rule), + *this); +} + +} // end namespace tooling +} // end namespace clang 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( - ExtractableCodeSelectionRequirement(), - OptionRequirement())); + Rules.push_back(EditorCommand::ExtractFunction().bind( + createRefactoringActionRule( + ExtractableCodeSelectionRequirement(), + OptionRequirement()))); return Rules; } }; 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> Actions = + createRefactoringActions(); + for (auto &Action : Actions) { + if (Action->getCommand() == "extract") { + std::vector> 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