From ec29e061152067569bddcfafe1c2f38977596bc3 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 7 Mar 2012 13:42:06 +0400 Subject: [PATCH] - KT-1430 Import popup/type completion does not work for extension functions - refactoring for auto import tests --- .../jet/plugin/caches/JetShortNamesCache.java | 73 ++++++++++- .../completion/JetCompletionContributor.java | 114 +++++++---------- .../plugin/quickfix/ImportClassAndFunFix.java | 59 ++++++--- .../basic/ExtensionFromStandardLibrary.kt | 2 +- ...erKotlinImport.kt => classImport.after.kt} | 0 ...ort.Main.kt => classImport.before.Main.kt} | 0 ...e.kt => classImport.before.data.Sample.kt} | 0 .../extensionFunctionImport.after.kt | 9 ++ .../extensionFunctionImport.before.Main.kt | 7 + ...ensionFunctionImport.before.data.Sample.kt | 4 + ...ctionImport.kt => functionImport.after.kt} | 0 ....Main.kt => functionImport.before.Main.kt} | 0 ...t => functionImport.before.data.Sample.kt} | 0 .../completion/JetBasicCompletionTest.java | 2 - .../JetMultifileBasicCompletionTest.java | 15 +-- .../plugin/quickfix/AutoImportFixTest.java | 56 ++++++++ .../quickfix/JetPsiCheckerMultifileTest.java | 90 ++----------- .../quickfix/JetQuickFixMultiFileTest.java | 121 ++++++++++++++++++ 18 files changed, 374 insertions(+), 178 deletions(-) rename idea/testData/quickfix/autoImports/{afterKotlinImport.kt => classImport.after.kt} (100%) rename idea/testData/quickfix/autoImports/{beforeKotlinImport.Main.kt => classImport.before.Main.kt} (100%) rename idea/testData/quickfix/autoImports/{beforeKotlinImport.Data.Sample.kt => classImport.before.data.Sample.kt} (100%) create mode 100644 idea/testData/quickfix/autoImports/extensionFunctionImport.after.kt create mode 100644 idea/testData/quickfix/autoImports/extensionFunctionImport.before.Main.kt create mode 100644 idea/testData/quickfix/autoImports/extensionFunctionImport.before.data.Sample.kt rename idea/testData/quickfix/autoImports/{afterFunctionImport.kt => functionImport.after.kt} (100%) rename idea/testData/quickfix/autoImports/{beforeFunctionImport.Main.kt => functionImport.before.Main.kt} (100%) rename idea/testData/quickfix/autoImports/{beforeFunctionImport.Data.Sample.kt => functionImport.before.data.Sample.kt} (100%) create mode 100644 idea/tests/org/jetbrains/jet/plugin/quickfix/AutoImportFixTest.java create mode 100644 idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixMultiFileTest.java diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java index 98fb60a0d75..c336cf9f56e 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java @@ -20,6 +20,7 @@ import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiField; @@ -33,11 +34,15 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.asJava.JavaElementFinder; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; -import org.jetbrains.jet.lang.psi.JetNamedFunction; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; import org.jetbrains.jet.lang.types.lang.JetStandardClasses; import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; @@ -204,6 +209,72 @@ public class JetShortNamesCache extends PsiShortNamesCache { return functions; } + // TODO: Make it work for properties + public Collection getJetCallableExtensions( + @NotNull Condition acceptedNameCondition, + @NotNull JetSimpleNameExpression expression, + @NotNull GlobalSearchScope searchScope + ) { + Collection resultDescriptors = new ArrayList(); + + if (!(expression.getContainingFile() instanceof JetFile)) { + return resultDescriptors; + } + + JetFile jetFile = (JetFile) expression.getContainingFile(); + + BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile); + JetExpression receiverExpression = expression.getReceiverExpression(); + + if (receiverExpression != null) { + JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); + JetScope scope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression); + + if (expressionType != null && scope != null) { + Collection extensionFunctionsNames = getAllJetExtensionFunctionsNames(searchScope); + + Set functionFQNs = new java.util.HashSet(); + + // Collect all possible extension function qualified names + for (String name : extensionFunctionsNames) { + if (acceptedNameCondition.value(name)) { + Collection extensionFunctions = getJetExtensionFunctionsByName(name, searchScope); + + for (PsiElement extensionFunction : extensionFunctions) { + if (extensionFunction instanceof JetNamedFunction) { + functionFQNs.add(JetPsiUtil.getFQName((JetNamedFunction) extensionFunction)); + } + else if (extensionFunction instanceof PsiMethod) { + PsiMethod function = (PsiMethod) extensionFunction; + PsiClass containingClass = function.getContainingClass(); + + if (containingClass != null) { + String classFQN = containingClass.getQualifiedName(); + + if (classFQN != null) { + String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN); + functionFQNs.add(QualifiedNamesUtil.combine(classParentFQN, function.getName())); + } + } + } + } + } + } + + // Iterate through the function with attempt to resolve found functions + for (String functionFQN : functionFQNs) { + for (CallableDescriptor functionDescriptor : ExpressionTypingUtils.canFindSuitableCall( + functionFQN, project, receiverExpression, expressionType, scope)) { + + resultDescriptors.add(functionDescriptor); + } + } + } + } + + return resultDescriptors; + } + public Collection getJetFunctionsByName(@NonNls @NotNull String name, @NotNull GlobalSearchScope scope) { return JetShortFunctionNameIndex.getInstance().get(name, project, scope); } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java index 4ae0f97eaba..27c945cdb88 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java @@ -20,10 +20,9 @@ import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.patterns.PlatformPatterns; -import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; @@ -31,19 +30,18 @@ import com.intellij.util.Consumer; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; -import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetQualifiedExpression; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.lang.psi.JetTypeReference; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.scopes.JetScope; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.plugin.caches.JetCacheManager; import org.jetbrains.jet.plugin.caches.JetShortNamesCache; import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; import org.jetbrains.jet.plugin.references.JetSimpleNameReference; -import org.jetbrains.jet.util.QualifiedNamesUtil; import java.util.Collection; import java.util.HashSet; @@ -80,14 +78,19 @@ public class JetCompletionContributor extends CompletionContributor { addReferenceVariant(result, variant, positions); } - if (result.getPrefixMatcher().getPrefix().isEmpty()) { + String prefix = result.getPrefixMatcher().getPrefix(); + + if (prefix.isEmpty() && parameters.getInvocationCount() < 2) { return; } - if (shouldRunTopLevelCompletion(parameters)) { + if (shouldRunTopLevelCompletion(parameters, prefix)) { addClasses(parameters, result); addJetTopLevelFunctions(result, position, positions); - addJetExtensionFunctions(jetReference.getExpression(), result, position); + } + + if (shouldRunExtensionsCompletion(parameters, prefix)) { + addJetExtensions(jetReference.getExpression(), result, position); } result.stopHere(); @@ -96,58 +99,23 @@ public class JetCompletionContributor extends CompletionContributor { }); } - // TODO: Make it work for properties - private static void addJetExtensionFunctions(JetSimpleNameExpression expression, CompletionResultSet result, PsiElement position) { + private static void addJetExtensions(JetSimpleNameExpression expression, CompletionResultSet result, PsiElement position) { + final PrefixMatcher prefixMatcher = result.getPrefixMatcher(); + JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache(); + Condition matchPrefixCondition = new Condition() { + @Override + public boolean value(String callableName) { + return prefixMatcher.prefixMatches(callableName); + } + }; + + Collection jetCallableExtensions = namesCache.getJetCallableExtensions( + matchPrefixCondition, expression, GlobalSearchScope.allScope(position.getProject())); BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) position.getContainingFile()); - JetExpression receiverExpression = expression.getReceiverExpression(); - if (receiverExpression != null) { - JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); - JetScope scope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression); - - if (expressionType != null && scope != null) { - JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache(); - Collection extensionFunctionsNames = namesCache.getAllJetExtensionFunctionsNames( - GlobalSearchScope.allScope(position.getProject())); - - Set functionFQNs = new HashSet(); - - // Collect all possible extension function qualified names - for (String name : extensionFunctionsNames) { - if (result.getPrefixMatcher().prefixMatches(name)) { - Collection extensionFunctions = - namesCache.getJetExtensionFunctionsByName(name, GlobalSearchScope.allScope(position.getProject())); - - for (PsiElement extensionFunction : extensionFunctions) { - if (extensionFunction instanceof JetNamedFunction) { - functionFQNs.add(JetPsiUtil.getFQName((JetNamedFunction) extensionFunction)); - } - else if (extensionFunction instanceof PsiMethod) { - PsiMethod function = (PsiMethod) extensionFunction; - PsiClass containingClass = function.getContainingClass(); - - if (containingClass != null) { - String classFQN = containingClass.getQualifiedName(); - - if (classFQN != null) { - String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN); - functionFQNs.add(QualifiedNamesUtil.combine(classParentFQN, function.getName())); - } - } - } - } - } - } - - // Iterate through the function with attempt to resolve found functions - for (String functionFQN : functionFQNs) { - for (CallableDescriptor functionDescriptor : ExpressionTypingUtils.canFindSuitableCall( - functionFQN, position.getProject(), receiverExpression, expressionType, scope)) { - result.addElement(DescriptorLookupConverter.createLookupElement(context, functionDescriptor)); - } - } - } + for (DeclarationDescriptor jetCallableExtension : jetCallableExtensions) { + result.addElement(DescriptorLookupConverter.createLookupElement(context, jetCallableExtension)); } } @@ -213,29 +181,33 @@ public class JetCompletionContributor extends CompletionContributor { return false; } - private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters) { - PsiElement element = parameters.getPosition(); - - if (parameters.getInvocationCount() > 1) { - return true; + private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters, String prefix) { + if (parameters.getInvocationCount() == 0 && prefix.length() < 3) { + return false; } + PsiElement element = parameters.getPosition(); if (element.getNode().getElementType() == JetTokens.IDENTIFIER) { if (element.getParent() instanceof JetSimpleNameExpression) { JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) element.getParent(); - if (PsiTreeUtil.getParentOfType(nameExpression, JetQualifiedExpression.class) != null) { - return false; - } - if (PsiTreeUtil.getParentOfType(nameExpression, JetUserType.class) != null) { - return parameters.getInvocationCount() == 1; - } + // Top level completion should be executed for simple which is not in qualified expression + return (PsiTreeUtil.getParentOfType(nameExpression, JetQualifiedExpression.class) == null); } } return false; } + private static boolean shouldRunExtensionsCompletion(CompletionParameters parameters, String prefix) { + if (parameters.getInvocationCount() == 0 && prefix.length() < 3) { + return false; + } + + return getJetReference(parameters) != null; + } + + @Nullable private static JetSimpleNameReference getJetReference(@NotNull CompletionParameters parameters) { PsiElement element = parameters.getPosition(); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassAndFunFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassAndFunFix.java index 9fed3b377e5..0090e5e0770 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassAndFunFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassAndFunFix.java @@ -29,6 +29,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; @@ -38,13 +40,16 @@ import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetNamedFunction; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.plugin.JetFileType; import org.jetbrains.jet.plugin.actions.JetAddImportAction; import org.jetbrains.jet.plugin.caches.JetCacheManager; +import org.jetbrains.jet.plugin.caches.JetShortNamesCache; import java.util.*; @@ -69,20 +74,23 @@ public class ImportClassAndFunFix extends JetHintAction return Collections.emptyList(); } + final String referenceName = element.getReferencedName(); + + if (!StringUtil.isNotEmpty(referenceName)) { + return Collections.emptyList(); + } + + assert referenceName != null; + final ArrayList result = new ArrayList(); - result.addAll(getClassNames(element, file.getProject())); - result.addAll(getJetTopLevelFunctions(element, file.getProject())); + result.addAll(getClassNames(referenceName, file.getProject())); + result.addAll(getJetTopLevelFunctions(referenceName, file.getProject())); + result.addAll(getJetExtensionFunctions(referenceName, element, file.getProject())); return result; } - private static Collection getJetTopLevelFunctions(@NotNull JetSimpleNameExpression expression, @NotNull Project project) { - final String referenceName = expression.getReferencedName(); - - if (referenceName == null) { - return Collections.emptyList(); - } - + private static Collection getJetTopLevelFunctions(@NotNull String referenceName, @NotNull Project project) { final Collection namedFunctions = JetCacheManager.getInstance(project).getNamesCache().getTopLevelFunctionsByName( referenceName, GlobalSearchScope.allScope(project)); @@ -104,16 +112,35 @@ public class ImportClassAndFunFix extends JetHintAction }); } + private static Collection getJetExtensionFunctions( + @NotNull final String referenceName, + @NotNull JetSimpleNameExpression expression, + @NotNull Project project + ) { + JetShortNamesCache namesCache = JetCacheManager.getInstance(project).getNamesCache(); + Collection jetCallableExtensions = namesCache.getJetCallableExtensions( + new Condition() { + @Override + public boolean value(String callableExtensionName) { + return callableExtensionName.equals(referenceName); + } + }, + expression, + GlobalSearchScope.allScope(project)); + + return Collections2.transform(jetCallableExtensions, new Function() { + @Override + public String apply(@Nullable DeclarationDescriptor declarationDescriptor) { + assert declarationDescriptor != null; + return DescriptorUtils.getFQName(declarationDescriptor); + } + }); + } + /* * Searches for possible class names in kotlin context and java facade. */ - public static List getClassNames(@NotNull JetSimpleNameExpression expression, @NotNull Project project) { - final String referenceName = expression.getReferencedName(); - - if (referenceName == null) { - return Collections.emptyList(); - } - + public static List getClassNames(@NotNull String referenceName, @NotNull Project project) { final GlobalSearchScope scope = GlobalSearchScope.allScope(project); Set possibleResolveNames = Sets.newHashSet(); possibleResolveNames.addAll(JetCacheManager.getInstance(project).getNamesCache().getFQNamesByName(referenceName, scope)); diff --git a/idea/testData/completion/basic/ExtensionFromStandardLibrary.kt b/idea/testData/completion/basic/ExtensionFromStandardLibrary.kt index f662efd8701..dce0c9ff246 100644 --- a/idea/testData/completion/basic/ExtensionFromStandardLibrary.kt +++ b/idea/testData/completion/basic/ExtensionFromStandardLibrary.kt @@ -8,6 +8,6 @@ fun firstFun() { } // RUNTIME: 1 -// TIME: 2 +// TIME: 1 // EXIST: toLinkedList // NUMBER: 1 \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/afterKotlinImport.kt b/idea/testData/quickfix/autoImports/classImport.after.kt similarity index 100% rename from idea/testData/quickfix/autoImports/afterKotlinImport.kt rename to idea/testData/quickfix/autoImports/classImport.after.kt diff --git a/idea/testData/quickfix/autoImports/beforeKotlinImport.Main.kt b/idea/testData/quickfix/autoImports/classImport.before.Main.kt similarity index 100% rename from idea/testData/quickfix/autoImports/beforeKotlinImport.Main.kt rename to idea/testData/quickfix/autoImports/classImport.before.Main.kt diff --git a/idea/testData/quickfix/autoImports/beforeKotlinImport.Data.Sample.kt b/idea/testData/quickfix/autoImports/classImport.before.data.Sample.kt similarity index 100% rename from idea/testData/quickfix/autoImports/beforeKotlinImport.Data.Sample.kt rename to idea/testData/quickfix/autoImports/classImport.before.data.Sample.kt diff --git a/idea/testData/quickfix/autoImports/extensionFunctionImport.after.kt b/idea/testData/quickfix/autoImports/extensionFunctionImport.after.kt new file mode 100644 index 00000000000..5073d950939 --- /dev/null +++ b/idea/testData/quickfix/autoImports/extensionFunctionImport.after.kt @@ -0,0 +1,9 @@ +// "Import Class" "true" +package testingExtensionFunctionsImport + +import testingExtensionFunctionsImport.data.someFun + +fun some() { + val str = "" + str.someFun() +} \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/extensionFunctionImport.before.Main.kt b/idea/testData/quickfix/autoImports/extensionFunctionImport.before.Main.kt new file mode 100644 index 00000000000..4bece8b061b --- /dev/null +++ b/idea/testData/quickfix/autoImports/extensionFunctionImport.before.Main.kt @@ -0,0 +1,7 @@ +// "Import Class" "true" +package testingExtensionFunctionsImport + +fun some() { + val str = "" + str.someFun() +} \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/extensionFunctionImport.before.data.Sample.kt b/idea/testData/quickfix/autoImports/extensionFunctionImport.before.data.Sample.kt new file mode 100644 index 00000000000..67c1800b093 --- /dev/null +++ b/idea/testData/quickfix/autoImports/extensionFunctionImport.before.data.Sample.kt @@ -0,0 +1,4 @@ +package testingExtensionFunctionsImport.data + +fun String.someFun() { +} \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/afterFunctionImport.kt b/idea/testData/quickfix/autoImports/functionImport.after.kt similarity index 100% rename from idea/testData/quickfix/autoImports/afterFunctionImport.kt rename to idea/testData/quickfix/autoImports/functionImport.after.kt diff --git a/idea/testData/quickfix/autoImports/beforeFunctionImport.Main.kt b/idea/testData/quickfix/autoImports/functionImport.before.Main.kt similarity index 100% rename from idea/testData/quickfix/autoImports/beforeFunctionImport.Main.kt rename to idea/testData/quickfix/autoImports/functionImport.before.Main.kt diff --git a/idea/testData/quickfix/autoImports/beforeFunctionImport.Data.Sample.kt b/idea/testData/quickfix/autoImports/functionImport.before.data.Sample.kt similarity index 100% rename from idea/testData/quickfix/autoImports/beforeFunctionImport.Data.Sample.kt rename to idea/testData/quickfix/autoImports/functionImport.before.data.Sample.kt diff --git a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java index 9defa10a27e..1044269ae4c 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java @@ -109,8 +109,6 @@ public class JetBasicCompletionTest extends JetCompletionTestBase { doTest(); } - - @Override protected String getTestDataPath() { return new File(PluginTestCaseBase.getTestDataPathBase(), "/completion/basic").getPath() + diff --git a/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java index 7056bf1131c..9408e1781c8 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java @@ -24,32 +24,31 @@ import org.jetbrains.jet.plugin.PluginTestCaseBase; public class JetMultifileBasicCompletionTest extends JetCompletionMultiTestBase { public void testDoNotCompleteWithConstraints() { - doFileTest(2); + doFileTest(); } public void testTopLevelFunction() throws Exception { - doFileTest(2); + doFileTest(); } public void todotestExtensionFunctionOnUnresolved() throws Exception { - doFileTest(2); + doFileTest(); } public void testExtensionOnNullable() throws Exception { - doFileTest(2); + doFileTest(); } public void todotestExtensionProperty() throws Exception { - doFileTest(2); + doFileTest(); } public void testNotImportedExtensionFunction() throws Exception { - doFileTest(2); + doFileTest(); } public void testExtensionFunction() throws Exception { - // TODO: fix and uncomment - // doFileTest(); + doFileTest(2); } @Override diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/AutoImportFixTest.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/AutoImportFixTest.java new file mode 100644 index 00000000000..f1b64fe6906 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/AutoImportFixTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-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.quickfix; + +import org.jetbrains.jet.JetTestCaseBuilder; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Nikolay Krasko + */ +public class AutoImportFixTest extends JetQuickFixMultiFileTest { + + public void testClassImport() throws Exception { + doTest(); + } + + public void testExtensionFunctionImport() throws Exception { + doTest(); + } + + public void testFunctionImport() throws Exception { + doTest(); + } + + @Override + protected String getCheckFileName() { + return getTestName(true) + ".after.kt"; + } + + @Override + protected List getTestFileNames() { + return Arrays.asList(getTestName(true) + ".before.Main.kt", + getTestName(true) + ".before.data.Sample.kt"); + } + + @Override + protected String getTestDataPath() { + return JetTestCaseBuilder.getHomeDirectory() + "/idea/testData/quickfix/autoImports/"; + } +} diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/JetPsiCheckerMultifileTest.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/JetPsiCheckerMultifileTest.java index fef11e02002..14e7769dd32 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/JetPsiCheckerMultifileTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/JetPsiCheckerMultifileTest.java @@ -20,16 +20,9 @@ import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; -import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase; -import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase; import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler; -import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.util.Pair; -import com.intellij.rt.execution.junit.FileComparisonFailure; -import com.intellij.util.ui.UIUtil; import junit.framework.Test; import junit.framework.TestSuite; import org.jetbrains.annotations.NotNull; @@ -44,7 +37,7 @@ import java.util.*; /** * @author Nikolay Krasko */ -public class JetPsiCheckerMultifileTest extends DaemonAnalyzerTestCase { +public class JetPsiCheckerMultifileTest extends JetQuickFixMultiFileTest { public final static String MAIN_SUBSTRING = ".Main"; public final static String DATA_SUBSTRING = ".Data"; @@ -56,86 +49,25 @@ public class JetPsiCheckerMultifileTest extends DaemonAnalyzerTestCase { this.dataPath = dataPath; this.name = name; - setName("testRun"); + setName("doTest"); } - protected static boolean shouldBeAvailableAfterExecution() { - return false; + @Override + protected String getCheckFileName() { + return name.replace("before", "after").replace(MAIN_SUBSTRING, "") + ".kt"; } - public void testRun() throws Exception { - configureByFiles(null, getFileNames(getTestFiles()).toArray(new String[1])); - doTest(); + @Override + protected List getTestFileNames() { + return getFileNames(getTestFiles()); } - public void doTest() { - CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() { - @Override - public void run() { - try { - final Pair pair = LightQuickFixTestCase.parseActionHint(getFile(), loadFile(getFile().getName())); - final String text = pair.getFirst(); - - final boolean actionShouldBeAvailable = pair.getSecond(); - - doAction(text, actionShouldBeAvailable, getTestDataPath()); - } - catch (FileComparisonFailure e){ - throw e; - } - catch (Throwable e) { - e.printStackTrace(); - fail(getTestName(true)); - } - } - }, "", ""); - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - public void doAction(final String text, final boolean actionShouldBeAvailable, final String testFullPath) - throws Exception { - IntentionAction action = LightQuickFixTestCase.findActionWithText(getAvailableActions(), text); - if (action == null) { - if (actionShouldBeAvailable) { - List actions = getAvailableActions(); - List texts = new ArrayList(); - for (IntentionAction intentionAction : actions) { - texts.add(intentionAction.getText()); - } - Collection infos = doHighlighting(); - fail("Action with text '" + text + "' is not available in test " + testFullPath + "\n" + - "Available actions (" + texts.size() + "): " + texts + "\n" + - actions + "\n" + - "Infos:" + infos); - } - } - else { - if (!actionShouldBeAvailable) { - fail("Action '" + text + "' is available (but must not) in test " + testFullPath); - } - - ShowIntentionActionsHandler.chooseActionAndInvoke(getFile(), getEditor(), action, action.getText()); - - UIUtil.dispatchAllInvocationEvents(); - - if (!shouldBeAvailableAfterExecution()) { - final IntentionAction afterAction = LightQuickFixTestCase.findActionWithText(getAvailableActions(), text); - - if (afterAction != null) { - fail("Action '" + text + "' is still available after its invocation in test " + testFullPath); - } - } - - checkResultByFile(name.replace("before", "after").replace(MAIN_SUBSTRING, "") + ".kt"); - } - } - protected List getTestFiles() { File dir = new File(getTestDataPath()); assertTrue("Main file should contain .Main. substring", name.contains(MAIN_SUBSTRING)); final String testPrefix = name.replace(MAIN_SUBSTRING, ""); - + // Files of single test FilenameFilter resultFilter = new FilenameFilter() { @Override @@ -145,7 +77,7 @@ public class JetPsiCheckerMultifileTest extends DaemonAnalyzerTestCase { }; List allTestFiles = Arrays.asList(dir.listFiles(resultFilter)); - + final Collection mainFiles = Collections2.filter(allTestFiles, new Predicate() { @Override public boolean apply(@Nullable File file) { @@ -168,7 +100,7 @@ public class JetPsiCheckerMultifileTest extends DaemonAnalyzerTestCase { return fileResult; } - + protected static List getFileNames(List files) { return Lists.newArrayList(Collections2.transform(files, new Function() { @Override diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixMultiFileTest.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixMultiFileTest.java new file mode 100644 index 00000000000..c67f014e3fe --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixMultiFileTest.java @@ -0,0 +1,121 @@ +/* + * Copyright 2000-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.quickfix; + +import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase; +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.Pair; +import com.intellij.util.ui.UIUtil; +import junit.framework.ComparisonFailure; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author Nikolay Krasko + */ +public abstract class JetQuickFixMultiFileTest extends DaemonAnalyzerTestCase { + + protected static boolean shouldBeAvailableAfterExecution() { + return false; + } + + public void doTest() throws Exception { + configureByFiles(null, getTestFileNames().toArray(new String[1])); + + CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() { + @Override + public void run() { + try { + final Pair pair = LightQuickFixTestCase.parseActionHint(getFile(), loadFile(getFile().getName())); + final String text = pair.getFirst(); + + final boolean actionShouldBeAvailable = pair.getSecond(); + + doAction(text, actionShouldBeAvailable, getTestDataPath()); + } + catch (ComparisonFailure e){ + throw e; + } + catch (Throwable e) { + e.printStackTrace(); + fail(getTestName(true)); + } + } + }, "", ""); + } + + @SuppressWarnings({"HardCodedStringLiteral"}) + public void doAction(final String text, final boolean actionShouldBeAvailable, final String testFullPath) + throws Exception { + IntentionAction action = LightQuickFixTestCase.findActionWithText(getAvailableActions(), text); + if (action == null) { + if (actionShouldBeAvailable) { + List actions = getAvailableActions(); + List texts = new ArrayList(); + for (IntentionAction intentionAction : actions) { + texts.add(intentionAction.getText()); + } + Collection infos = doHighlighting(); + fail("Action with text '" + text + "' is not available in test " + testFullPath + "\n" + + "Available actions (" + texts.size() + "): " + texts + "\n" + + actions + "\n" + + "Infos:" + infos); + } + } + else { + if (!actionShouldBeAvailable) { + fail("Action '" + text + "' is available (but must not) in test " + testFullPath); + } + + ShowIntentionActionsHandler.chooseActionAndInvoke(getFile(), getEditor(), action, action.getText()); + + UIUtil.dispatchAllInvocationEvents(); + + if (!shouldBeAvailableAfterExecution()) { + final IntentionAction afterAction = LightQuickFixTestCase.findActionWithText(getAvailableActions(), text); + + if (afterAction != null) { + fail("Action '" + text + "' is still available after its invocation in test " + testFullPath); + } + } + + checkResultByFile(getCheckFileName()); + } + } + + protected abstract String getCheckFileName(); + + protected abstract List getTestFileNames(); + + protected List getAvailableActions() { + doHighlighting(); + return LightQuickFixTestCase.getAvailableActions(getEditor(), getFile()); + } + + @Override + protected Sdk getTestProjectJdk() { + return PluginTestCaseBase.jdkFromIdeaHome(); + } +}