IDEA-136826 Autocomplete fails for static nested annotations
authorpeter <peter@jetbrains.com>
Tue, 24 Feb 2015 09:50:01 +0000 (10:50 +0100)
committerpeter <peter@jetbrains.com>
Tue, 24 Feb 2015 11:21:01 +0000 (12:21 +0100)
java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java
java/java-tests/testData/codeInsight/completion/normal/InnerAnnotation.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/completion/normal/InnerAnnotation_after.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy

index d36c35ca6e739887670d6e9d52c497f9275c6916..90e550a75b307b276bdab9b12fe43a8253406a2c 100644 (file)
@@ -37,9 +37,11 @@ import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.psi.util.PsiUtil;
 import com.intellij.util.Consumer;
 import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
+import java.util.Set;
 
 import static com.intellij.patterns.PsiJavaPatterns.psiClass;
 import static com.intellij.patterns.PsiJavaPatterns.psiElement;
@@ -117,26 +119,52 @@ public class JavaClassNameCompletionContributor extends CompletionContributor {
 
     final boolean pkgContext = JavaCompletionUtil.inSomePackage(insertedElement);
     AllClassesGetter.processJavaClasses(parameters, matcher, filterByScope, new Consumer<PsiClass>() {
-        @Override
-        public void consume(PsiClass psiClass) {
-          if (filter.isAcceptable(psiClass, insertedElement)) {
-            if (!inJavaContext) {
-              consumer.consume(AllClassesGetter.createLookupItem(psiClass, AllClassesGetter.TRY_SHORTENING));
-            } else {
-              for (JavaPsiClassReferenceElement element : createClassLookupItems(psiClass, afterNew,
-                                                                                 JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, new Condition<PsiClass>() {
+      @Override
+      public void consume(PsiClass psiClass) {
+        processClass(psiClass, ContainerUtil.<PsiClass>newHashSet(), "");
+      }
+
+      private void processClass(PsiClass psiClass, Set<PsiClass> visited, String prefix) {
+        if (!visited.add(psiClass)) return;
+
+        boolean isInnerClass = StringUtil.isNotEmpty(prefix);
+        if (isInnerClass && isProcessedIndependently(psiClass)) {
+          return;
+        }
+
+        if (filter.isAcceptable(psiClass, insertedElement)) {
+          if (!inJavaContext) {
+            JavaPsiClassReferenceElement element = AllClassesGetter.createLookupItem(psiClass, AllClassesGetter.TRY_SHORTENING);
+            element.setLookupString(prefix + element.getLookupString());
+            consumer.consume(element);
+          } else {
+            for (JavaPsiClassReferenceElement element : createClassLookupItems(psiClass, afterNew,
+                                                                               JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, new Condition<PsiClass>() {
                 @Override
                 public boolean value(PsiClass psiClass) {
                   return filter.isAcceptable(psiClass, insertedElement) &&
                          AllClassesGetter.isAcceptableInContext(insertedElement, psiClass, filterByScope, pkgContext);
                 }
               })) {
-                consumer.consume(element);
-              }
+              element.setLookupString(prefix + element.getLookupString());
+              consumer.consume(element);
+            }
+          }
+        } else {
+          String name = psiClass.getName();
+          if (name != null) {
+            for (PsiClass innerClass : psiClass.getInnerClasses()) {
+              processClass(innerClass, visited, prefix + name + ".");
             }
           }
         }
-      });
+      }
+
+      private boolean isProcessedIndependently(PsiClass psiClass) {
+        String innerName = psiClass.getName();
+        return innerName != null && matcher.prefixMatches(innerName);
+      }
+    });
   }
 
   static LookupElement highlightIfNeeded(JavaPsiClassReferenceElement element, CompletionParameters parameters) {
diff --git a/java/java-tests/testData/codeInsight/completion/normal/InnerAnnotation.java b/java/java-tests/testData/codeInsight/completion/normal/InnerAnnotation.java
new file mode 100644 (file)
index 0000000..4fd5cc0
--- /dev/null
@@ -0,0 +1,11 @@
+final class MyModule {
+  @Target({FIELD,PARAMETER,METHOD})
+  @Retention(RUNTIME)
+  public static @interface Dependency { }
+}
+
+final class SomeService {
+
+  SomeService(@My<caret>) {
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/completion/normal/InnerAnnotation_after.java b/java/java-tests/testData/codeInsight/completion/normal/InnerAnnotation_after.java
new file mode 100644 (file)
index 0000000..9ad5271
--- /dev/null
@@ -0,0 +1,11 @@
+final class MyModule {
+  @Target({FIELD,PARAMETER,METHOD})
+  @Retention(RUNTIME)
+  public static @interface Dependency { }
+}
+
+final class SomeService {
+
+  SomeService(@MyModule.Dependency<caret>) {
+  }
+}
\ No newline at end of file
index d8abdbd5a9e820e575f80e605974d187de7b2aef..0852028413bf9b12b2b663b754505d57fb4517c7 100644 (file)
@@ -932,6 +932,7 @@ public class ListUtils {
 
   public void testTabReplacesMethodNameWithLocalVariableName() throws Throwable { doTest('\t'); }
   public void testMethodParameterAnnotationClass() throws Throwable { doTest(); }
+  public void testInnerAnnotation() { doTest('\n'); }
   public void testPrimitiveCastOverwrite() throws Throwable { doTest '\t' }
   public void testClassReferenceInFor() throws Throwable { doTest ' ' }
   public void testClassReferenceInFor2() throws Throwable { doTest ' ' }