search for unused property only if it's name is not frequent
authorAlexey Kudravtsev <cdr@intellij.com>
Fri, 12 Mar 2010 07:37:12 +0000 (10:37 +0300)
committerAlexey Kudravtsev <cdr@intellij.com>
Fri, 12 Mar 2010 07:37:12 +0000 (10:37 +0300)
java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java
platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexCacheManagerImpl.java
platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java
plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java

index 83ac2642fd4e2305736a8d003b4c4da4e48ab2bf..7c60ae78f1fbe95a8deba506a0ab954a17090327 100644 (file)
@@ -55,14 +55,12 @@ import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
 import com.intellij.psi.*;
 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
 import com.intellij.psi.impl.PsiClassImplUtil;
-import com.intellij.psi.impl.PsiManagerEx;
-import com.intellij.psi.impl.cache.CacheManager;
+import com.intellij.psi.impl.search.PsiSearchHelperImpl;
 import com.intellij.psi.impl.source.jsp.jspJava.JspxImportStatement;
 import com.intellij.psi.jsp.JspFile;
 import com.intellij.psi.jsp.JspSpiUtil;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.search.SearchScope;
-import com.intellij.psi.search.UsageSearchContext;
 import com.intellij.psi.search.searches.MethodReferencesSearch;
 import com.intellij.psi.search.searches.OverridingMethodsSearch;
 import com.intellij.psi.search.searches.ReferencesSearch;
@@ -523,23 +521,16 @@ public class PostHighlightingPass extends TextEditorHighlightingPass {
     if (name == null) return false;
     SearchScope useScope = member.getUseScope();
     if (!(useScope instanceof GlobalSearchScope)) return false;
-    final int[] count = {0};
     GlobalSearchScope scope = (GlobalSearchScope)useScope;
     // some classes may have references from within XML outside dependent modules, e.g. our actions
     if (member instanceof PsiClass) scope = scope.uniteWith(GlobalSearchScope.projectScope(myProject));
 
-    CacheManager cacheManager = ((PsiManagerEx)myFile.getManager()).getCacheManager();
-    if (!cacheManager.processFilesWithWord(new Processor<PsiFile>() {
-      public boolean process(PsiFile file) {
-        if (file == myFile) return true;
-        count[0]++;
-        return count[0] <= 10;
-      }
-    }, name, UsageSearchContext.ANY, scope, true)) return false;
+    PsiSearchHelperImpl.Result cheapEnough = ((PsiSearchHelperImpl)myFile.getManager().getSearchHelper()).isItCheapEnoughToSearch(myFile, name, scope);
+    if (cheapEnough == PsiSearchHelperImpl.Result.TOO_MANY_OCCURRENCES) return false;
 
     //search usages if it cheap
     //if count is 0 there is no usages since we've called myRefCountHolder.isReferenced() before
-    if (count[0] == 0 && !canbeReferencedViaWeirdNames(member)) return true;
+    if (cheapEnough == PsiSearchHelperImpl.Result.ZERO_OCCURRENCES && !canbeReferencedViaWeirdNames(member)) return true;
 
     Query<PsiReference> query = member instanceof PsiMethod
                                 ? MethodReferencesSearch.search((PsiMethod)member, scope, true)
index 9585b99a6bee419d1211512c4943346b1138d2cd..cecbef3dcdaaf4b1d5da1d68ee6461488c56a07d 100644 (file)
@@ -96,6 +96,8 @@ public class IndexCacheManagerImpl implements CacheManager{
       }
     });
 
+    if (vFiles.isEmpty()) return true;
+
     final ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
 
     final Processor<VirtualFile> virtualFileProcessor = new Processor<VirtualFile>() {
@@ -117,7 +119,7 @@ public class IndexCacheManagerImpl implements CacheManager{
 
 
     // IMPORTANT!!!
-    // Since implementation of virtualFileProcessor.process() may call indices dierctly or indirectly,
+    // Since implementation of virtualFileProcessor.process() may call indices directly or indirectly,
     // we cannot call it inside FileBasedIndex.processValues() method
     // If we do, deadlocks are possible (IDEADEV-42137). So first we obtain files with the word specified,
     // and then process them not holding indices' read lock.
index d8fd4fa9944020029f2da9f30e84ef04332e0469..e24fdb3b6ad7fe893179748fc02a8d0a2742f8ab 100644 (file)
@@ -20,7 +20,6 @@ import com.intellij.concurrency.JobUtil;
 import com.intellij.ide.todo.TodoConfiguration;
 import com.intellij.lang.LanguageParserDefinitions;
 import com.intellij.lang.ParserDefinition;
-import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.progress.ProcessCanceledException;
@@ -34,6 +33,7 @@ import com.intellij.psi.*;
 import com.intellij.psi.impl.PsiManagerEx;
 import com.intellij.psi.search.*;
 import com.intellij.psi.search.searches.IndexPatternSearch;
+import com.intellij.util.CommonProcessors;
 import com.intellij.util.Processor;
 import com.intellij.util.text.StringSearcher;
 import gnu.trove.THashSet;
@@ -214,36 +214,11 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
     myManager.startBatchFilesProcessingMode();
 
     try {
-      List<String> words = StringUtil.getWordsIn(searcher.getPattern());
-      if (words.isEmpty()) return true;
-      Set<PsiFile> fileSet = new THashSet<PsiFile>();
-      Set<PsiFile> copy = new THashSet<PsiFile>();
-      final Application application = ApplicationManager.getApplication();
-      for (final String word : words) {
-        PsiFile[] psiFiles = application.runReadAction(new Computable<PsiFile[]>() {
-          public PsiFile[] compute() {
-            return myManager.getCacheManager().getFilesWithWord(word, searchContext, scope, caseSensitively);
-          }
-        });
-        if (fileSet.isEmpty()) {
-          fileSet.addAll(Arrays.asList(psiFiles));
-        }
-        else {
-          for (PsiFile psiFile : psiFiles) {
-            if (fileSet.contains(psiFile)) {
-              copy.add(psiFile);
-            }
-          }
-          Set<PsiFile> tmp = copy;
-          copy = fileSet;
-          fileSet = tmp;
-          copy.clear();
-        }
-        if (fileSet.isEmpty()) break;
-      }
+      String text = searcher.getPattern();
+      List<PsiFile> fileSet = getFilesWithText(scope, searchContext, caseSensitively, text);
 
       if (progress != null) {
-        progress.setText(PsiBundle.message("psi.search.for.word.progress", searcher.getPattern()));
+        progress.setText(PsiBundle.message("psi.search.for.word.progress", text));
       }
 
       final AtomicInteger counter = new AtomicInteger(0);
@@ -251,35 +226,36 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
       final AtomicBoolean pceThrown = new AtomicBoolean(false);
 
       final int size = fileSet.size();
-      boolean completed = JobUtil.invokeConcurrentlyUnderMyProgress(new ArrayList<PsiFile>(fileSet), new Processor<PsiFile>() {
+      boolean completed = JobUtil.invokeConcurrentlyUnderMyProgress(fileSet, new Processor<PsiFile>() {
         public boolean process(final PsiFile file) {
-          if (file instanceof PsiBinaryFile) return true;
-          file.getViewProvider().getContents(); // load contents outside readaction
-          ApplicationManager.getApplication().runReadAction(new Runnable() {
-            public void run() {
-              try {
-                PsiElement[] psiRoots = file.getPsiRoots();
-                Set<PsiElement> processed = new HashSet<PsiElement>(psiRoots.length * 2, (float)0.5);
-                for (PsiElement psiRoot : psiRoots) {
-                  ProgressManager.checkCanceled();
-                  if (!processed.add(psiRoot)) continue;
-                  if (!LowLevelSearchUtil.processElementsContainingWordInElement(processor, psiRoot, searcher, false)) {
-                    canceled.set(true);
-                    return;
+          if (!(file instanceof PsiBinaryFile)) {
+            file.getViewProvider().getContents(); // load contents outside readaction
+            ApplicationManager.getApplication().runReadAction(new Runnable() {
+              public void run() {
+                try {
+                  PsiElement[] psiRoots = file.getPsiRoots();
+                  Set<PsiElement> processed = new HashSet<PsiElement>(psiRoots.length * 2, (float)0.5);
+                  for (PsiElement psiRoot : psiRoots) {
+                    ProgressManager.checkCanceled();
+                    if (!processed.add(psiRoot)) continue;
+                    if (!LowLevelSearchUtil.processElementsContainingWordInElement(processor, psiRoot, searcher, false)) {
+                      canceled.set(true);
+                      return;
+                    }
                   }
+                  myManager.dropResolveCaches();
                 }
-                if (progress != null) {
-                  double fraction = (double)counter.incrementAndGet() / size;
-                  progress.setFraction(fraction);
+                catch (ProcessCanceledException e) {
+                  canceled.set(true);
+                  pceThrown.set(true);
                 }
-                myManager.dropResolveCaches();
-              }
-              catch (ProcessCanceledException e) {
-                canceled.set(true);
-                pceThrown.set(true);
               }
-            }
-          });
+            });
+          }
+          if (progress != null) {
+            double fraction = (double)counter.incrementAndGet() / size;
+            progress.setFraction(fraction);
+          }
           return !canceled.get();
         }
       }, "Process usages in files");
@@ -298,6 +274,59 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
     }
   }
 
+  private List<PsiFile> getFilesWithText(final GlobalSearchScope scope,
+                                        final short searchContext,
+                                        final boolean caseSensitively,
+                                        String text) {
+    List<PsiFile> result = new ArrayList<PsiFile>();
+    if (!processFilesWithText(scope, searchContext, caseSensitively, text, new CommonProcessors.CollectProcessor<PsiFile>(result))) return Collections.emptyList();
+    return result;
+  }
+
+  private boolean processFilesWithText(final GlobalSearchScope scope,
+                                        final short searchContext,
+                                        final boolean caseSensitively,
+                                        String text,
+                                        final Processor<PsiFile> processor) {
+    List<String> words = StringUtil.getWordsIn(text);
+    if (words.isEmpty()) return true;
+    Collections.sort(words, new Comparator<String>() {
+      public int compare(String o1, String o2) {
+        return o2.length() - o1.length();
+      }
+    });
+    final Set<PsiFile> fileSet;
+    if (words.size() > 1) {
+      fileSet = new THashSet<PsiFile>();
+      Set<PsiFile> copy = new THashSet<PsiFile>();
+      for (int i = 0; i < words.size() - 1; i++) {
+        ProgressManager.checkCanceled();
+        final String word = words.get(i);
+        myManager.getCacheManager().processFilesWithWord(new CommonProcessors.CollectProcessor<PsiFile>(copy), word, searchContext, scope, caseSensitively);
+        if (i == 0) {
+          fileSet.addAll(copy);
+        }
+        else {
+          fileSet.retainAll(copy);
+        }
+        copy.clear();
+        if (fileSet.isEmpty()) break;
+      }
+      if (fileSet.isEmpty()) return true;
+    }
+    else {
+      fileSet = null;
+    }
+    return myManager.getCacheManager().processFilesWithWord(new Processor<PsiFile>() {
+      public boolean process(PsiFile psiFile) {
+        if (fileSet != null && !fileSet.contains(psiFile)) {
+          return true;
+        }
+        return processor.process(psiFile);
+      }
+    }, words.get(words.size()-1), searchContext, scope, caseSensitively);
+  }
+
   @NotNull
   public PsiFile[] findFilesWithPlainTextWords(@NotNull String word) {
     return myManager.getCacheManager().getFilesWithWord(word,
@@ -395,4 +424,25 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
     myManager.getCacheManager().processFilesWithWord(processor, word, UsageSearchContext.IN_STRINGS, scope, true);
   }
 
+  public static enum Result {
+    ZERO_OCCURRENCES, FEW_OCCURRENCES, TOO_MANY_OCCURRENCES
+  }
+  public Result isItCheapEnoughToSearch(@Nullable final PsiFile fileToIgnoreOccurencesIn, @NotNull String name, @NotNull GlobalSearchScope scope) {
+    final int[] count = {0};
+    if (!processFilesWithText(scope, UsageSearchContext.ANY, true, name, new Processor<PsiFile>() {
+      public boolean process(PsiFile file) {
+        if (file == fileToIgnoreOccurencesIn) return true;
+        synchronized (count) {
+          count[0]++;
+          return count[0] <= 10;
+        }
+      }
+    })) {
+      return Result.TOO_MANY_OCCURRENCES;
+    }
+
+    synchronized (count) {
+      return count[0] == 0 ? Result.ZERO_OCCURRENCES : Result.FEW_OCCURRENCES;
+    }
+  }
 }
index 5ef258fc51b9ba200c009da2f9f32f0b060de5fd..63720e08e1ec5bc6311c6e3b1fd8f07431d7b464 100644 (file)
@@ -29,6 +29,7 @@ import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiReference;
+import com.intellij.psi.impl.search.PsiSearchHelperImpl;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.search.searches.ReferencesSearch;
 import com.intellij.util.Processor;
@@ -51,7 +52,7 @@ public class UnusedPropertyInspection extends PropertySuppressableInspectionBase
     return "UnusedProperty";
   }
 
-  public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) {
+  public ProblemDescriptor[] checkFile(@NotNull final PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) {
     if (!(file instanceof PropertiesFile)) return null;
     final List<Property> properties = ((PropertiesFile)file).getProperties();
     Module module = ModuleUtil.findModuleForPsiElement(file);
@@ -67,7 +68,11 @@ public class UnusedPropertyInspection extends PropertySuppressableInspectionBase
           original.setText(PropertiesBundle.message("searching.for.property.key.progress.text", property.getUnescapedKey()));
         }
 
-        final PsiReference usage = ReferencesSearch.search(property, searchScope, false).findFirst();
+        PsiSearchHelperImpl.Result cheapEnough = ((PsiSearchHelperImpl)file.getManager().getSearchHelper()).isItCheapEnoughToSearch(file, property.getName(), searchScope);
+        if (cheapEnough == PsiSearchHelperImpl.Result.TOO_MANY_OCCURRENCES) return true;
+
+        final PsiReference usage = cheapEnough == PsiSearchHelperImpl.Result.ZERO_OCCURRENCES ? null :
+                                   ReferencesSearch.search(property, searchScope, false).findFirst();
         if (usage == null) {
           final ASTNode propertyNode = property.getNode();
           assert propertyNode != null;