提交 4311bcc9 编写于 作者: N Nikolay Krasko

#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.
上级 90e5510b
......@@ -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,47 +69,72 @@ 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 (!session.isSomethingAdded && session.customInvocationCount == 0) {
// Rerun completion if nothing was found
session.customInvocationCount = 1;
completeForReference(parameters, result, position, jetReference, session);
}
}
}
});
}
if (shouldRunTypeCompletionOnly(position, jetReference)) {
addClasses(parameters, result);
return;
}
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();
for (Object variant : jetReference.getVariants()) {
addReferenceVariant(result, variant, positions);
}
if (isOnlyKeywordCompletion(position)) {
return;
}
String prefix = result.getPrefixMatcher().getPrefix();
if (shouldRunTypeCompletionOnly(position, jetReference)) {
if (session.customInvocationCount > 0) {
addClasses(parameters, result);
}
else {
for (Object variant : jetReference.getVariants()) {
if (isTypeDeclaration(variant)) {
addReferenceVariant(result, variant, session);
}
}
}
// Try to avoid computing not-imported descriptors for empty prefix
if (prefix.isEmpty()) {
if (parameters.getInvocationCount() < 2) {
return;
}
return;
}
if (PsiTreeUtil.getParentOfType(jetReference.getElement(), JetDotQualifiedExpression.class) == null) {
return;
}
}
for (Object variant : jetReference.getVariants()) {
addReferenceVariant(result, variant, session);
}
if (shouldRunTopLevelCompletion(parameters, prefix)) {
addClasses(parameters, result);
addJetTopLevelFunctions(jetReference.getExpression(), result, position, positions);
}
String prefix = result.getPrefixMatcher().getPrefix();
if (shouldRunExtensionsCompletion(parameters, prefix)) {
addJetExtensions(jetReference.getExpression(), result, position);
}
}
}
});
// 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) {
......@@ -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,37 +286,29 @@ public class JetCompletionContributor extends CompletionContributor {
private static void addCompletionToResult(
@NotNull CompletionResultSet result,
@NotNull LookupElement element,
@NotNull Set<LookupPositionObject> positions) {
// 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 {
result.addElement(element);
// }
}
@NotNull CompletionSession session) {
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);
}
if (result.getPrefixMatcher().prefixMatches(element)) {
result.addElement(element);
session.isSomethingAdded = true;
}
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) {
super.beforeCompletion(context); //To change body of overridden methods use File | Settings | File Templates.
......
package first
fun firstFun() {
val a = In<caret>
}
// RUNTIME: 1
// TIME: 0
// EXIST: Int~(jet)
// ABSENT: Int~(jet.runtime.SharedVar)
\ No newline at end of file
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册