diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHandler.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHandler.java index eb96eef575b1..b8f5a477097d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateEqualsHandler.java @@ -91,13 +91,7 @@ public class GenerateEqualsHandler extends GenerateMembersHandlerBase { return null; } } - boolean hasNonStaticFields = false; - for (PsiField field : aClass.getFields()) { - if (!field.hasModifierProperty(PsiModifier.STATIC)) { - hasNonStaticFields = true; - break; - } - } + boolean hasNonStaticFields = hasNonStaticFields(aClass); if (!hasNonStaticFields) { HintManager.getInstance().showErrorHint(editor, "No fields to include in equals/hashCode have been found"); return null; @@ -113,6 +107,20 @@ public class GenerateEqualsHandler extends GenerateMembersHandlerBase { return DUMMY_RESULT; } + private static boolean hasNonStaticFields(PsiClass aClass) { + for (PsiField field : aClass.getFields()) { + if (!field.hasModifierProperty(PsiModifier.STATIC)) { + return true; + } + } + return false; + } + + @Override + protected boolean hasMembers(@NotNull PsiClass aClass) { + return hasNonStaticFields(aClass); + } + @Override @NotNull protected List generateMemberPrototypes(PsiClass aClass, ClassMember[] originalMembers) throws IncorrectOperationException { diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateGetterSetterHandlerBase.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateGetterSetterHandlerBase.java index 791ea0c8e959..8ed38446b21c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateGetterSetterHandlerBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateGetterSetterHandlerBase.java @@ -74,6 +74,11 @@ public abstract class GenerateGetterSetterHandlerBase extends GenerateMembersHan super(chooserTitle); } + @Override + protected boolean hasMembers(@NotNull PsiClass aClass) { + return !GenerateAccessorProviderRegistrar.getEncapsulatableClassMembers(aClass).isEmpty(); + } + @Override protected String getHelpId() { return "Getter and Setter Templates Dialog"; diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersHandlerBase.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersHandlerBase.java index 90eb770891c0..65c4eb3ed3cb 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersHandlerBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersHandlerBase.java @@ -23,6 +23,8 @@ import com.intellij.codeInsight.template.TemplateEditingAdapter; import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInspection.ex.GlobalInspectionContextBase; import com.intellij.ide.util.MemberChooser; +import com.intellij.lang.ContextAwareActionHandler; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; @@ -47,7 +49,7 @@ import javax.swing.*; import java.util.ArrayList; import java.util.List; -public abstract class GenerateMembersHandlerBase implements CodeInsightActionHandler { +public abstract class GenerateMembersHandlerBase implements CodeInsightActionHandler, ContextAwareActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateMembersHandlerBase"); private final String myChooserTitle; @@ -57,6 +59,16 @@ public abstract class GenerateMembersHandlerBase implements CodeInsightActionHan myChooserTitle = chooserTitle; } + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + final PsiClass aClass = OverrideImplementUtil.getContextClass(file.getProject(), editor, file, false); + return aClass != null && hasMembers(aClass); + } + + protected boolean hasMembers(@NotNull PsiClass aClass) { + return true; + } + @Override public final void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file) { if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return; diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/JavaImplementMethodsHandler.java b/java/java-impl/src/com/intellij/codeInsight/generation/JavaImplementMethodsHandler.java index 8723c66aa37e..aaaf61737a05 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/JavaImplementMethodsHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/JavaImplementMethodsHandler.java @@ -16,7 +16,9 @@ package com.intellij.codeInsight.generation; import com.intellij.codeInsight.hint.HintManager; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.LanguageCodeInsightActionHandler; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; @@ -29,15 +31,14 @@ import org.jetbrains.annotations.NotNull; /** * @author yole */ -public class JavaImplementMethodsHandler implements LanguageCodeInsightActionHandler { +public class JavaImplementMethodsHandler implements ContextAwareActionHandler, LanguageCodeInsightActionHandler { @Override public boolean isValidFor(final Editor editor, final PsiFile file) { if (!(file instanceof PsiJavaFile) && !(file instanceof PsiCodeFragment)) { return false; } - PsiClass aClass = OverrideImplementUtil.getContextClass(file.getProject(), editor, file, PsiUtil.isLanguageLevel8OrHigher(file)); - return aClass != null && !OverrideImplementUtil.getMethodSignaturesToImplement(aClass).isEmpty(); + return OverrideImplementUtil.getContextClass(file.getProject(), editor, file, PsiUtil.isLanguageLevel8OrHigher(file)) != null; } @Override @@ -57,4 +58,10 @@ public class JavaImplementMethodsHandler implements LanguageCodeInsightActionHan public boolean startInWriteAction() { return false; } + + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + PsiClass aClass = OverrideImplementUtil.getContextClass(file.getProject(), editor, file, PsiUtil.isLanguageLevel8OrHigher(file)); + return aClass != null && !OverrideImplementUtil.getMethodSignaturesToImplement(aClass).isEmpty(); + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/JavaOverrideMethodsHandler.java b/java/java-impl/src/com/intellij/codeInsight/generation/JavaOverrideMethodsHandler.java index 4c6185aee0b3..d7aefabfd00f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/JavaOverrideMethodsHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/JavaOverrideMethodsHandler.java @@ -16,7 +16,9 @@ package com.intellij.codeInsight.generation; import com.intellij.codeInsight.hint.HintManager; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.LanguageCodeInsightActionHandler; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; @@ -28,15 +30,14 @@ import org.jetbrains.annotations.NotNull; /** * @author yole */ -public class JavaOverrideMethodsHandler implements LanguageCodeInsightActionHandler { +public class JavaOverrideMethodsHandler implements ContextAwareActionHandler, LanguageCodeInsightActionHandler { @Override public boolean isValidFor(final Editor editor, final PsiFile file) { if (!(file instanceof PsiJavaFile) && !(file instanceof PsiCodeFragment)) { return false; } - PsiClass aClass = OverrideImplementUtil.getContextClass(file.getProject(), editor, file, true); - return aClass != null && !OverrideImplementUtil.getMethodSignaturesToOverride(aClass).isEmpty(); + return OverrideImplementUtil.getContextClass(file.getProject(), editor, file, true) != null; } @Override @@ -55,4 +56,10 @@ public class JavaOverrideMethodsHandler implements LanguageCodeInsightActionHand public boolean startInWriteAction() { return false; } + + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + PsiClass aClass = OverrideImplementUtil.getContextClass(file.getProject(), editor, file, true); + return aClass != null && !OverrideImplementUtil.getMethodSignaturesToOverride(aClass).isEmpty(); + } } diff --git a/java/java-impl/src/com/intellij/refactoring/IntroduceHandlerBase.java b/java/java-impl/src/com/intellij/refactoring/IntroduceHandlerBase.java index 6eef977c6566..d452f465d0dd 100644 --- a/java/java-impl/src/com/intellij/refactoring/IntroduceHandlerBase.java +++ b/java/java-impl/src/com/intellij/refactoring/IntroduceHandlerBase.java @@ -15,6 +15,7 @@ */ package com.intellij.refactoring; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.PlatformDataKeys; @@ -23,6 +24,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.extractMethod.ExtractMethodHandler; import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly; @@ -30,9 +32,15 @@ import org.jetbrains.annotations.TestOnly; /** * @author dsl */ -public abstract class IntroduceHandlerBase implements RefactoringActionHandler { +public abstract class IntroduceHandlerBase implements RefactoringActionHandler, ContextAwareActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.IntroduceHandlerBase"); + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + final PsiElement[] elements = ExtractMethodHandler.getElements(file.getProject(), editor, file); + return elements != null && elements.length > 0; + } + public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { LOG.assertTrue(elements.length >= 1 && elements[0] instanceof PsiExpression, "incorrect invoke() parameters"); final PsiElement tempExpr = elements[0]; diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodHandler.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodHandler.java index c9a44d9eb6bc..3e42023d392a 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodHandler.java @@ -18,6 +18,7 @@ package com.intellij.refactoring.extractMethod; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; import com.intellij.codeInsight.highlighting.HighlightManager; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.LangDataKeys; @@ -56,7 +57,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -public class ExtractMethodHandler implements RefactoringActionHandler { +public class ExtractMethodHandler implements RefactoringActionHandler, ContextAwareActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.extractMethod.ExtractMethodHandler"); public static final String REFACTORING_NAME = RefactoringBundle.message("extract.method.title"); @@ -103,10 +104,21 @@ public class ExtractMethodHandler implements RefactoringActionHandler { } } + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + callback.pass(getElements(project, editor, file)); + } + + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + final PsiElement[] elements = getElements(file.getProject(), editor, file); + return elements != null && elements.length > 0; + } + + public static PsiElement[] getElements(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { int startOffset = editor.getSelectionModel().getSelectionStart(); int endOffset = editor.getSelectionModel().getSelectionEnd(); - PsiDocumentManager.getInstance(project).commitAllDocuments(); PsiElement[] elements; PsiExpression expr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset); @@ -125,7 +137,7 @@ public class ExtractMethodHandler implements RefactoringActionHandler { } } } - callback.pass(elements); + return elements; } private static void invokeOnElements(final Project project, final Editor editor, PsiFile file, PsiElement[] elements) { diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java index 070a519dcdc5..d6a3ac5da79e 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java @@ -20,6 +20,7 @@ */ package com.intellij.refactoring.extractMethodObject; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; @@ -42,7 +43,7 @@ import com.intellij.refactoring.util.duplicates.DuplicatesImpl; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; -public class ExtractMethodObjectHandler implements RefactoringActionHandler { +public class ExtractMethodObjectHandler implements RefactoringActionHandler, ContextAwareActionHandler { private static final Logger LOG = Logger.getInstance("#" + ExtractMethodObjectHandler.class.getName()); public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, final DataContext dataContext) { @@ -53,6 +54,12 @@ public class ExtractMethodObjectHandler implements RefactoringActionHandler { }); } + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + final PsiElement[] elements = ExtractMethodHandler.getElements(file.getProject(), editor, file); + return elements != null && elements.length > 0; + } + private static void invokeOnElements(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file, diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectHandler.java index dcf6f2b893dc..d0e70f512e5e 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectHandler.java @@ -16,10 +16,9 @@ package com.intellij.refactoring.introduceparameterobject; import com.intellij.ide.util.SuperMethodWarningUtil; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; @@ -34,12 +33,33 @@ import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.util.CommonRefactoringUtil; import org.jetbrains.annotations.NotNull; -public class IntroduceParameterObjectHandler implements RefactoringActionHandler { +public class IntroduceParameterObjectHandler implements RefactoringActionHandler, ContextAwareActionHandler { private static final String REFACTORING_NAME = RefactorJBundle.message("introduce.parameter.object"); + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + final PsiMethod selectedMethod = getSelectedMethod(editor, file, dataContext); + if (selectedMethod != null) { + final PsiMethod[] deepestSuperMethods = selectedMethod.findDeepestSuperMethods(); + return deepestSuperMethods.length > 0 || getErrorMessage(selectedMethod) == null; + } + return false; + } + public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { final ScrollingModel scrollingModel = editor.getScrollingModel(); scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE); + PsiMethod selectedMethod = getSelectedMethod(editor, file, dataContext); + if (selectedMethod == null) { + final String message = RefactorJBundle.message("cannot.perform.the.refactoring") + + RefactorJBundle.message("the.caret.should.be.positioned.at.the.name.of.the.method.to.be.refactored"); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.IntroduceParameterObject); + return; + } + invoke(project, selectedMethod, editor); + } + + private static PsiMethod getSelectedMethod(Editor editor, PsiFile file, DataContext dataContext) { final PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext); PsiMethod selectedMethod = null; if (element instanceof PsiMethod) { @@ -63,13 +83,7 @@ public class IntroduceParameterObjectHandler implements RefactoringActionHandler } } } - if (selectedMethod == null) { - final String message = RefactorJBundle.message("cannot.perform.the.refactoring") + - RefactorJBundle.message("the.caret.should.be.positioned.at.the.name.of.the.method.to.be.refactored"); - CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.IntroduceParameterObject); - return; - } - invoke(project, selectedMethod, editor); + return selectedMethod; } public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { @@ -87,20 +101,26 @@ public class IntroduceParameterObjectHandler implements RefactoringActionHandler private static void invoke(final Project project, final PsiMethod selectedMethod, Editor editor) { PsiMethod newMethod = SuperMethodWarningUtil.checkSuperMethod(selectedMethod, RefactoringBundle.message("to.refactor")); if (newMethod == null) return; - if (!CommonRefactoringUtil.checkReadOnlyStatus(project, newMethod)) return; - - final PsiParameter[] parameters = newMethod.getParameterList().getParameters(); - if (parameters.length == 0) { - final String message = - RefactorJBundle.message("cannot.perform.the.refactoring") + RefactorJBundle.message("method.selected.has.no.parameters"); + final String message = getErrorMessage(newMethod); + if (message != null) { CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.IntroduceParameterObject); return; } - if (newMethod instanceof PsiCompiledElement) { - CommonRefactoringUtil.showErrorHint(project, editor, RefactorJBundle.message("cannot.perform.the.refactoring") + RefactorJBundle.message( - "the.selected.method.cannot.be.wrapped.because.it.is.defined.in.a.non.project.class"), REFACTORING_NAME, HelpID.IntroduceParameterObject); - return; - } + if (!CommonRefactoringUtil.checkReadOnlyStatus(project, newMethod)) return; + new IntroduceParameterObjectDialog(newMethod).show(); } + + private static String getErrorMessage(PsiMethod newMethod) { + final PsiParameter[] parameters = newMethod.getParameterList().getParameters(); + if (parameters.length == 0) { + return RefactorJBundle.message("cannot.perform.the.refactoring") + + RefactorJBundle.message("method.selected.has.no.parameters"); + } + if (newMethod instanceof PsiCompiledElement) { + return RefactorJBundle.message("cannot.perform.the.refactoring") + + RefactorJBundle.message("the.selected.method.cannot.be.wrapped.because.it.is.defined.in.a.non.project.class"); + } + return null; + } } diff --git a/java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesHandler.java b/java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesHandler.java index 11568aedf84f..a6d97af3d961 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesHandler.java @@ -20,6 +20,7 @@ import com.intellij.analysis.AnalysisUIOptions; import com.intellij.analysis.BaseAnalysisActionDialog; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; @@ -55,10 +56,16 @@ import java.util.*; /** * @author dsl */ -public class MethodDuplicatesHandler implements RefactoringActionHandler { +public class MethodDuplicatesHandler implements RefactoringActionHandler, ContextAwareActionHandler { public static final String REFACTORING_NAME = RefactoringBundle.message("replace.method.code.duplicates.title"); private static final Logger LOG = Logger.getInstance("#" + MethodDuplicatesHandler.class.getName()); + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + return getCannotRefactorMessage(PsiTreeUtil.getParentOfType(element, PsiMember.class)) == null; + } + @Override public void invoke(@NotNull final Project project, final Editor editor, PsiFile file, DataContext dataContext) { final int offset = editor.getCaretModel().getOffset(); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplement15Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplement15Test.java index 9bd0d80724d2..f0c40c723f11 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplement15Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplement15Test.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.generation.OverrideImplementUtil; import com.intellij.codeInsight.generation.PsiMethodMember; import com.intellij.codeInsight.intention.impl.ImplementAbstractMethodHandler; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.actionSystem.impl.SimpleDataContext; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettings; @@ -29,6 +30,7 @@ import com.intellij.psi.util.MethodSignature; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.testFramework.LightCodeInsightTestCase; +import com.intellij.testFramework.MapDataContext; import com.intellij.util.FunctionUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; @@ -134,7 +136,9 @@ public class OverrideImplement15Test extends LightCodeInsightTestCase { PsiElement context = getFile().findElementAt(offset); final PsiClass aClass = PsiTreeUtil.getParentOfType(context, PsiClass.class); assertTrue(aClass != null && aClass.isAnnotationType()); - assertFalse(new JavaOverrideMethodsHandler().isValidFor(getEditor(), getFile())); + final JavaOverrideMethodsHandler handler = new JavaOverrideMethodsHandler(); + assertTrue(handler.isValidFor(getEditor(), getFile())); + assertFalse(handler.isAvailableForQuickList(getEditor(), getFile(), new MapDataContext())); } private void doTest(boolean copyJavadoc) { doTest(copyJavadoc, null); } diff --git a/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java b/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java index 742665350e31..8a74db72ed52 100644 --- a/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java +++ b/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java @@ -18,8 +18,10 @@ package com.intellij.codeInsight.generation.actions; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.actions.CodeInsightAction; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; @@ -34,6 +36,19 @@ public class BaseGenerateAction extends CodeInsightAction implements GenerateAct myHandler = handler; } + @Override + protected void update(@NotNull Presentation presentation, + @NotNull Project project, + @NotNull Editor editor, + @NotNull PsiFile file, + @NotNull DataContext dataContext, + @Nullable String actionPlace) { + super.update(presentation, project, editor, file, dataContext, actionPlace); + if (myHandler instanceof ContextAwareActionHandler && presentation.isEnabled()) { + presentation.setEnabled(((ContextAwareActionHandler)myHandler).isAvailableForQuickList(editor, file, dataContext)); + } + } + @Nullable public AnAction createEditTemplateAction(DataContext dataContext) { return null; diff --git a/platform/lang-api/src/com/intellij/codeInsight/CodeInsightActionHandler.java b/platform/lang-api/src/com/intellij/codeInsight/CodeInsightActionHandler.java index 70ac0a1b4a42..0483265f2d02 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/CodeInsightActionHandler.java +++ b/platform/lang-api/src/com/intellij/codeInsight/CodeInsightActionHandler.java @@ -16,11 +16,15 @@ package com.intellij.codeInsight; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; +/** + * See {@link ContextAwareActionHandler} to hide action from popups but allow access by shortcut, main menu or find. + */ public interface CodeInsightActionHandler { void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file); diff --git a/platform/lang-api/src/com/intellij/codeInsight/actions/CodeInsightAction.java b/platform/lang-api/src/com/intellij/codeInsight/actions/CodeInsightAction.java index 6496a1ddd74b..5906feadc935 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/actions/CodeInsightAction.java +++ b/platform/lang-api/src/com/intellij/codeInsight/actions/CodeInsightAction.java @@ -82,7 +82,8 @@ public abstract class CodeInsightAction extends AnAction { return; } - Editor editor = getEditor(e.getDataContext(), project); + final DataContext dataContext = e.getDataContext(); + Editor editor = getEditor(dataContext, project); if (editor == null) { presentation.setEnabled(false); return; @@ -94,13 +95,18 @@ public abstract class CodeInsightAction extends AnAction { return; } - update(presentation, project, editor, file); + update(presentation, project, editor, file, dataContext, e.getPlace()); } protected void update(@NotNull Presentation presentation, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { presentation.setEnabled(isValidForFile(project, editor, file)); } + protected void update(@NotNull Presentation presentation, @NotNull Project project, + @NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext, @Nullable String actionPlace) { + update(presentation, project, editor, file); + } + protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { return true; } diff --git a/platform/lang-api/src/com/intellij/lang/ContextAwareActionHandler.java b/platform/lang-api/src/com/intellij/lang/ContextAwareActionHandler.java new file mode 100644 index 000000000000..8a5a6f659532 --- /dev/null +++ b/platform/lang-api/src/com/intellij/lang/ContextAwareActionHandler.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.lang; + +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +public interface ContextAwareActionHandler { + /** + * Handlers could provide useful hints when they are actually not available, e.g. + * 'No methods to implement', 'Selected block should represent ...', 'Caret should be positioned at the name of ...', etc. + * At the same time, when the action is invoked through refactorings quick list popup, + * generate popup or in another manner but not through main menu (shortcut or find action are treated the same), + * it's better to hide the action: it would pollute menu with one more choice but can't do anything. + * + * @return It's assumed that handler is valid for file. Still should be lightweight, because is invoked from action update. + * false - if action won't proceed + */ + boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext); +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java index 3b716a11cb11..d24f34839977 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java @@ -17,10 +17,13 @@ package com.intellij.codeInsight.generation.actions; import com.intellij.codeInsight.actions.BaseCodeInsightAction; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.Language; import com.intellij.lang.LanguageCodeInsightActionHandler; import com.intellij.lang.LanguageExtension; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -49,13 +52,17 @@ public abstract class PresentableActionHandlerBasedAction extends BaseCodeInsigh } @Override - protected void update(@NotNull Presentation presentation, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { + protected void update(@NotNull Presentation presentation, @NotNull Project project, + @NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext, @Nullable String actionPlace) { // avoid evaluating isValidFor several times unnecessary LanguageCodeInsightActionHandler handler = getValidHandler(editor, file); presentation.setEnabled(handler != null); + if (handler instanceof ContextAwareActionHandler && !ActionPlaces.isMainMenuOrActionSearch(actionPlace)) { + presentation.setVisible(((ContextAwareActionHandler)handler).isAvailableForQuickList(editor, file, dataContext)); + } - if (handler instanceof PresentableLanguageCodeInsightActionHandler) { + if (presentation.isVisible() && handler instanceof PresentableLanguageCodeInsightActionHandler) { ((PresentableLanguageCodeInsightActionHandler)handler).update(editor, file, presentation); } } diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java index 1ae15ab02421..8c5f3458b5d8 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java @@ -22,6 +22,7 @@ import com.intellij.codeInsight.lookup.LookupEx; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.ide.IdeEventQueue; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.command.CommandProcessor; @@ -64,7 +65,18 @@ public abstract class BaseRefactoringAction extends AnAction { } protected boolean hasAvailableHandler(@NotNull DataContext dataContext) { - return getHandler(dataContext) != null; + final RefactoringActionHandler handler = getHandler(dataContext); + if (handler != null) { + if (handler instanceof ContextAwareActionHandler) { + final Editor editor = CommonDataKeys.EDITOR.getData(dataContext); + final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext); + if (editor != null && file != null && !((ContextAwareActionHandler)handler).isAvailableForQuickList(editor, file, dataContext)) { + return false; + } + } + return true; + } + return false; } @Nullable diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java index f07419aa9be1..6a2a3a68cba3 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java @@ -15,6 +15,7 @@ */ package com.intellij.refactoring.actions; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.Language; import com.intellij.lang.LanguageRefactoringSupport; import com.intellij.lang.refactoring.RefactoringSupportProvider; @@ -90,7 +91,14 @@ public class ChangeSignatureAction extends BasePlatformRefactoringAction { @Nullable @Override protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider, final PsiElement element) { - return new RefactoringActionHandler() { + abstract class ContextAwareChangeSignatureHandler implements RefactoringActionHandler, ContextAwareActionHandler {} + + return new ContextAwareChangeSignatureHandler() { + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return findTargetMember(element) != null; + } + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);