- KT-1430 Import popup/type completion does not work for extension functions

- refactoring for auto import tests
This commit is contained in:
Nikolay Krasko
2012-03-07 13:42:06 +04:00
parent a94b77ffc9
commit ec29e06115
18 changed files with 374 additions and 178 deletions
@@ -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<DeclarationDescriptor> getJetCallableExtensions(
@NotNull Condition<String> acceptedNameCondition,
@NotNull JetSimpleNameExpression expression,
@NotNull GlobalSearchScope searchScope
) {
Collection<DeclarationDescriptor> resultDescriptors = new ArrayList<DeclarationDescriptor>();
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<String> extensionFunctionsNames = getAllJetExtensionFunctionsNames(searchScope);
Set<String> functionFQNs = new java.util.HashSet<String>();
// Collect all possible extension function qualified names
for (String name : extensionFunctionsNames) {
if (acceptedNameCondition.value(name)) {
Collection<PsiElement> 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<JetNamedFunction> getJetFunctionsByName(@NonNls @NotNull String name, @NotNull GlobalSearchScope scope) {
return JetShortFunctionNameIndex.getInstance().get(name, project, scope);
}
@@ -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<String> matchPrefixCondition = new Condition<String>() {
@Override
public boolean value(String callableName) {
return prefixMatcher.prefixMatches(callableName);
}
};
Collection<DeclarationDescriptor> 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<String> extensionFunctionsNames = namesCache.getAllJetExtensionFunctionsNames(
GlobalSearchScope.allScope(position.getProject()));
Set<String> functionFQNs = new HashSet<String>();
// Collect all possible extension function qualified names
for (String name : extensionFunctionsNames) {
if (result.getPrefixMatcher().prefixMatches(name)) {
Collection<PsiElement> 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();
@@ -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<JetSimpleNameExpression>
return Collections.emptyList();
}
final String referenceName = element.getReferencedName();
if (!StringUtil.isNotEmpty(referenceName)) {
return Collections.emptyList();
}
assert referenceName != null;
final ArrayList<String> result = new ArrayList<String>();
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<String> getJetTopLevelFunctions(@NotNull JetSimpleNameExpression expression, @NotNull Project project) {
final String referenceName = expression.getReferencedName();
if (referenceName == null) {
return Collections.emptyList();
}
private static Collection<String> getJetTopLevelFunctions(@NotNull String referenceName, @NotNull Project project) {
final Collection<JetNamedFunction> namedFunctions =
JetCacheManager.getInstance(project).getNamesCache().getTopLevelFunctionsByName(
referenceName, GlobalSearchScope.allScope(project));
@@ -104,16 +112,35 @@ public class ImportClassAndFunFix extends JetHintAction<JetSimpleNameExpression>
});
}
private static Collection<String> getJetExtensionFunctions(
@NotNull final String referenceName,
@NotNull JetSimpleNameExpression expression,
@NotNull Project project
) {
JetShortNamesCache namesCache = JetCacheManager.getInstance(project).getNamesCache();
Collection<DeclarationDescriptor> jetCallableExtensions = namesCache.getJetCallableExtensions(
new Condition<String>() {
@Override
public boolean value(String callableExtensionName) {
return callableExtensionName.equals(referenceName);
}
},
expression,
GlobalSearchScope.allScope(project));
return Collections2.transform(jetCallableExtensions, new Function<DeclarationDescriptor, String>() {
@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<String> getClassNames(@NotNull JetSimpleNameExpression expression, @NotNull Project project) {
final String referenceName = expression.getReferencedName();
if (referenceName == null) {
return Collections.emptyList();
}
public static List<String> getClassNames(@NotNull String referenceName, @NotNull Project project) {
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
Set<String> possibleResolveNames = Sets.newHashSet();
possibleResolveNames.addAll(JetCacheManager.getInstance(project).getNamesCache().getFQNamesByName(referenceName, scope));
@@ -8,6 +8,6 @@ fun firstFun() {
}
// RUNTIME: 1
// TIME: 2
// TIME: 1
// EXIST: toLinkedList
// NUMBER: 1
@@ -0,0 +1,9 @@
// "Import Class" "true"
package testingExtensionFunctionsImport
import testingExtensionFunctionsImport.data.someFun
fun some() {
val str = ""
str.someFun()
}
@@ -0,0 +1,7 @@
// "Import Class" "true"
package testingExtensionFunctionsImport
fun some() {
val str = ""
str.<caret>someFun()
}
@@ -0,0 +1,4 @@
package testingExtensionFunctionsImport.data
fun String.someFun() {
}
@@ -109,8 +109,6 @@ public class JetBasicCompletionTest extends JetCompletionTestBase {
doTest();
}
@Override
protected String getTestDataPath() {
return new File(PluginTestCaseBase.getTestDataPathBase(), "/completion/basic").getPath() +
@@ -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
@@ -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<String> 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/";
}
}
@@ -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<String> getTestFileNames() {
return getFileNames(getTestFiles());
}
public void doTest() {
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
@Override
public void run() {
try {
final Pair<String, Boolean> 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<IntentionAction> actions = getAvailableActions();
List<String> texts = new ArrayList<String>();
for (IntentionAction intentionAction : actions) {
texts.add(intentionAction.getText());
}
Collection<HighlightInfo> 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<File> 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<File> allTestFiles = Arrays.asList(dir.listFiles(resultFilter));
final Collection<File> mainFiles = Collections2.filter(allTestFiles, new Predicate<File>() {
@Override
public boolean apply(@Nullable File file) {
@@ -168,7 +100,7 @@ public class JetPsiCheckerMultifileTest extends DaemonAnalyzerTestCase {
return fileResult;
}
protected static List<String> getFileNames(List<File> files) {
return Lists.newArrayList(Collections2.transform(files, new Function<File, String>() {
@Override
@@ -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<String, Boolean> 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<IntentionAction> actions = getAvailableActions();
List<String> texts = new ArrayList<String>();
for (IntentionAction intentionAction : actions) {
texts.add(intentionAction.getText());
}
Collection<HighlightInfo> 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<String> getTestFileNames();
protected List<IntentionAction> getAvailableActions() {
doHighlighting();
return LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
}
@Override
protected Sdk getTestProjectJdk() {
return PluginTestCaseBase.jdkFromIdeaHome();
}
}