items that are prefixes of other items should go first

Java expected type also should go first, and win
current class should be preferred in completion (IDEA-64703)
This commit is contained in:
peter
2011-02-14 21:21:32 +01:00
parent 67042244dd
commit ec40eab2f2
14 changed files with 201 additions and 32 deletions
@@ -50,12 +50,14 @@ public class JavaClassNameCompletionContributor extends CompletionContributor {
public JavaClassNameCompletionContributor() {
extend(CompletionType.CLASS_NAME, psiElement(), new CompletionProvider<CompletionParameters>() {
public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext matchingContext, @NotNull final CompletionResultSet result) {
public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext matchingContext, @NotNull final CompletionResultSet _result) {
if (shouldShowSecondSmartCompletionHint(parameters) &&
CompletionUtil.shouldShowFeature(parameters, CodeCompletionFeatures.SECOND_CLASS_NAME_COMPLETION)) {
CompletionService.getCompletionService().setAdvertisementText(CompletionBundle.message("completion.class.name.hint.2", getActionShortcut(IdeActions.ACTION_CLASS_NAME_COMPLETION)));
}
final CompletionResultSet result = JavaCompletionContributor.addJavaSorting(parameters, _result);
final PsiElement insertedElement = parameters.getPosition();
final ElementFilter filter =
@@ -15,6 +15,7 @@
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix;
import com.intellij.codeInsight.hint.ShowParameterInfoHandler;
@@ -165,7 +166,7 @@ public class JavaCompletionContributor extends CompletionContributor {
return TrueFilter.INSTANCE;
}
public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) {
public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet _result) {
if (parameters.getCompletionType() != CompletionType.BASIC) {
return;
}
@@ -179,6 +180,9 @@ public class JavaCompletionContributor extends CompletionContributor {
return;
}
final CompletionResultSet result = addJavaSorting(parameters, _result);
if (ANNOTATION_ATTRIBUTE_NAME.accepts(position)) {
completeAnnotationAttributeName(result, position, parameters);
result.stopHere();
@@ -202,6 +206,18 @@ public class JavaCompletionContributor extends CompletionContributor {
result.stopHere();
}
public static CompletionResultSet addJavaSorting(CompletionParameters parameters, CompletionResultSet result) {
final boolean afterNew = JavaSmartCompletionContributor.AFTER_NEW.accepts(parameters.getPosition());
final ExpectedTypeInfo[] expectedTypes = afterNew ? JavaSmartCompletionContributor.getExpectedTypes(parameters) : ExpectedTypeInfo.EMPTY_ARRAY;
return result.withRelevanceSorter(CompletionSorter.defaultSorter(parameters).weighBefore("liftShorter", new LookupElementWeigher("expectedAfterNew") {
@Override
@Nullable
public Comparable weigh(@NotNull LookupElement element) {
return -((Enum)PreferExpectedTypeWeigher.weigh(element, expectedTypes)).ordinal();
}
}));
}
public static void addAllClasses(CompletionParameters parameters,
final CompletionResultSet result,
PsiElement position,
@@ -43,8 +43,12 @@ public class PreferExpectedTypeWeigher extends CompletionWeigher {
ExpectedTypeInfo[] expectedInfos = JavaCompletionUtil.EXPECTED_TYPES.getValue(location);
if (expectedInfos == null) return MyResult.normal;
return weigh(item, expectedInfos);
}
public static MyResult weigh(LookupElement item, ExpectedTypeInfo[] expectedInfos) {
PsiType itemType = JavaCompletionUtil.getLookupElementType(item);
if (itemType == null || !itemType.isValid()) return MyResult.normal;
if (itemType == null) return MyResult.normal;
for (final ExpectedTypeInfo expectedInfo : expectedInfos) {
final PsiType defaultType = expectedInfo.getDefaultType();
@@ -44,6 +44,12 @@ public class KnownElementWeigher extends ProximityWeigher {
return -1;
}
}
if ("subSequence".equals(method.getName())) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && CommonClassNames.JAVA_LANG_STRING.equals(containingClass.getQualifiedName())) {
return -1;
}
}
}
return 0;
}
@@ -1,6 +1,6 @@
class A{
{
String str;
str.toString();
str.toCharArray();
}
}
@@ -1,7 +1,7 @@
class A{
{
final String path = BASE_PATH;
path.toString()<caret>;
path.toCharArray()<caret>;
sdkfjsdkhjfh();
}
}
@@ -223,7 +223,7 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
public void testTryInExpression() throws Throwable {
configureByFile(BASE_PATH + "/" + getTestName(true) + ".java");
assertStringItems("this", "toString");
assertStringItems("toString", "this");
}
public void testNull() throws Exception{
@@ -71,7 +71,7 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase {
final int old = CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE;
try {
CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE = CodeInsightSettings.FIRST_LETTER;
checkPreferredItems(0, "classLoader", "classBeforeLoader", "clone", "class");
checkPreferredItems(0, "classLoader", "class", "classBeforeLoader", "clone");
}
finally {
CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE = old;
@@ -111,7 +111,7 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase {
public void testDispreferImpls() throws Throwable {
myFixture.addClass("package foo; public class Xxx {}");
checkPreferredItems(0, "Xxy", "Xxx", "XxxEx", "XxxImpl");
checkPreferredItems(0, "Xxx", "XxxEx", "XxxImpl", "Xxy");
}
public void testPreferOwnInnerClasses() throws Throwable {
@@ -130,7 +130,7 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase {
public void testPreferLessHumps() throws Throwable {
myFixture.addClass("package foo; public interface XaYa {}");
myFixture.addClass("package foo; public interface XyYa {}");
checkPreferredItems(0, "XaYa", "XyYa", "XaYaEx", "XaYaImpl", "XyYaXa");
checkPreferredItems(0, "XaYa", "XaYaEx", "XaYaImpl", "XyYa", "XyYaXa");
}
public void testPreferLessParameters() throws Throwable {
@@ -178,7 +178,7 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase {
checkPreferredItems(0, "value");
}
public void _testCurrentClassBest() {
public void testCurrentClassBest() {
checkPreferredItems(0, "XcodeProjectTemplate", "XcodeConfigurable");
}
@@ -7,13 +7,13 @@ import com.intellij.codeInsight.lookup.Lookup
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupManager
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.fileTypes.StdFileTypes
import com.intellij.psi.CommonClassNames
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiMethod
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import com.intellij.openapi.fileTypes.StdFileTypes
public class NormalCompletionTest extends LightFixtureCompletionTestCase {
@Override
@@ -101,26 +101,17 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase {
}
public void testQualifiedNew1() throws Exception {
configureByFile("QualifiedNew1.java");
assertEquals(2, myItems.length);
assertEquals("Inner", myItems[0].getLookupString());
assertEquals("IInner", myItems[1].getLookupString());
configure()
assertStringItems "IInner", "Inner"
}
public void testQualifiedNew2() throws Exception {
configureByFile("QualifiedNew2.java");
assertEquals(2, myItems.length);
assertEquals("AnInner", myItems[0].getLookupString());
assertEquals("Inner", myItems[1].getLookupString());
configure()
assertStringItems "AnInner", "Inner"
}
public void testKeywordsInName() throws Exception {
myFixture.configureByFile(getTestName(false) + ".java");
myFixture.completeBasic()
myFixture.type 'a\n'
checkResult()
doTest 'a\n'
}
public void testSimpleVariable() throws Exception { doTest() }
@@ -16,7 +16,7 @@ public class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase {
}
public void testJComponentAdd() throws Throwable {
checkPreferredItems(0, "name", "b", "fooBean239", "this", "getName");
checkPreferredItems(0, "name", "b", "foo", "fooBean239", "this");
}
public void testJComponentAddNew() throws Throwable {
@@ -35,7 +35,7 @@ public class PrefixMatchingWeigher extends CompletionWeigher {
final String prefixHumps = StringUtil.capitalsOnly(prefix);
final String itemHumps = StringUtil.capitalsOnly(lookupString);
if (itemHumps.startsWith(prefixHumps)) return 100 - itemHumps.length();
if (itemHumps.startsWith(prefixHumps)) return 100;// - itemHumps.length();
if (lookupString.startsWith(prefix)) return 5;
if (StringUtil.startsWithIgnoreCase(lookupString, prefix)) return 1;
@@ -16,6 +16,7 @@
package com.intellij.codeInsight.completion.impl;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.Classifier;
import com.intellij.codeInsight.lookup.ClassifierFactory;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
@@ -34,7 +35,7 @@ import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.*;
/**
* @author peter
@@ -231,7 +232,25 @@ public class CompletionServiceImpl extends CompletionService{
public CompletionSorterImpl defaultSorter(CompletionParameters parameters) {
final CompletionLocation location = new CompletionLocation(parameters);
CompletionSorterImpl sorter = emptySorter();
CompletionSorterImpl sorter = emptySorter().weigh(new LookupElementWeigher("prefixHumps") {
@Override
public Boolean weigh(@NotNull LookupElement element) {
final String prefix = element.getPrefixMatcher().getPrefix();
if (!prefix.isEmpty()) {
final String prefixHumps = StringUtil.capitalsOnly(prefix);
if (prefixHumps.length() > 0) {
for (String itemString : element.getAllLookupStrings()) {
if (StringUtil.capitalsOnly(itemString).startsWith(prefixHumps)) {
return false;
}
}
}
}
return true;
}
});
for (final Weigher weigher : WeighingService.getWeighers(CompletionService.RELEVANCE_KEY)) {
sorter = sorter.weigh(new LookupElementWeigher(weigher.toString()) {
@Override
@@ -241,7 +260,12 @@ public class CompletionServiceImpl extends CompletionService{
});
}
return sorter;
return sorter.withClassifier("priority", true, new ClassifierFactory<LookupElement>("liftShorter") {
@Override
public Classifier<LookupElement> createClassifier(final Classifier<LookupElement> next) {
return new LiftShorterItemsClassifier(next);
}
});
}
public CompletionSorterImpl emptySorter() {
@@ -35,10 +35,11 @@ public class CompletionSorterImpl extends CompletionSorter {
}
private static ClassifierFactory<LookupElement> weighingFactory(final LookupElementWeigher weigher) {
return new ClassifierFactory<LookupElement>(weigher.getClass().getName()) {
final String id = weigher.toString();
return new ClassifierFactory<LookupElement>(id) {
@Override
public Classifier<LookupElement> createClassifier(Classifier<LookupElement> next) {
return new ComparingClassifier<LookupElement>(next, weigher.toString()) {
return new ComparingClassifier<LookupElement>(next, id) {
@Override
public Comparable getWeight(LookupElement element) {
return weigher.weigh(element);
@@ -0,0 +1,125 @@
/*
* Copyright 2000-2011 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.codeInsight.completion.impl;
import com.intellij.codeInsight.lookup.Classifier;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.util.SmartList;
import com.intellij.util.containers.MultiMap;
import java.util.*;
/**
* @author peter
*/
class LiftShorterItemsClassifier extends Classifier<LookupElement> {
private final TreeSet<String> mySortedStrings;
private final MultiMap<String, LookupElement> myElements;
private final MultiMap<String, String> myPrefixes;
private final Classifier<LookupElement> myNext;
public LiftShorterItemsClassifier(Classifier<LookupElement> next) {
myNext = next;
mySortedStrings = new TreeSet<String>();
myElements = new MultiMap<String, LookupElement>();
myPrefixes = new MultiMap<String, String>();
}
@Override
public void addElement(LookupElement element) {
final Set<String> strings = element.getAllLookupStrings();
for (String string : strings) {
if (string.length() == 0) continue;
myElements.putValue(string, element);
mySortedStrings.add(string);
final NavigableSet<String> after = mySortedStrings.tailSet(string, false);
for (String s : after) {
if (!s.startsWith(string)) {
break;
}
myPrefixes.putValue(s, string);
}
final char first = string.charAt(0);
final SortedSet<String> before = mySortedStrings.descendingSet().tailSet(string, false);
for (String s : before) {
if (s.charAt(0) != first) {
break;
}
if (string.startsWith(s)) {
myPrefixes.putValue(string, s);
}
}
}
myNext.addElement(element);
}
@Override
public Iterable<List<LookupElement>> classify(List<LookupElement> source) {
return liftShorterElements(source, new HashSet<LookupElement>());
}
private Iterable<List<LookupElement>> liftShorterElements(List<LookupElement> source, Set<LookupElement> lifted) {
final Set<LookupElement> srcSet = new HashSet<LookupElement>(source);
final Iterable<List<LookupElement>> classified = myNext.classify(source);
final Set<LookupElement> processed = new HashSet<LookupElement>();
final ArrayList<List<LookupElement>> result = new ArrayList<List<LookupElement>>();
for (List<LookupElement> list : classified) {
final ArrayList<LookupElement> group = new ArrayList<LookupElement>();
for (LookupElement element : list) {
if (processed.add(element)) {
final List<String> prefixes = new SmartList<String>();
for (String string : element.getAllLookupStrings()) {
prefixes.addAll(myPrefixes.get(string));
}
Collections.sort(prefixes);
for (String prefix : prefixes) {
for (LookupElement shorterElement : myElements.get(prefix)) {
if (srcSet.contains(shorterElement) && processed.add(shorterElement)) {
lifted.add(shorterElement);
group.add(shorterElement);
}
}
}
group.add(element);
}
}
result.add(group);
}
return result;
}
@Override
public void describeItems(LinkedHashMap<LookupElement, StringBuilder> map) {
final HashSet<LookupElement> lifted = new HashSet<LookupElement>();
liftShorterElements(new ArrayList<LookupElement>(map.keySet()), lifted);
if (!lifted.isEmpty()) {
for (LookupElement element : map.keySet()) {
final StringBuilder builder = map.get(element);
if (builder.length() > 0) {
builder.append(", ");
}
builder.append("liftShorter=").append(lifted.contains(element));
}
}
myNext.describeItems(map);
}
}