diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ClassByNameMerger.java b/java/java-impl/src/com/intellij/codeInsight/completion/ClassByNameMerger.java new file mode 100644 index 000000000000..a3bff10b30a0 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ClassByNameMerger.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2011 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 com.intellij.codeInsight.completion; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.util.Consumer; + +/** +* @author peter +*/ +public class ClassByNameMerger implements Consumer { + private int number = 0; + private LookupElement lastElement; + private final boolean myShouldMerge; + private final CompletionResultSet myResult; + + public ClassByNameMerger(boolean shouldMerge, CompletionResultSet result) { + myShouldMerge = shouldMerge; + myResult = result; + } + + @Override + public void consume(LookupElement element) { + if (!myShouldMerge) { + myResult.addElement(element); + return; + } + + if (lastElement != null) { + if (lastElement.getLookupString().equals(element.getLookupString())) { + number++; + lastElement = LookupElementBuilder.create(element.getLookupString()).setTailText(" (" + number + " variants...)", true); + return; + } + + myResult.addElement(lastElement); + } + lastElement = element; + number = 1; + } + + public void finishedClassProcessing() { + if (lastElement != null) { + myResult.addElement(lastElement); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java index f0fea262e638..e3c81663a4fc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java @@ -36,11 +36,17 @@ public class JavaNoVariantsDelegator extends CompletionContributor { }; result.runRemainingContributors(parameters, passResult); + if (!empty.get() && parameters.getInvocationCount() == 0) { + result.restartCompletionWhenNothingMatches(); + } + if (empty.get()) { if (parameters.getCompletionType() == CompletionType.BASIC && - parameters.getInvocationCount() == 1 && + parameters.getInvocationCount() <= 1 && JavaCompletionContributor.mayStartClassName(result, false) && JavaCompletionContributor.isClassNamePossible(parameters.getPosition())) { + final ClassByNameMerger merger = new ClassByNameMerger(parameters.getInvocationCount() == 0, result); + JavaClassNameCompletionContributor.addAllClasses(parameters, JavaCompletionSorting.addJavaSorting(parameters, result), true, new Consumer() { @Override @@ -49,9 +55,13 @@ public class JavaNoVariantsDelegator extends CompletionContributor { if (classElement != null) { classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); } - result.addElement(element); + + merger.consume(classElement); } }); + + merger.finishedClassProcessing(); + } else if (parameters.getCompletionType() == CompletionType.SMART && parameters.getInvocationCount() == 2) { result.runRemainingContributors(parameters.withInvocationCount(3), passResult); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index 6fbd35e6d74d..f91c7fe5dae8 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -1095,5 +1095,20 @@ class Foo {{ assert end == myFixture.editor.caretModel.logicalPosition } + public void testNonImportedClass() { + myFixture.addClass("package foo; public class Abcdefg {}") + myFixture.configureByText 'a.java', 'class Foo extends ' + type 'Abcde ' + myFixture.checkResult 'import foo.Abcdefg;\n\nclass Foo extends Abcdefg ' + } + + public void testTwoNonImportedClasses() { + myFixture.addClass("package foo; public class Abcdefg {}") + myFixture.addClass("package bar; public class Abcdefg {}") + myFixture.configureByText 'a.java', 'class Foo extends ' + type 'Abcde ' + myFixture.checkResult 'class Foo extends Abcdefg ' + } + } diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java index 36f3f45addd2..9605aadad92b 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionResultSet.java @@ -99,4 +99,6 @@ public abstract class CompletionResultSet { } public abstract void restartCompletionOnPrefixChange(ElementPattern prefixCondition); + + public abstract void restartCompletionWhenNothingMatches(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index dba42acbcdfd..558af206731d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -501,7 +501,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } private boolean hideAutopopupIfMeaningless() { - if (isAutopopupCompletion() && !myLookup.isSelectionTouched() && !myLookup.isCalculating()) { + if (!myLookup.isLookupDisposed() && isAutopopupCompletion() && !myLookup.isSelectionTouched() && !myLookup.isCalculating()) { myLookup.refreshUi(true); final List items = myLookup.getItems(); @@ -618,7 +618,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); LOG.assertTrue(this == current, current + "!=" + this); - if (isAutopopupCompletion() && (myLookup.isLookupDisposed() || !myLookup.isShown() || hideAutopopupIfMeaningless())) { + if (isAutopopupCompletion() && !myLookup.isShown()) { if (CompletionServiceImpl.getCompletionService().getCurrentCompletion() == this) { closeAndFinish(true); } @@ -627,6 +627,8 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement return; } + hideAutopopupIfMeaningless(); + final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(this, myEditor); CompletionServiceImpl.setCompletionPhase(phase); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java index 337a2d3cfef4..279f08cfe12c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java @@ -181,6 +181,14 @@ public class CompletionServiceImpl extends CompletionService{ indicator.addWatchedPrefix(myTextBeforePosition.length() - getPrefixMatcher().getPrefix().length(), prefixCondition); } } + + @Override + public void restartCompletionWhenNothingMatches() { + final CompletionProgressIndicator indicator = getCompletionService().getCurrentCompletion(); + if (indicator != null) { + indicator.getLookup().setStartCompletionWhenNothingMatches(true); + } + } } public static boolean assertPhase(Class... possibilities) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupTypedHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupTypedHandler.java index fd3e335523b2..fea1130d9a8f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupTypedHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupTypedHandler.java @@ -84,7 +84,12 @@ public class LookupTypedHandler extends TypedHandlerDelegate { }); lookup.appendPrefix(charTyped); if (lookup.isStartCompletionWhenNothingMatches() && lookup.getItems().isEmpty()) { - AutoPopupController.getInstance(editor.getProject()).scheduleAutoPopup(editor, null); + final CompletionProgressIndicator completion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); + if (completion != null) { + completion.scheduleRestart(); + } else { + AutoPopupController.getInstance(editor.getProject()).scheduleAutoPopup(editor, null); + } } AutoHardWrapHandler.getInstance().wrapLineIfNecessary(editor, DataManager.getInstance().getDataContext(editor.getContentComponent()), modificationStamp); diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index a7f66cf5555c..2a285856c646 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -235,6 +235,7 @@ implementationClass="org.jetbrains.plugins.groovy.codeInsight.GroovyLineMarkerProvider"/> + () { + @Override + public void consume(LookupElement element) { + result.addElement(element); + } + }, inheritors); + } + + public static void addAllClasses(CompletionParameters parameters, CompletionResultSet result, final Consumer consumer, final InheritorsHolder inheritors) { final PsiElement position = parameters.getPosition(); final ElementFilter filter = getClassFilter(position); AllClassesGetter.processJavaClasses(parameters, result.getPrefixMatcher(), parameters.getInvocationCount() <= 1, new Consumer() { @Override public void consume(PsiClass psiClass) { if (!inheritors.alreadyProcessed(psiClass) && filter.isAcceptable(psiClass, position)) { - result.addElement(GroovyCompletionUtil.createClassLookupItem(psiClass)); + consumer.consume(GroovyCompletionUtil.createClassLookupItem(psiClass)); } } }); @@ -311,8 +320,7 @@ public class GroovyCompletionContributor extends CompletionContributor { if (reference.getQualifier() == null) { GroovySmartCompletionContributor.addExpectedClassMembers(parameters, result); - if (!PsiJavaPatterns.psiElement().inside(GrImportStatement.class).accepts(position) && - JavaCompletionContributor.mayStartClassName(result, parameters.isRelaxedMatching())) { + if (isClassNamePossible(position) && JavaCompletionContributor.mayStartClassName(result, parameters.isRelaxedMatching())) { if (JavaCompletionContributor.mayShowAllClasses(parameters)) { addAllClasses(parameters, result, inheritors); } else { @@ -350,6 +358,12 @@ public class GroovyCompletionContributor extends CompletionContributor { } + public static boolean isClassNamePossible(PsiElement position) { + PsiElement parent = position.getParent(); + return parent instanceof GrReferenceElement && ((GrReferenceElement)parent).getQualifier() == null && + !PsiJavaPatterns.psiElement().inside(GrImportStatement.class).accepts(position); + } + private static void addUnfinishedMethodTypeParameters(PsiElement position, CompletionResultSet result) { final ProcessingContext context = new ProcessingContext(); if (PsiJavaPatterns.psiElement().inside( diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java new file mode 100644 index 000000000000..034c164852fe --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2011 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.plugins.groovy.lang.completion; + +import com.intellij.codeInsight.completion.*; +import com.intellij.codeInsight.lookup.AutoCompletionPolicy; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.util.Ref; +import com.intellij.util.Consumer; + +/** + * @author peter + */ +public class GroovyNoVariantsDelegator extends CompletionContributor { + + @Override + public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) { + final Ref empty = Ref.create(true); + Consumer passResult = new Consumer() { + public void consume(final CompletionResult lookupElement) { + empty.set(false); + result.passResult(lookupElement); + } + }; + result.runRemainingContributors(parameters, passResult); + + if (!empty.get() && parameters.getInvocationCount() == 0) { + result.restartCompletionWhenNothingMatches(); + } + + if (empty.get()) { + if (parameters.getCompletionType() == CompletionType.BASIC && + parameters.getInvocationCount() <= 1 && + JavaCompletionContributor.mayStartClassName(result, false) && + GroovyCompletionContributor.isClassNamePossible(parameters.getPosition())) { + final ClassByNameMerger merger = new ClassByNameMerger(parameters.getInvocationCount() == 0, result); + + GroovyCompletionContributor.addAllClasses(parameters, result, + new Consumer() { + @Override + public void consume(LookupElement element) { + JavaPsiClassReferenceElement classElement = element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY); + if (classElement != null) { + classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + } + merger.consume(classElement); + } + }, new InheritorsHolder(parameters.getPosition(), result)); + + merger.finishedClassProcessing(); + + } + } + } + +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy index 706ebf192c93..d5a139ecb586 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy @@ -197,4 +197,20 @@ class GroovyAutoPopupTest extends CompletionAutoPopupTestCase { type ' in c' assert lookup.focused } + + public void testNonImportedClass() { + myFixture.addClass("package foo; public class Abcdefg {}") + myFixture.configureByText 'a.groovy', '' + type 'Abcde ' + myFixture.checkResult 'import foo.Abcdefg\n\nAbcdefg ' + } + + public void testTwoNonImportedClasses() { + myFixture.addClass("package foo; public class Abcdefg {}") + myFixture.addClass("package bar; public class Abcdefg {}") + myFixture.configureByText 'a.groovy', '' + type 'Abcde ' + myFixture.checkResult 'Abcdefg ' + } + }