From 12db18b111e5061d6fe860f7c0933d57f90dd258 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 21 Nov 2018 18:15:47 +0100 Subject: [PATCH] java completion: don't suggest overridden members if they match by middle of the name (IDEA-195106) --- ...vaGenerateMemberCompletionContributor.java | 37 ++++++++++++++++++- .../NoOverrideWithMiddleMatchedName.java | 9 +++++ .../completion/NormalCompletionTest.groovy | 5 +++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/NoOverrideWithMiddleMatchedName.java 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 076934e773c0..90b9a69069e3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java @@ -1,6 +1,7 @@ // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInsight.completion; +import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; import com.intellij.codeInsight.generation.*; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; @@ -10,6 +11,7 @@ import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.infos.CandidateInfo; @@ -19,6 +21,7 @@ import com.intellij.ui.RowIcon; import com.intellij.util.ObjectUtils; import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.FList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.java.generate.exception.GenerateCodeException; @@ -48,14 +51,19 @@ public class JavaGenerateMemberCompletionContributor { PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(position); PsiModifierList modifierList = PsiTreeUtil.getParentOfType(prevLeaf, PsiModifierList.class); if (modifierList != null) { - result = result.withPrefixMatcher(position.getContainingFile().getText().substring(modifierList.getTextRange().getStartOffset(), parameters.getOffset())); + String fileText = position.getContainingFile().getText(); + result = result.withPrefixMatcher(new NoMiddleMatchesAfterSpace( + 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)) { PsiAnnotation annotation = ObjectUtils.assertNotNull(PsiTreeUtil.getParentOfType(position, PsiAnnotation.class)); int annoStart = annotation.getTextRange().getStartOffset(); - suggestGeneratedMethods(result.withPrefixMatcher(annotation.getText().substring(0, parameters.getOffset() - annoStart)), position, (PsiModifierList)annotation.getParent()); + suggestGeneratedMethods( + result.withPrefixMatcher(new NoMiddleMatchesAfterSpace(annotation.getText().substring(0, parameters.getOffset() - annoStart))), + position, + (PsiModifierList)annotation.getParent()); } } @@ -208,4 +216,29 @@ public class JavaGenerateMemberCompletionContributor { private static String getShortParameterName(PsiSubstitutor substitutor, PsiParameter p) { return PsiNameHelper.getShortClassName(substitutor.substitute(p.getType()).getPresentableText(false)); } + + private static class NoMiddleMatchesAfterSpace extends CamelHumpMatcher { + NoMiddleMatchesAfterSpace(String prefix) { + super(prefix); + } + + @Override + public boolean prefixMatches(@NotNull LookupElement element) { + if (!super.prefixMatches(element)) return false; + + if (!myPrefix.contains(" ")) return true; + + String signature = element.getLookupString(); + FList fragments = matchingFragments(signature); + return fragments == null || fragments.stream().noneMatch(f -> isMiddleMatch(signature, f)); + + } + + private static boolean isMiddleMatch(String signature, TextRange fragment) { + int start = fragment.getStartOffset(); + return start > 0 && + Character.isJavaIdentifierPart(signature.charAt(start)) && + Character.isJavaIdentifierPart(signature.charAt(start - 1)); + } + } } diff --git a/java/java-tests/testData/codeInsight/completion/normal/NoOverrideWithMiddleMatchedName.java b/java/java-tests/testData/codeInsight/completion/normal/NoOverrideWithMiddleMatchedName.java new file mode 100644 index 000000000000..42c44c662f24 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/NoOverrideWithMiddleMatchedName.java @@ -0,0 +1,9 @@ +class Foo extends Bar { + public Editorx +} + +class Bar { + public void removeTemporaryEditorNode() {} +} + +class Editor {} 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 4fec852a1541..1eb0f0532896 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 @@ -1675,6 +1675,11 @@ class Bar { assert 'Door' == LookupElementPresentation.renderElement(myFixture.lookup.items[0]).typeText } + void testNoOverrideWithMiddleMatchedName() { + configure() + assert !('public void removeTemporaryEditorNode' in myFixture.lookupElementStrings) + } + void testShowVarInitializers() { configure() assert LookupElementPresentation.renderElement(myFixture.lookup.items[0]).tailText == '( "x")'