#KT-1629 fixed Don't import jet.runtime.SharedVar.Int when typing "val a : Int = ..."
Completion testing framework extended with ability to assert tail text.
This commit is contained in:
@@ -30,8 +30,7 @@ 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.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
@@ -41,22 +40,27 @@ import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade;
|
||||
import org.jetbrains.jet.plugin.references.JetSimpleNameReference;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Nikolay Krasko
|
||||
*/
|
||||
public class JetCompletionContributor extends CompletionContributor {
|
||||
|
||||
private static class CompletionSession {
|
||||
public boolean isSomethingAdded = false;
|
||||
public int customInvocationCount = 0;
|
||||
}
|
||||
|
||||
public JetCompletionContributor() {
|
||||
extend(CompletionType.BASIC, PlatformPatterns.psiElement(),
|
||||
new CompletionProvider<CompletionParameters>() {
|
||||
@Override
|
||||
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context,
|
||||
@NotNull CompletionResultSet result) {
|
||||
result.restartCompletionWhenNothingMatches();
|
||||
|
||||
Set<LookupPositionObject> positions = new HashSet<LookupPositionObject>();
|
||||
CompletionSession session = new CompletionSession();
|
||||
session.customInvocationCount = parameters.getInvocationCount();
|
||||
|
||||
PsiElement position = parameters.getPosition();
|
||||
if (!(position.getContainingFile() instanceof JetFile)) {
|
||||
@@ -65,49 +69,74 @@ public class JetCompletionContributor extends CompletionContributor {
|
||||
|
||||
JetSimpleNameReference jetReference = getJetReference(parameters);
|
||||
if (jetReference != null) {
|
||||
completeForReference(parameters, result, position, jetReference, session);
|
||||
|
||||
// Prevent from adding reference variants from standard reference contributor
|
||||
result.stopHere();
|
||||
|
||||
if (isOnlyKeywordCompletion(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldRunTypeCompletionOnly(position, jetReference)) {
|
||||
addClasses(parameters, result);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Object variant : jetReference.getVariants()) {
|
||||
addReferenceVariant(result, variant, positions);
|
||||
}
|
||||
|
||||
String prefix = result.getPrefixMatcher().getPrefix();
|
||||
|
||||
// Try to avoid computing not-imported descriptors for empty prefix
|
||||
if (prefix.isEmpty()) {
|
||||
if (parameters.getInvocationCount() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PsiTreeUtil.getParentOfType(jetReference.getElement(), JetDotQualifiedExpression.class) == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRunTopLevelCompletion(parameters, prefix)) {
|
||||
addClasses(parameters, result);
|
||||
addJetTopLevelFunctions(jetReference.getExpression(), result, position, positions);
|
||||
}
|
||||
|
||||
if (shouldRunExtensionsCompletion(parameters, prefix)) {
|
||||
addJetExtensions(jetReference.getExpression(), result, position);
|
||||
if (!session.isSomethingAdded && session.customInvocationCount == 0) {
|
||||
// Rerun completion if nothing was found
|
||||
session.customInvocationCount = 1;
|
||||
completeForReference(parameters, result, position, jetReference, session);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void completeForReference(
|
||||
@NotNull CompletionParameters parameters,
|
||||
@NotNull CompletionResultSet result,
|
||||
@NotNull PsiElement position,
|
||||
@NotNull JetSimpleNameReference jetReference,
|
||||
@NotNull CompletionSession session
|
||||
) {
|
||||
// Prevent from adding reference variants from standard reference contributor
|
||||
result.stopHere();
|
||||
|
||||
if (isOnlyKeywordCompletion(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldRunTypeCompletionOnly(position, jetReference)) {
|
||||
if (session.customInvocationCount > 0) {
|
||||
addClasses(parameters, result);
|
||||
}
|
||||
else {
|
||||
for (Object variant : jetReference.getVariants()) {
|
||||
if (isTypeDeclaration(variant)) {
|
||||
addReferenceVariant(result, variant, session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (Object variant : jetReference.getVariants()) {
|
||||
addReferenceVariant(result, variant, session);
|
||||
}
|
||||
|
||||
String prefix = result.getPrefixMatcher().getPrefix();
|
||||
|
||||
// Try to avoid computing not-imported descriptors for empty prefix
|
||||
if (prefix.isEmpty()) {
|
||||
if (session.customInvocationCount < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PsiTreeUtil.getParentOfType(jetReference.getElement(), JetDotQualifiedExpression.class) == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRunTopLevelCompletion(parameters, session)) {
|
||||
addClasses(parameters, result);
|
||||
addJetTopLevelFunctions(jetReference.getExpression(), result, position, session);
|
||||
}
|
||||
|
||||
if (shouldRunExtensionsCompletion(parameters, prefix, session)) {
|
||||
addJetExtensions(jetReference.getExpression(), result, position);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOnlyKeywordCompletion(PsiElement position) {
|
||||
return PsiTreeUtil.getParentOfType(position, JetModifierList.class) != null;
|
||||
}
|
||||
@@ -135,18 +164,32 @@ public class JetCompletionContributor extends CompletionContributor {
|
||||
private static void addReferenceVariant(
|
||||
@NotNull CompletionResultSet result,
|
||||
@NotNull Object variant,
|
||||
@NotNull Set<LookupPositionObject> positions) {
|
||||
@NotNull CompletionSession session) {
|
||||
|
||||
if (variant instanceof LookupElement) {
|
||||
addCompletionToResult(result, (LookupElement) variant, positions);
|
||||
addCompletionToResult(result, (LookupElement) variant, session);
|
||||
}
|
||||
else {
|
||||
addCompletionToResult(result, LookupElementBuilder.create(variant.toString()), positions);
|
||||
addCompletionToResult(result, LookupElementBuilder.create(variant.toString()), session);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isTypeDeclaration(@NotNull Object variant) {
|
||||
if (variant instanceof LookupElement) {
|
||||
Object object = ((LookupElement)variant).getObject();
|
||||
if (object instanceof JetLookupObject) {
|
||||
DeclarationDescriptor descriptor = ((JetLookupObject) object).getDescriptor();
|
||||
return (descriptor instanceof ClassDescriptor) ||
|
||||
(descriptor instanceof NamespaceDescriptor) ||
|
||||
(descriptor instanceof TypeParameterDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void addJetTopLevelFunctions(JetSimpleNameExpression expression, @NotNull CompletionResultSet result, @NotNull PsiElement position,
|
||||
@NotNull Set<LookupPositionObject> positions) {
|
||||
@NotNull CompletionSession session) {
|
||||
|
||||
String actualPrefix = result.getPrefixMatcher().getPrefix();
|
||||
|
||||
@@ -161,7 +204,7 @@ public class JetCompletionContributor extends CompletionContributor {
|
||||
for (String name : functionNames) {
|
||||
if (name.contains(actualPrefix)) {
|
||||
for (FunctionDescriptor function : namesCache.getTopLevelFunctionDescriptorsByName(name, expression, scope)) {
|
||||
addCompletionToResult(result, DescriptorLookupConverter.createLookupElement(resolutionContext, function), positions);
|
||||
addCompletionToResult(result, DescriptorLookupConverter.createLookupElement(resolutionContext, function), session);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,8 +237,8 @@ public class JetCompletionContributor extends CompletionContributor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters, String prefix) {
|
||||
if (parameters.getInvocationCount() == 0 && prefix.length() < 3) {
|
||||
private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters, CompletionSession session) {
|
||||
if (session.customInvocationCount == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -212,8 +255,8 @@ public class JetCompletionContributor extends CompletionContributor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean shouldRunExtensionsCompletion(CompletionParameters parameters, String prefix) {
|
||||
if (parameters.getInvocationCount() == 0 && prefix.length() < 3) {
|
||||
private static boolean shouldRunExtensionsCompletion(CompletionParameters parameters, String prefix, CompletionSession session) {
|
||||
if (session.customInvocationCount == 0 && prefix.length() < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -243,36 +286,28 @@ public class JetCompletionContributor extends CompletionContributor {
|
||||
private static void addCompletionToResult(
|
||||
@NotNull CompletionResultSet result,
|
||||
@NotNull LookupElement element,
|
||||
@NotNull Set<LookupPositionObject> positions) {
|
||||
@NotNull CompletionSession session) {
|
||||
|
||||
// LookupPositionObject lookupPosition = getLookupPosition(element);
|
||||
// if (lookupPosition != null) {
|
||||
// if (!positions.contains(lookupPosition)) {
|
||||
// positions.add(lookupPosition);
|
||||
// result.addElement(element);
|
||||
// }
|
||||
//
|
||||
// // There is already an element with same position - ignore duplicate
|
||||
// }
|
||||
// else {
|
||||
if (result.getPrefixMatcher().prefixMatches(element)) {
|
||||
result.addElement(element);
|
||||
// }
|
||||
session.isSomethingAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static LookupPositionObject getLookupPosition(LookupElement element) {
|
||||
Object lookupObject = element.getObject();
|
||||
if (lookupObject instanceof PsiElement) {
|
||||
return new LookupPositionObject((PsiElement) lookupObject);
|
||||
}
|
||||
else if (lookupObject instanceof JetLookupObject) {
|
||||
PsiElement psiElement = ((JetLookupObject) lookupObject).getPsiElement();
|
||||
if (psiElement != null) {
|
||||
return new LookupPositionObject(psiElement);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
//private static LookupPositionObject getLookupPosition(LookupElement element) {
|
||||
// Object lookupObject = element.getObject();
|
||||
// if (lookupObject instanceof PsiElement) {
|
||||
// return new LookupPositionObject((PsiElement) lookupObject);
|
||||
// }
|
||||
// else if (lookupObject instanceof JetLookupObject) {
|
||||
// PsiElement psiElement = ((JetLookupObject) lookupObject).getPsiElement();
|
||||
// if (psiElement != null) {
|
||||
// return new LookupPositionObject(psiElement);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return null;
|
||||
//}
|
||||
|
||||
@Override
|
||||
public void beforeCompletion(@NotNull CompletionInitializationContext context) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package first
|
||||
|
||||
fun firstFun() {
|
||||
val a = In<caret>
|
||||
}
|
||||
|
||||
// RUNTIME: 1
|
||||
// TIME: 0
|
||||
// EXIST: Int~(jet)
|
||||
// ABSENT: Int~(jet.runtime.SharedVar)
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
package org.jetbrains.jet.completion;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import junit.framework.Assert;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -34,6 +39,39 @@ import java.util.List;
|
||||
* @author Nikolay Krasko
|
||||
*/
|
||||
public class ExpectedCompletionUtils {
|
||||
|
||||
public static class CompletionProposal {
|
||||
public static final String TAIL_FLAG = "~";
|
||||
|
||||
private final String lookupString;
|
||||
private final String tailString;
|
||||
|
||||
|
||||
|
||||
public CompletionProposal(@NotNull String lookupString, @Nullable String tailString) {
|
||||
this.lookupString = lookupString;
|
||||
this.tailString = tailString != null ? tailString.trim() : null;
|
||||
}
|
||||
|
||||
public boolean isSuitable(CompletionProposal proposal) {
|
||||
if (proposal.tailString != null) {
|
||||
if (!proposal.tailString.equals(tailString)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return lookupString.equals(proposal.lookupString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (tailString != null) {
|
||||
return lookupString + TAIL_FLAG + tailString;
|
||||
}
|
||||
|
||||
return lookupString;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String EXIST_LINE_PREFIX = "// EXIST:";
|
||||
public static final String ABSENT_LINE_PREFIX = "// ABSENT:";
|
||||
@@ -57,13 +95,30 @@ public class ExpectedCompletionUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] itemsShouldExist(String fileText) {
|
||||
return findListWithPrefix(existLinePrefix, fileText);
|
||||
public CompletionProposal[] itemsShouldExist(String fileText) {
|
||||
return processProposalAssertions(existLinePrefix, fileText);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] itemsShouldAbsent(String fileText) {
|
||||
return findListWithPrefix(absentLinePrefix, fileText);
|
||||
public CompletionProposal[] itemsShouldAbsent(String fileText) {
|
||||
return processProposalAssertions(absentLinePrefix, fileText);
|
||||
}
|
||||
|
||||
public static CompletionProposal[] processProposalAssertions(String prefix, String fileText) {
|
||||
List<CompletionProposal> proposals = new ArrayList<CompletionProposal>();
|
||||
for (String proposalStr : findListWithPrefix(prefix, fileText)) {
|
||||
int tailChar = proposalStr.indexOf(CompletionProposal.TAIL_FLAG);
|
||||
|
||||
if (tailChar > 0) {
|
||||
proposals.add(new CompletionProposal(proposalStr.substring(0, tailChar),
|
||||
proposalStr.substring(tailChar + 1, proposalStr.length())));
|
||||
}
|
||||
else {
|
||||
proposals.add(new CompletionProposal(proposalStr, null));
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayUtil.toObjectArray(proposals, CompletionProposal.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -137,4 +192,57 @@ public class ExpectedCompletionUtils {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static void assertContainsRenderedItems(CompletionProposal[] expected, LookupElement[] items) {
|
||||
List<CompletionProposal> itemsInformation = getItemsInformation(items);
|
||||
|
||||
for (CompletionProposal expectedProposal : expected) {
|
||||
boolean isFound = false;
|
||||
|
||||
for (CompletionProposal proposal : itemsInformation) {
|
||||
if (proposal.isSuitable(expectedProposal)) {
|
||||
isFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue("Expected '" + expectedProposal + "' not found in " + listToString(itemsInformation), isFound);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void assertNotContainsRenderedItems(CompletionProposal[] unexpected,LookupElement[] items) {
|
||||
List<CompletionProposal> itemsInformation = getItemsInformation(items);
|
||||
|
||||
for (CompletionProposal unexpectedProposal : unexpected) {
|
||||
for (CompletionProposal proposal : itemsInformation) {
|
||||
Assert.assertFalse("Unexpected '" + unexpectedProposal + "' presented in " + listToString(itemsInformation),
|
||||
proposal.isSuitable(unexpectedProposal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static List<CompletionProposal> getItemsInformation(LookupElement[] items) {
|
||||
final LookupElementPresentation presentation = new LookupElementPresentation();
|
||||
|
||||
List<CompletionProposal> result = new ArrayList<CompletionProposal>();
|
||||
if (items != null) {
|
||||
for (LookupElement item : items) {
|
||||
item.renderElement(presentation);
|
||||
result.add(new ExpectedCompletionUtils.CompletionProposal(item.getLookupString(), presentation.getTailText()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static String listToString(List<ExpectedCompletionUtils.CompletionProposal> items) {
|
||||
return StringUtil.join(
|
||||
Collections2.transform(items, new Function<CompletionProposal, String>() {
|
||||
@Override
|
||||
public String apply(@Nullable CompletionProposal proposal) {
|
||||
assert proposal != null;
|
||||
return proposal.toString();
|
||||
}
|
||||
}), "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,10 @@ public class JetBasicCompletionTest extends JetCompletionTestBase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testOnlyScopedClassesWithoutExplicit() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testOverloadFunctions() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
package org.jetbrains.jet.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionTestCase;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Nikolay Krasko
|
||||
@@ -41,8 +37,8 @@ public abstract class JetCompletionMultiTestBase extends CompletionTestCase {
|
||||
final String fileText = getFile().getText();
|
||||
final ExpectedCompletionUtils completionUtils = new ExpectedCompletionUtils();
|
||||
|
||||
assertContainsItems(completionUtils.itemsShouldExist(fileText));
|
||||
assertNotContainItems(completionUtils.itemsShouldAbsent(fileText));
|
||||
ExpectedCompletionUtils.assertContainsRenderedItems(completionUtils.itemsShouldExist(fileText), myItems);
|
||||
ExpectedCompletionUtils.assertNotContainsRenderedItems(completionUtils.itemsShouldAbsent(fileText), myItems);
|
||||
|
||||
Integer itemsNumber = completionUtils.getExpectedNumber(fileText);
|
||||
if (itemsNumber != null) {
|
||||
@@ -56,33 +52,4 @@ public abstract class JetCompletionMultiTestBase extends CompletionTestCase {
|
||||
protected void doFileTest() {
|
||||
doFileTest(1);
|
||||
}
|
||||
|
||||
// Copied from com.intellij.codeInsight.completion.LightCompletionTestCase
|
||||
protected void assertContainsItems(final String... expected) {
|
||||
final Set<String> actual = getLookupStrings();
|
||||
for (String s : expected) {
|
||||
assertTrue("Expected '" + s + "' not found in " + actual,
|
||||
actual.contains(s));
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from com.intellij.codeInsight.completion.LightCompletionTestCase
|
||||
protected void assertNotContainItems(final String... unexpected) {
|
||||
final Set<String> actual = getLookupStrings();
|
||||
for (String s : unexpected) {
|
||||
assertFalse("Unexpected '" + s + "' presented in " + actual,
|
||||
actual.contains(s));
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from com.intellij.codeInsight.completion.LightCompletionTestCase
|
||||
private Set<String> getLookupStrings() {
|
||||
final Set<String> actual = new HashSet<String>();
|
||||
if (myItems != null) {
|
||||
for (LookupElement lookupElement : myItems) {
|
||||
actual.add(lookupElement.getLookupString());
|
||||
}
|
||||
}
|
||||
return actual;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,14 +59,14 @@ public abstract class JetCompletionTestBase extends LightCompletionTestCase {
|
||||
|
||||
complete(completionTime == null ? 1 : completionTime);
|
||||
|
||||
final String[] expected = completionUtils.itemsShouldExist(fileText);
|
||||
final String[] unexpected = completionUtils.itemsShouldAbsent(fileText);
|
||||
ExpectedCompletionUtils.CompletionProposal[] expected = completionUtils.itemsShouldExist(fileText);
|
||||
ExpectedCompletionUtils.CompletionProposal[] unexpected = completionUtils.itemsShouldAbsent(fileText);
|
||||
Integer itemsNumber = completionUtils.getExpectedNumber(fileText);
|
||||
|
||||
assertTrue("Should be some assertions about completion", expected.length != 0 || unexpected.length != 0 || itemsNumber != null);
|
||||
|
||||
assertContainsItems(expected);
|
||||
assertNotContainItems(unexpected);
|
||||
ExpectedCompletionUtils.assertContainsRenderedItems(expected, myItems);
|
||||
ExpectedCompletionUtils.assertNotContainsRenderedItems(unexpected, myItems);
|
||||
|
||||
if (itemsNumber != null) {
|
||||
assertEquals("Invalid number of completion items", itemsNumber.intValue(), myItems.length);
|
||||
|
||||
Reference in New Issue
Block a user