diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java index 4d25490ff766..679ec9a7d9cb 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java @@ -23,6 +23,7 @@ import com.intellij.util.ObjectUtils; import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FList; +import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.java.generate.exception.GenerateCodeException; @@ -54,8 +55,7 @@ public class JavaGenerateMemberCompletionContributor { fileText.substring(modifierList.getTextRange().getStartOffset(), parameters.getOffset()))); } suggestGeneratedMethods(result, position, modifierList); - } else if (psiElement(PsiIdentifier.class) - .withParents(PsiJavaCodeReferenceElement.class, PsiAnnotation.class, PsiModifierList.class, PsiClass.class).accepts(position)) { + } else if (isTypingAnnotationForNewMember(position)) { PsiAnnotation annotation = ObjectUtils.assertNotNull(PsiTreeUtil.getParentOfType(position, PsiAnnotation.class)); int annoStart = annotation.getTextRange().getStartOffset(); @@ -69,10 +69,33 @@ public class JavaGenerateMemberCompletionContributor { } + private static boolean isTypingAnnotationForNewMember(PsiElement position) { + if (psiElement(PsiIdentifier.class) + .withParents(PsiJavaCodeReferenceElement.class, PsiAnnotation.class, PsiModifierList.class).accepts(position)) { + PsiElement parent = Objects.requireNonNull(PsiTreeUtil.getParentOfType(position, PsiModifierList.class)).getParent(); + if (parent instanceof PsiClass) { + return true; + } + + if (parent instanceof PsiMethod || parent instanceof PsiField) { + PsiAnnotation anno = Objects.requireNonNull(PsiTreeUtil.getParentOfType(position, PsiAnnotation.class)); + return anno.getTextRange().getStartOffset() == parent.getTextRange().getStartOffset() && isFollowedByEol(anno); + } + } + return false; + } + + private static boolean isFollowedByEol(PsiAnnotation anno) { + CharSequence fileText = anno.getContainingFile().getViewProvider().getContents(); + int afterAnno = CharArrayUtil.shiftForward(fileText, anno.getTextRange().getEndOffset(), " \t"); + return fileText.length() > afterAnno && fileText.charAt(afterAnno) == '\n'; + } + @NotNull private static LookupElementBuilder itemWithOverrideImplementDialog(int annoStart) { return LookupElementBuilder.create("Override/Implement methods...").withInsertHandler((context, item) -> { context.getDocument().deleteString(annoStart, context.getTailOffset()); + context.commitDocument(); context.setAddCompletionChar(false); context.setLaterRunnable(() -> { new OverrideMethodsHandler().invoke(context.getProject(), context.getEditor(), context.getFile()); diff --git a/java/java-tests/testData/codeInsight/completion/normal/SuggestToOverrideMethodsWhenTypingOverrideAnnotationBeforeMethod.java b/java/java-tests/testData/codeInsight/completion/normal/SuggestToOverrideMethodsWhenTypingOverrideAnnotationBeforeMethod.java new file mode 100644 index 000000000000..5c6ed74842d0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SuggestToOverrideMethodsWhenTypingOverrideAnnotationBeforeMethod.java @@ -0,0 +1,10 @@ +interface Foo { + void run(T t, int myInt); + void run2(T t, int myInt); +} + +class A implements Foo { + @Overr + + void foo() {} +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/SuggestToOverrideMethodsWhenTypingOverrideAnnotationBeforeMethod_after.java b/java/java-tests/testData/codeInsight/completion/normal/SuggestToOverrideMethodsWhenTypingOverrideAnnotationBeforeMethod_after.java new file mode 100644 index 000000000000..6872d842086a --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SuggestToOverrideMethodsWhenTypingOverrideAnnotationBeforeMethod_after.java @@ -0,0 +1,47 @@ +interface Foo { + void run(T t, int myInt); + void run2(T t, int myInt); +} + +class A implements Foo { + public A() { + super(); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + @Override + public String toString() { + return super.toString(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + } + + @Override + public void run(String s, int myInt) { + + } + + @Override + public void run2(String s, int myInt) { + + } + + void foo() {} +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy index 6c6bddbbb2f7..193394dc5d79 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy @@ -578,7 +578,7 @@ interface TxANotAnno {} } void testPreferAnnotationsToInterfaceKeyword() { - checkPreferredItems 0, 'Deprecated', 'Override' + checkPreferredItems 0, 'Override/Implement methods...', 'Deprecated', 'Override' } void testPreferThrownExceptionsInCatch() { diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy index 2f139171ff64..d24eddebc205 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy @@ -1519,6 +1519,13 @@ class XInternalError {} checkResult() } + void testSuggestToOverrideMethodsWhenTypingOverrideAnnotationBeforeMethod() { + configure() + myFixture.assertPreferredCompletionItems 0, 'Override/Implement methods...', 'Override' + myFixture.type('\n') + checkResult() + } + void testStrikeOutDeprecatedSuperMethods() { configure() myFixture.assertPreferredCompletionItems 0, 'void foo1', 'void foo2'