[java-completion] IDEA-363488 Completion for similar methods: propose more often

- restart if there are not matched items, but tags can be used

GitOrigin-RevId: 9620630c72759450e0c4b2c375da4784bee8f088
This commit is contained in:
Mikhail Pyltsin
2024-11-19 10:44:09 +00:00
committed by intellij-monorepo-bot
parent 691b94da97
commit 732018d4f2
3 changed files with 99 additions and 45 deletions
@@ -482,6 +482,13 @@ public final class JavaCompletionContributor extends CompletionContributor imple
}
else {
refSuggestions = completeReference(parameters, parentRef, session, expectedInfos, matcher::prefixMatches);
if (refSuggestions
.stream()
.map(lookupElement -> MethodTags.collectTags(lookupElement, matcher::prefixMatches))
.anyMatch(t -> t != null && !t.isEmpty())) {
//it is possible to propose some tags, let's try to do this
_result.restartCompletionWhenNothingMatches();
}
}
}
List<LookupElement> filtered = filterReferenceSuggestions(parameters, expectedInfos, refSuggestions);
@@ -8,6 +8,7 @@ import com.intellij.codeInsight.lookup.impl.LookupCellRenderer;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.Strings;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
@@ -36,20 +37,28 @@ public final class MethodTags {
if (matcher.value(element.getLookupString())) {
return null;
}
Set<String> tags = collectTags(element, matcher);
if (tags == null) return null;
if (tags.isEmpty()) {
return null;
}
return new TagLookupElementDecorator(element, tags, prefix, completionType == CompletionType.SMART);
}
/**
* @return Set of tags, which can be used for this element, can return null if an element is not for PsiMember
*/
static @Nullable Set<String> collectTags(@NotNull LookupElement element, @NotNull Condition<? super String> matcher) {
String lookupString = element.getLookupString();
PsiElement psiElement = element.getPsiElement();
if (!(psiElement instanceof PsiMember psiMember)) {
return null;
}
PsiClass psiClass = psiMember.getContainingClass();
Set<String> tags = tags(lookupString).stream()
return tags(lookupString).stream()
.filter(t -> matcher.value(t.name()) && t.matcher().test(psiClass))
.map(t -> t.name())
.collect(Collectors.toSet());
if (tags.isEmpty()) {
return null;
}
return new TagLookupElementDecorator(element, tags, prefix, completionType == CompletionType.SMART);
}
@ApiStatus.Experimental
@@ -89,7 +98,22 @@ public final class MethodTags {
@Override
public void renderElement(@NotNull LookupElementPresentation presentation) {
super.renderElement(presentation);
if (myTags.size() == 1) {
String itemText = presentation.getItemText();
String tailText = presentation.getTailText();
if (itemText != null && (tailText == null || tailText.isBlank() || (tailText.startsWith("(") && tailText.endsWith(")")))) {
if (tailText == null) {
tailText = "";
}
String tagMessage = JavaBundle.message("java.completion.tag", myTags.size());
String fullItemText = itemText + tailText + " " + tagMessage + " ";
String allTags = Strings.join(myTags, ", ");
fullItemText += allTags;
presentation.setItemText(fullItemText);
presentation.decorateItemTextRange(new TextRange(itemText.length(), itemText.length() + tailText.length() + 1 + tagMessage.length()),
LookupElementPresentation.LookupItemDecoration.GRAY);
presentation.setTailText("");
}
else if (myTags.size() == 1) {
presentation.appendTailText(" " + JavaBundle.message("java.completion.tag", myTags.size()) + " ", true);
int startOffset = getStartOffset(presentation);
String text = myTags.iterator().next();
@@ -1837,7 +1837,7 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
myFixture.complete(CompletionType.BASIC, 2);
myFixture.checkResult("""
import foo.myClass;
class Foo extends myClass
""");
}
@@ -1865,7 +1865,7 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
void foo(Foo f) {
f.<caret>
}
}
}
""");
myFixture.completeBasic();
assertFalse(myFixture.getLookupElementStrings().contains("Inner"));
@@ -1929,7 +1929,7 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
UIUtil.dispatchAllInvocationEvents();
myFixture.checkResult("""
class Foo {int i;
public int getI() {
return this.i;
}
@@ -2836,28 +2836,28 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
public void testPackagePrivateConstantInInterface() {
myFixture.addClass("""
package com.example.x;
public interface Exposed extends PackPrivate {
int value();
}
""");
myFixture.addClass("""
package com.example.x;
interface PackPrivate {
Exposed CONSTANT = () -> 5;
}
""");
myFixture.configureByText("Main.java", """
package com.example;
import com.example.x.Exposed;
public class Main {
public static void main(String[] args) {
test(CONST<caret>);
}
private static void test(Exposed exposed) {
}
}""");
@@ -2865,14 +2865,14 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
myFixture.type('\n');
myFixture.checkResult("""
package com.example;
import com.example.x.Exposed;
public class Main {
public static void main(String[] args) {
test(Exposed.CONSTANT);
}
private static void test(Exposed exposed) {
}
}""");
@@ -2894,10 +2894,10 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
public void testEnumMapNoTypeParams() {
myFixture.configureByText("Test.java", """
import java.util.Map;
public abstract class SuperClass {
enum X {A, B, C}
void run() {
Map<X, String> map = new EnumM<caret>
}
@@ -2908,10 +2908,10 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
myFixture.checkResult("""
import java.util.EnumMap;
import java.util.Map;
public abstract class SuperClass {
enum X {A, B, C}
void run() {
Map<X, String> map = new EnumMap<>()
}
@@ -2923,9 +2923,9 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
Registry.get("java.completion.methods.use.tags").setValue(true, getTestRootDisposable());
myFixture.configureByText("Test.java", """
import java.util.HashSet;
public abstract class SuperClass {
void run() {
HashSet<Object> objects = new HashSet<>();
objects.len<caret>;
@@ -2936,9 +2936,9 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
myFixture.type('\n');
myFixture.checkResult("""
import java.util.HashSet;
public abstract class SuperClass {
void run() {
HashSet<Object> objects = new HashSet<>();
objects.size();
@@ -2952,14 +2952,14 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
Registry.get("java.completion.methods.use.tags").setValue(true, getTestRootDisposable());
myFixture.configureByText("Test.java", """
import java.util.HashSet;
public abstract class SuperClass {
void run() {
HashSet<Object> objects = new HashSet<>();
objects.len<caret>;
}
static void len(HashSet<Object> o){}
}
""");
@@ -2967,14 +2967,14 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
myFixture.type('\n');
myFixture.checkResult("""
import java.util.HashSet;
public abstract class SuperClass {
void run() {
HashSet<Object> objects = new HashSet<>();
objects.size();
}
static void len(HashSet<Object> o){}
}
""");
@@ -2985,9 +2985,9 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
Registry.get("java.completion.methods.use.tags").setValue(true, getTestRootDisposable());
myFixture.configureByText("Test.java", """
import java.util.HashSet;
public abstract class SuperClass {
void run() {
HashSet<Object> objects = new HashSet<>();
objects.le<caret>;
@@ -3001,6 +3001,29 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
assertTrue(ContainerUtil.exists(lookupElements, t -> t.getLookupString().equals("size")));
}
@NeedsIndex.Full
public void testTagAddInvocationRestart() {
Registry.get("java.completion.methods.use.tags").setValue(true, getTestRootDisposable());
myFixture.configureByText("Test.java", """
import java.util.HashSet;
public abstract class SuperClass {
void run() {
HashSet<Object> objects = new HashSet<>();
objects.<caret>;
}
}
""");
myFixture.complete(CompletionType.BASIC, 1);
myFixture.type("l");
myFixture.type("e");
myFixture.type("n");
myFixture.type("g");
myFixture.type("t");
assertTrue(ContainerUtil.exists(myFixture.getLookupElements(), t -> t.getLookupString().equals("size")));
}
@NeedsIndex.Full
public void testResolveToSubclassMethod() {
myFixture.configureByText("Test.java", """
@@ -3107,9 +3130,9 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
public void testNestedImplicitClass() {
myFixture.configureByText("Test.java", """
public static class NestedClass{
}
public static void main(String[] args) {
NestedCla<caret>
}
@@ -3117,9 +3140,9 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
myFixture.complete(CompletionType.BASIC);
myFixture.checkResult("""
public static class NestedClass{
}
public static void main(String[] args) {
NestedClass
}
@@ -3132,14 +3155,14 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
public static class Nested2ClassMore {
}
}
public void main(String[] args) {
Nested nested = new Nested();
}
public void t(Nested2ClassMo<caret> nested2) {
}
""");
myFixture.complete(CompletionType.BASIC);
@@ -3149,14 +3172,14 @@ public class NormalCompletionTest extends NormalCompletionTestCase {
public static class Nested2ClassMore {
}
}
public void main(String[] args) {
Nested nested = new Nested();
}
public void t(Nested.Nested2ClassMore nested2) {
}
"""); }