diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/CompletionProposalRequestor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/CompletionProposalRequestor.java index 35e5f2f56e..9a098cf677 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/CompletionProposalRequestor.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/CompletionProposalRequestor.java @@ -16,7 +16,6 @@ import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -57,6 +56,7 @@ import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemDefaults; import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.CompletionItemLabelDetails; import org.eclipse.lsp4j.CompletionItemTag; import org.eclipse.lsp4j.InsertReplaceRange; import org.eclipse.lsp4j.InsertTextMode; @@ -69,7 +69,7 @@ public final class CompletionProposalRequestor extends CompletionRequestor { private List proposals = new ArrayList<>(); // Cache to store all the types that has been collapsed, due to off mode of argument guessing. - private Set collapsedTypes = new HashSet<>(); + private Map collapsedTypes = new HashMap<>(); private final ICompilationUnit unit; private final String uri; // URI of this.unit, used in future "resolve" requests private CompletionProposalDescriptionProvider descriptionProvider; @@ -430,6 +430,19 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { } else if (labelDetailsEnabled && $.getKind() == CompletionItemKind.Constructor && $.getLabelDetails() != null && $.getLabelDetails().getDetail() != null) { filterText = newText.concat($.getLabelDetails().getDetail()); } + if (proposal.getKind() == CompletionProposal.METHOD_REF + && collapsedTypes.containsKey(String.valueOf(proposal.getName())) && collapsedTypes.get(String.valueOf(proposal.getName())) > 1) { + if (labelDetailsEnabled) { + CompletionItemLabelDetails newLabelDetails = new CompletionItemLabelDetails(); + newLabelDetails.setDetail("(...)"); + newLabelDetails.setDescription(collapsedTypes.get(String.valueOf(proposal.getName())).toString() + " overloads"); + $.setLabelDetails(newLabelDetails); + $.setDetail(null); + } else { + $.setLabel(String.valueOf(proposal.getName()) + "(...)"); + $.setDetail(collapsedTypes.get(String.valueOf(proposal.getName())).toString() + " overloads"); + } + } if (range != null && !filterText.isEmpty()) { $.setFilterText(filterText); } else if (range != null && newText != null) { @@ -739,15 +752,20 @@ private boolean matchCase(CompletionProposal proposal) { * Check if the current completion proposal needs to be collapsed. */ private boolean needToCollapse(CompletionProposal proposal) { - if (preferenceManager.getPreferences().getGuessMethodArgumentsMode() != CompletionGuessMethodArgumentsMode.OFF) { + if (preferenceManager.getPreferences().isCollapseCompletionItemsEnabled() == false || preferenceManager.getPreferences().getGuessMethodArgumentsMode() != CompletionGuessMethodArgumentsMode.OFF) { return false; } + CompletionProposal requiredProposal = CompletionProposalUtils.getRequiredTypeProposal(proposal); + if (proposal.getKind() == CompletionProposal.METHOD_REF) { + return collapsedTypes.merge(String.valueOf(proposal.getName()), 1, Integer::sum) > 1; + } + if (requiredProposal == null) { return false; } - return !collapsedTypes.add(String.valueOf(requiredProposal.getSignature())); + return collapsedTypes.merge(String.valueOf(requiredProposal.getSignature()), 1, Integer::sum) > 1; } } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java index c1ddc6a60e..d34f9d6a79 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java @@ -398,6 +398,8 @@ public class Preferences { */ public static final String JAVA_COMPLETION_GUESS_METHOD_ARGUMENTS_KEY = "java.completion.guessMethodArguments"; + public static final String JAVA_COMPLETION_COLLAPSE_KEY = "java.completion.collapseCompletionItems"; + /** * A named preference that defines how member elements are ordered by code * actions. @@ -621,6 +623,7 @@ public class Preferences { private boolean foldingRangeEnabled; private boolean selectionRangeEnabled; private CompletionGuessMethodArgumentsMode guessMethodArguments; + private boolean collapseCompletionItems; private boolean javaFormatComments; private boolean hashCodeEqualsTemplateUseJava7Objects; @@ -877,6 +880,7 @@ public Preferences() { foldingRangeEnabled = true; selectionRangeEnabled = true; guessMethodArguments = CompletionGuessMethodArgumentsMode.INSERT_PARAMETER_NAMES; + collapseCompletionItems = false; javaFormatComments = true; hashCodeEqualsTemplateUseJava7Objects = false; hashCodeEqualsTemplateUseInstanceof = false; @@ -1080,6 +1084,9 @@ public static Preferences createFrom(Map configuration) { CompletionGuessMethodArgumentsMode.INSERT_PARAMETER_NAMES)); } + boolean collapseCompletionItemsEnabled = getBoolean(configuration, JAVA_COMPLETION_COLLAPSE_KEY, false); + prefs.setCollapseCompletionItemsEnabled(collapseCompletionItemsEnabled); + boolean hashCodeEqualsTemplateUseJava7Objects = getBoolean(configuration, JAVA_CODEGENERATION_HASHCODEEQUALS_USEJAVA7OBJECTS, false); prefs.setHashCodeEqualsTemplateUseJava7Objects(hashCodeEqualsTemplateUseJava7Objects); boolean hashCodeEqualsTemplateUseInstanceof = getBoolean(configuration, JAVA_CODEGENERATION_HASHCODEEQUALS_USEINSTANCEOF, false); @@ -1544,6 +1551,11 @@ public Preferences setGuessMethodArgumentsMode(CompletionGuessMethodArgumentsMod return this; } + public Preferences setCollapseCompletionItemsEnabled(boolean enabled) { + this.collapseCompletionItems = enabled; + return this; + } + public Preferences setJavaFormatEnabled(boolean enabled) { this.javaFormatEnabled = enabled; return this; @@ -1864,6 +1876,10 @@ public CompletionGuessMethodArgumentsMode getGuessMethodArgumentsMode() { return guessMethodArguments; } + public boolean isCollapseCompletionItemsEnabled() { + return collapseCompletionItems; + } + public boolean isHashCodeEqualsTemplateUseJava7Objects() { return hashCodeEqualsTemplateUseJava7Objects; } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java index 8a1254b009..de3d2e168b 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java @@ -4087,6 +4087,55 @@ public void testCompletion_order() throws Exception { assertTrue(list.getItems().get(2).getFilterText().startsWith("test(String x, int y, boolean z)")); } + @Test + public void testCompletion_collapse() throws Exception { + when(preferenceManager.getClientPreferences().isCompletionItemLabelDetailsSupport()).thenReturn(true); + preferenceManager.getPreferences().setCollapseCompletionItemsEnabled(true); + preferenceManager.getPreferences().setGuessMethodArgumentsMode(CompletionGuessMethodArgumentsMode.OFF); + ICompilationUnit unit = getWorkingCopy("src/org/sample/Test.java", String.join("\n", + //@formatter:off + "package org.sample", + "public class Test {", + " public void test(String x){}", + " public void test(String x, int y){}", + " public void test(String x, int y, boolean z){}", + " public static void main(String[] args) {", + " Test obj = new Test();", + " obj.test", + " }", + "}")); + //@formatter:on + CompletionList list = requestCompletions(unit, "obj.test"); + assertFalse(list.getItems().isEmpty()); + assertTrue(list.getItems().get(0).getLabelDetails().getDetail().startsWith("(...)")); + assertTrue(list.getItems().get(0).getLabelDetails().getDescription().startsWith("3 overloads")); + } + + @Test + public void testCompletion_collapse_extends() throws Exception { + when(preferenceManager.getClientPreferences().isCompletionItemLabelDetailsSupport()).thenReturn(true); + preferenceManager.getPreferences().setCollapseCompletionItemsEnabled(true); + preferenceManager.getPreferences().setGuessMethodArgumentsMode(CompletionGuessMethodArgumentsMode.OFF); + ICompilationUnit unit = getWorkingCopy("src/org/sample/Test.java", String.join("\n", + //@formatter:off + "package org.sample", + "class Test extends TestSuper {", + " public void test(String x){}", + " public static void main(String[] args) {", + " Test obj = new Test();", + " obj.test", + " }", + "}", + "public class TestSuper {", + " public void test(String x, int y){}", + "}")); + //@formatter:on + CompletionList list = requestCompletions(unit, "obj.test"); + assertFalse(list.getItems().isEmpty()); + assertTrue(list.getItems().get(0).getLabelDetails().getDetail().startsWith("(...)")); + assertTrue(list.getItems().get(0).getLabelDetails().getDescription().startsWith("2 overloads")); + } + private CompletionList requestCompletions(ICompilationUnit unit, String completeBehind) throws JavaModelException { return requestCompletions(unit, completeBehind, 0); }