diff --git a/idea/src/org/jetbrains/jet/plugin/completion/weigher/JetCompletionSorting.java b/idea/src/org/jetbrains/jet/plugin/completion/weigher/JetCompletionSorting.java index 5c9cf07ea61..7870752caaf 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/weigher/JetCompletionSorting.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/weigher/JetCompletionSorting.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.plugin.completion.weigher; import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.CompletionSorter; +import org.jetbrains.jet.lang.psi.JetFile; /** * @author Nikolay Krasko @@ -29,7 +30,9 @@ public final class JetCompletionSorting { public static CompletionResultSet addJetSorting(CompletionParameters parameters, CompletionResultSet result) { CompletionSorter sorter = CompletionSorter.defaultSorter(parameters, result.getPrefixMatcher()); - sorter = sorter.weighAfter("negativeStats", new JetLocalPreferableWeigher()); + sorter = sorter.weighAfter("negativeStats", + new JetLocalPreferableWeigher(), + new JetExplicitlyImportedWeigher((JetFile)parameters.getOriginalFile())); return result.withRelevanceSorter(sorter); } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/weigher/JetExplicitlyImportedWeigher.java b/idea/src/org/jetbrains/jet/plugin/completion/weigher/JetExplicitlyImportedWeigher.java new file mode 100644 index 00000000000..e85dd8e4899 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/completion/weigher/JetExplicitlyImportedWeigher.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2012 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 org.jetbrains.jet.plugin.completion.weigher; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementWeigher; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.ImportPath; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; +import org.jetbrains.jet.plugin.completion.JetLookupObject; +import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper; + +/** + * @author Nikolay Krasko + */ +// class ExplicitlyImportedWeigher extends ProximityWeigher { +public class JetExplicitlyImportedWeigher extends LookupElementWeigher { + private final JetFile file; + + protected JetExplicitlyImportedWeigher(JetFile file) { + super("JetExplicitlyWeigher"); + this.file = file; + } + + private enum MyResult { + kotlinDefaultImport, + imported, + notImported, + normal + } + + + @NotNull + @Override + public Comparable weigh(@NotNull LookupElement element) { + Object object = element.getObject(); + if (object instanceof JetLookupObject) { + JetLookupObject lookupObject = (JetLookupObject) object; + DeclarationDescriptor descriptor = lookupObject.getDescriptor(); + if (descriptor != null) { + FqNameUnsafe fqName = DescriptorUtils.getFQName(descriptor); + // Invalid name can be met for class object descriptor: Test.MyTest.A..testOther + if (FqName.isValid(fqName.toString())) { + ImportPath importPath = new ImportPath(fqName.toString()); + if (ImportInsertHelper.doNeedImport(importPath, null, file)) { + return MyResult.notImported; + } + else { + if (ImportInsertHelper.isImportedWithKotlinDefault(importPath)) { + return MyResult.kotlinDefaultImport; + } + return MyResult.imported; + } + } + } + } + + return MyResult.normal; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java index b3a89e0f992..25f54c9643d 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java @@ -31,7 +31,10 @@ import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetImportDirective; import org.jetbrains.jet.lang.psi.JetPsiFactory; import org.jetbrains.jet.lang.psi.JetPsiUtil; -import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.ImportPath; import org.jetbrains.jet.lang.resolve.java.JavaBridgeConfiguration; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; import org.jetbrains.jet.lang.resolve.name.FqName; @@ -140,18 +143,28 @@ public class ImportInsertHelper { return true; } - for (ImportPath defaultJetImport : DefaultModuleConfiguration.DEFAULT_JET_IMPORTS) { - if (QualifiedNamesUtil.isImported(defaultJetImport, importPath)) { - return true; - } - } + if (isImportedWithKotlinDefault(importPath)) return true; + if (isImportedWithJavaDefault(importPath)) return true; + + return false; + } + + public static boolean isImportedWithJavaDefault(ImportPath importPath) { for (ImportPath defaultJavaImport : JavaBridgeConfiguration.DEFAULT_JAVA_IMPORTS) { if (QualifiedNamesUtil.isImported(defaultJavaImport, importPath)) { return true; } } + return false; + } + public static boolean isImportedWithKotlinDefault(ImportPath importPath) { + for (ImportPath defaultJetImport : DefaultModuleConfiguration.DEFAULT_JET_IMPORTS) { + if (QualifiedNamesUtil.isImported(defaultJetImport, importPath)) { + return true; + } + } return false; } diff --git a/idea/testData/completion/basic/StandardJetArrayFirst.kt b/idea/testData/completion/basic/StandardJetArrayFirst.kt new file mode 100644 index 00000000000..af30eafbab2 --- /dev/null +++ b/idea/testData/completion/basic/StandardJetArrayFirst.kt @@ -0,0 +1,15 @@ +fun main(args: Array) { + Array +} + +// COMPLETE: 2 +// WITH_ORDER: 1 +// EXIST: Array~(jet) +// EXIST: Array~(java.sql) + + + + + + +// Developer! Every ancient failure of this test is important! \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java b/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java index 33ceee1c78e..2e2e1eef163 100644 --- a/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java +++ b/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.testing.InTextDirectivesUtils; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -94,21 +95,25 @@ public class ExpectedCompletionUtils { public static final String ABSENT_LINE_PREFIX = "// ABSENT:"; public static final String NUMBER_LINE_PREFIX = "// NUMBER:"; public static final String EXECUTION_TIME_PREFIX = "// TIME:"; + public static final String WITH_ORDER_PREFIX = "// WITH_ORDER:"; private final String existLinePrefix; private final String absentLinePrefix; private final String numberLinePrefix; private final String executionTimePrefix; + private final String withOrderPrefix; public ExpectedCompletionUtils() { - this(EXIST_LINE_PREFIX, ABSENT_LINE_PREFIX, NUMBER_LINE_PREFIX, EXECUTION_TIME_PREFIX); + this(EXIST_LINE_PREFIX, ABSENT_LINE_PREFIX, NUMBER_LINE_PREFIX, EXECUTION_TIME_PREFIX, WITH_ORDER_PREFIX); } - public ExpectedCompletionUtils(String existLinePrefix, String absentLinePrefix, String numberLinePrefix, String execitionTimePrefix) { + public ExpectedCompletionUtils(String existLinePrefix, String absentLinePrefix, + String numberLinePrefix, String executionTimePrefix, String withOrderPrefix) { this.existLinePrefix = existLinePrefix; this.absentLinePrefix = absentLinePrefix; this.numberLinePrefix = numberLinePrefix; - this.executionTimePrefix = execitionTimePrefix; + this.executionTimePrefix = executionTimePrefix; + this.withOrderPrefix = withOrderPrefix; } @NotNull @@ -122,7 +127,7 @@ public class ExpectedCompletionUtils { } public static CompletionProposal[] processProposalAssertions(String prefix, String fileText) { - List proposals = new ArrayList(); + Collection proposals = new ArrayList(); for (String proposalStr : InTextDirectivesUtils.findListWithPrefix(prefix, fileText)) { Matcher matcher = CompletionProposal.PATTERN.matcher(proposalStr); matcher.find(); @@ -144,15 +149,28 @@ public class ExpectedCompletionUtils { return InTextDirectivesUtils.getPrefixedInt(fileText, executionTimePrefix); } - protected static void assertContainsRenderedItems(CompletionProposal[] expected, LookupElement[] items) { + public boolean isWithOrder(String fileText) { + return InTextDirectivesUtils.getPrefixedInt(fileText, withOrderPrefix) != null; + } + + protected static void assertContainsRenderedItems(CompletionProposal[] expected, LookupElement[] items, boolean checkOrder) { List itemsInformation = getItemsInformation(items); + int indexOfPrevious = Integer.MIN_VALUE; + for (CompletionProposal expectedProposal : expected) { boolean isFound = false; - for (CompletionProposal proposal : itemsInformation) { + for (int index = 0; index < itemsInformation.size(); index++) { + CompletionProposal proposal = itemsInformation.get(index); + if (proposal.isSuitable(expectedProposal)) { isFound = true; + + Assert.assertTrue("Invalid order of existent elements in " + listToString(itemsInformation), + !checkOrder || index > indexOfPrevious); + indexOfPrevious = index; + break; } } @@ -186,7 +204,7 @@ public class ExpectedCompletionUtils { return result; } - protected static String listToString(List items) { + protected static String listToString(Collection items) { return StringUtil.join( Collections2.transform(items, new Function() { @Override diff --git a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java index 97620889167..d9ba50ff5f3 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java @@ -145,6 +145,10 @@ public class JetBasicCompletionTest extends JetCompletionTestBase { doTest(); } + public void testStandardJetArrayFirst() { + doTest(); + } + public void testSubpackageInFun() { doTest(); } diff --git a/idea/tests/org/jetbrains/jet/completion/JetCompletionMultiTestBase.java b/idea/tests/org/jetbrains/jet/completion/JetCompletionMultiTestBase.java index cb11bef5e1c..6e706422bc7 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetCompletionMultiTestBase.java +++ b/idea/tests/org/jetbrains/jet/completion/JetCompletionMultiTestBase.java @@ -38,7 +38,9 @@ public abstract class JetCompletionMultiTestBase extends CompletionTestCase { final String fileText = getFile().getText(); final ExpectedCompletionUtils completionUtils = new ExpectedCompletionUtils(); - ExpectedCompletionUtils.assertContainsRenderedItems(completionUtils.itemsShouldExist(fileText), myItems); + ExpectedCompletionUtils.assertContainsRenderedItems( + completionUtils.itemsShouldExist(fileText), myItems, completionUtils.isWithOrder(fileText)); + ExpectedCompletionUtils.assertNotContainsRenderedItems(completionUtils.itemsShouldAbsent(fileText), myItems); Integer itemsNumber = completionUtils.getExpectedNumber(fileText); diff --git a/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java index 947917387db..e761e7dda85 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java +++ b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java @@ -19,9 +19,9 @@ package org.jetbrains.jet.completion; import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.completion.LightCompletionTestCase; +import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupManager; -import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.openapi.projectRoots.JavaSdk; import com.intellij.openapi.projectRoots.Sdk; import org.apache.commons.lang.SystemUtils; @@ -71,7 +71,7 @@ public abstract class JetCompletionTestBase extends LightCompletionTestCase { myItems = new LookupElement[0]; } - ExpectedCompletionUtils.assertContainsRenderedItems(expected, myItems); + ExpectedCompletionUtils.assertContainsRenderedItems(expected, myItems, completionUtils.isWithOrder(fileText)); ExpectedCompletionUtils.assertNotContainsRenderedItems(unexpected, myItems); if (itemsNumber != null) { @@ -110,7 +110,7 @@ public abstract class JetCompletionTestBase extends LightCompletionTestCase { protected void complete(final int time) { new CodeCompletionHandlerBase(type, false, false, true).invokeCompletion(getProject(), getEditor(), time, false); - LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(myEditor); + Lookup lookup = LookupManager.getActiveLookup(myEditor); myItems = lookup == null ? null : lookup.getItems().toArray(LookupElement.EMPTY_ARRAY); myPrefix = lookup == null ? null : lookup.itemPattern(lookup.getItems().get(0)); }