Retooling incremental tests to the new builders (compiler server).
[idea/community.git] / platform / lang-impl / src / com / intellij / find / impl / FindInProjectUtil.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.find.impl;
18
19 import com.intellij.find.*;
20 import com.intellij.find.ngrams.TrigramIndex;
21 import com.intellij.navigation.ItemPresentation;
22 import com.intellij.openapi.actionSystem.DataContext;
23 import com.intellij.openapi.actionSystem.LangDataKeys;
24 import com.intellij.openapi.actionSystem.PlatformDataKeys;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ApplicationNamesInfo;
27 import com.intellij.openapi.editor.Document;
28 import com.intellij.openapi.editor.Editor;
29 import com.intellij.openapi.fileEditor.FileDocumentManager;
30 import com.intellij.openapi.fileEditor.FileEditor;
31 import com.intellij.openapi.fileTypes.FileTypeManager;
32 import com.intellij.openapi.module.Module;
33 import com.intellij.openapi.module.ModuleManager;
34 import com.intellij.openapi.progress.ProcessCanceledException;
35 import com.intellij.openapi.progress.ProgressIndicator;
36 import com.intellij.openapi.progress.ProgressManager;
37 import com.intellij.openapi.project.DumbService;
38 import com.intellij.openapi.project.IndexNotReadyException;
39 import com.intellij.openapi.project.Project;
40 import com.intellij.openapi.project.ProjectUtil;
41 import com.intellij.openapi.roots.*;
42 import com.intellij.openapi.roots.impl.FileIndexImplUtil;
43 import com.intellij.openapi.ui.MessageType;
44 import com.intellij.openapi.util.Computable;
45 import com.intellij.openapi.util.Factory;
46 import com.intellij.openapi.util.Pair;
47 import com.intellij.openapi.util.TextRange;
48 import com.intellij.openapi.util.registry.Registry;
49 import com.intellij.openapi.util.text.StringUtil;
50 import com.intellij.openapi.util.text.TrigramBuilder;
51 import com.intellij.openapi.vfs.JarFileSystem;
52 import com.intellij.openapi.vfs.LocalFileSystem;
53 import com.intellij.openapi.vfs.VirtualFile;
54 import com.intellij.openapi.vfs.VirtualFileFilter;
55 import com.intellij.openapi.wm.ToolWindowId;
56 import com.intellij.openapi.wm.ToolWindowManager;
57 import com.intellij.psi.*;
58 import com.intellij.psi.impl.cache.CacheManager;
59 import com.intellij.psi.search.*;
60 import com.intellij.usageView.UsageInfo;
61 import com.intellij.usages.*;
62 import com.intellij.util.CommonProcessors;
63 import com.intellij.util.Function;
64 import com.intellij.util.PatternUtil;
65 import com.intellij.util.Processor;
66 import com.intellij.util.containers.ContainerUtil;
67 import com.intellij.util.indexing.FileBasedIndex;
68 import gnu.trove.THashSet;
69 import gnu.trove.TIntHashSet;
70 import gnu.trove.TIntIterator;
71 import org.intellij.lang.annotations.Language;
72 import org.jetbrains.annotations.NotNull;
73 import org.jetbrains.annotations.Nullable;
74
75 import javax.swing.*;
76 import java.io.File;
77 import java.util.*;
78 import java.util.regex.Pattern;
79
80 public class FindInProjectUtil {
81   private static final int USAGES_PER_READ_ACTION = 100;
82   private static final int FILES_SIZE_LIMIT = 70 * 1024 * 1024; // megabytes.
83   private static final int SINGLE_FILE_SIZE_LIMIT = 5 * 1024 * 1024; // megabytes.
84
85   private FindInProjectUtil() {}
86
87   public static void setDirectoryName(FindModel model, DataContext dataContext) {
88     PsiElement psiElement;
89     try {
90       psiElement = LangDataKeys.PSI_ELEMENT.getData(dataContext);
91     }
92     catch (IndexNotReadyException e) {
93       psiElement = null;
94     }
95
96     String directoryName = null;
97
98     if (psiElement instanceof PsiDirectory) {
99       directoryName = ((PsiDirectory)psiElement).getVirtualFile().getPresentableUrl();
100     }
101     else {
102       final PsiFile psiFile = LangDataKeys.PSI_FILE.getData(dataContext);
103       if (psiFile != null) {
104         PsiDirectory psiDirectory = psiFile.getContainingDirectory();
105         if (psiDirectory != null) {
106           directoryName = psiDirectory.getVirtualFile().getPresentableUrl();
107         }
108       }
109     }
110
111     if (directoryName == null && psiElement instanceof PsiDirectoryContainer) {
112       final PsiDirectory[] directories = ((PsiDirectoryContainer)psiElement).getDirectories();
113       directoryName = directories.length == 1 ? directories[0].getVirtualFile().getPresentableUrl():null;
114     }
115
116     Module module = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
117     if (module != null) {
118       model.setModuleName(module.getName());
119     }
120
121     Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
122     if (model.getModuleName() == null || editor == null) {
123       model.setDirectoryName(directoryName);
124       model.setProjectScope(directoryName == null && module == null && !model.isCustomScope() || editor != null);
125     }
126   }
127
128   @Nullable
129   public static PsiDirectory getPsiDirectory(final FindModel findModel, Project project) {
130     String directoryName = findModel.getDirectoryName();
131     if (findModel.isProjectScope() || directoryName == null) {
132       return null;
133     }
134
135     final PsiManager psiManager = PsiManager.getInstance(project);
136     String path = directoryName.replace(File.separatorChar, '/');
137     VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);
138     if (virtualFile == null || !virtualFile.isDirectory()) {
139       if (!path.contains(JarFileSystem.JAR_SEPARATOR)) {
140         path += JarFileSystem.JAR_SEPARATOR;
141       }
142       virtualFile = JarFileSystem.getInstance().findFileByPath(path);
143     }
144     return virtualFile == null ? null : psiManager.findDirectory(virtualFile);
145   }
146
147   private static void addFilesUnderDirectory(PsiDirectory directory, Collection<PsiFile> fileList, boolean isRecursive, Pattern fileMaskRegExp) {
148     final PsiElement[] children = directory.getChildren();
149
150     for (PsiElement child : children) {
151       if (child instanceof PsiFile &&
152           (fileMaskRegExp == null ||
153            fileMaskRegExp.matcher(((PsiFile)child).getName()).matches()
154           )
155         ) {
156         PsiFile file = (PsiFile)child;
157         PsiFile sourceFile = (PsiFile)file.getNavigationElement();
158         if (sourceFile != null) file = sourceFile;
159         fileList.add(file);
160       }
161       else if (isRecursive && child instanceof PsiDirectory) {
162         addFilesUnderDirectory((PsiDirectory)child, fileList, isRecursive, fileMaskRegExp);
163       }
164     }
165   }
166
167   @NotNull
168   public static List<UsageInfo> findUsages(final FindModel findModel, final PsiDirectory psiDirectory, final Project project) {
169
170     final CommonProcessors.CollectProcessor<UsageInfo> collector = new CommonProcessors.CollectProcessor<UsageInfo>();
171     findUsages(findModel, psiDirectory, project, collector);
172
173     return new ArrayList<UsageInfo>(collector.getResults());
174   }
175
176   @Nullable
177   private static Pattern createFileMaskRegExp(FindModel findModel) {
178     final String filter = findModel.getFileFilter();
179     return createFileMaskRegExp(filter);
180   }
181
182   public static Pattern createFileMaskRegExp(String filter) {
183     if (filter == null) {
184       return null;
185     }
186     String pattern;
187     final List<String> strings = StringUtil.split(filter, ",");
188     if (strings.size() == 1) {
189       pattern = PatternUtil.convertToRegex(filter.trim());
190     }
191     else {
192       pattern = StringUtil.join(strings, new Function<String, String>() {
193         @Override
194         public String fun(String s) {
195           return "(" + PatternUtil.convertToRegex(s.trim()) + ")";
196         }
197       }, "|");
198     }
199     return Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
200   }
201
202   public static void findUsages(final FindModel findModel,
203                                 final PsiDirectory psiDirectory,
204                                 final Project project,
205                                 final Processor<UsageInfo> consumer) {
206     final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
207
208     final Collection<PsiFile> psiFiles = getFilesToSearchIn(findModel, project, psiDirectory);
209     try {
210       final Set<PsiFile> largeFiles = new THashSet<PsiFile>();
211
212       int i = 0;
213       long totalFilesSize = 0;
214       int count = 0;
215       final boolean[] warningShown = {false};
216
217       final UsageViewManager usageViewManager = UsageViewManager.getInstance(project);
218       for (final PsiFile psiFile : psiFiles) {
219         usageViewManager.checkSearchCanceled();
220         final VirtualFile virtualFile = psiFile.getVirtualFile();
221         final int index = i++;
222         if (virtualFile == null) continue;
223
224         long fileLength = getFileLength(virtualFile);
225         if (fileLength == -1) continue; // Binary or invalid
226
227         if (ProjectUtil.isProjectOrWorkspaceFile(virtualFile) && !Registry.is("find.search.in.project.files")) continue;
228
229         if (fileLength > SINGLE_FILE_SIZE_LIMIT) {
230           largeFiles.add(psiFile);
231           continue;
232         }
233
234         if (progress != null) {
235           progress.setFraction((double)index / psiFiles.size());
236           String text = FindBundle.message("find.searching.for.string.in.file.progress",
237                                            findModel.getStringToFind(), virtualFile.getPresentableUrl());
238           progress.setText(text);
239           progress.setText2(FindBundle.message("find.searching.for.string.in.file.occurrences.progress", count));
240         }
241
242         int countInFile = processUsagesInFile(psiFile, findModel, consumer);
243
244         count += countInFile;
245         if (countInFile > 0) {
246           totalFilesSize += fileLength;
247           if (totalFilesSize > FILES_SIZE_LIMIT && !warningShown[0]) {
248             warningShown[0] = true;
249             String message = FindBundle.message("find.excessive.total.size.prompt", presentableSize(totalFilesSize),
250                                                 ApplicationNamesInfo.getInstance().getProductName());
251             UsageLimitUtil.showAndCancelIfAborted(project, message);
252           }
253         }
254       }
255
256       if (!largeFiles.isEmpty()) {
257         @Language("HTML")
258         String message = "<html><body>";
259         if (largeFiles.size() == 1) {
260           final VirtualFile vFile = largeFiles.iterator().next().getVirtualFile();
261           message += "File " + presentableFileInfo(vFile) + " is ";
262         }
263         else {
264           message += "Files<br> ";
265
266           int counter = 0;
267           for (PsiFile file : largeFiles) {
268             final VirtualFile vFile = file.getVirtualFile();
269             message += presentableFileInfo(vFile) + "<br> ";
270             if (counter++ > 10) break;
271           }
272
273           message += "are ";
274         }
275
276         message += "too large and cannot be scanned</body></html>";
277
278         final String finalMessage = message;
279         ApplicationManager.getApplication().invokeLater(new Runnable() {
280           @Override
281           public void run() {
282             ToolWindowManager.getInstance(project).notifyByBalloon(ToolWindowId.FIND, MessageType.WARNING, finalMessage);
283           }
284         }, project.getDisposed());
285       }
286     }
287     catch (ProcessCanceledException e) {
288       // fine
289     }
290
291     if (progress != null) {
292       progress.setText(FindBundle.message("find.progress.search.completed"));
293     }
294   }
295
296   private static String presentableFileInfo(VirtualFile vFile) {
297     return getPresentablePath(vFile)
298            + "&nbsp;("
299            + presentableSize(getFileLength(vFile))
300            + ")";
301   }
302
303   private static int processUsagesInFile(final PsiFile psiFile,
304                                          final FindModel findModel,
305                                          final Processor<UsageInfo> consumer) {
306     final VirtualFile virtualFile = psiFile.getVirtualFile();
307     if (virtualFile == null) return 0;
308     if (virtualFile.getFileType().isBinary()) return 0; // do not decompile .class files
309     final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
310     if (document == null) return 0;
311     final int[] offset = {0};
312     int count = 0;
313     int found;
314     do {
315       found = ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
316         @Override
317         @NotNull
318         public Integer compute() {
319           if (!psiFile.isValid()) return 0;
320           return addToUsages(document, consumer, findModel, psiFile, offset, USAGES_PER_READ_ACTION);
321         }
322       });
323       count += found;
324     }
325     while (found != 0);
326     return count;
327   }
328
329   private static String getPresentablePath(final VirtualFile virtualFile) {
330     return "'" + ApplicationManager.getApplication().runReadAction(new Computable<String>() {
331       @Override
332       public String compute() {
333         return virtualFile.getPresentableUrl();
334       }
335     }) + "'";
336   }
337
338   private static String presentableSize(long bytes) {
339     long megabytes = bytes / (1024 * 1024);
340     return FindBundle.message("find.file.size.megabytes", Long.toString(megabytes));
341   }
342
343   private static long getFileLength(final VirtualFile virtualFile) {
344     final long[] length = {-1L};
345     ApplicationManager.getApplication().runReadAction(new Runnable() {
346       @Override
347       public void run() {
348         if (!virtualFile.isValid()) return;
349         if (virtualFile.getFileType().isBinary()) return;
350         length[0] = virtualFile.getLength();
351       }
352     });
353     return length[0];
354   }
355
356   private static Collection<PsiFile> getFilesToSearchIn(final FindModel findModel, final Project project, final PsiDirectory psiDirectory) {
357     return ApplicationManager.getApplication().runReadAction(new Computable<Collection<PsiFile>>() {
358       @Override
359       public Collection<PsiFile> compute() {
360         return getFilesToSearchInReadAction(findModel, project, psiDirectory);
361       }
362     });
363   }
364   private static Collection<PsiFile> getFilesToSearchInReadAction(final FindModel findModel, final Project project, final PsiDirectory psiDirectory) {
365     String moduleName = findModel.getModuleName();
366     Module module = moduleName == null ? null : ModuleManager.getInstance(project).findModuleByName(moduleName);
367     final FileIndex fileIndex = module == null ?
368                                 ProjectRootManager.getInstance(project).getFileIndex() :
369                                 ModuleRootManager.getInstance(module).getFileIndex();
370
371     if (psiDirectory == null || findModel.isWithSubdirectories() && fileIndex.isInContent(psiDirectory.getVirtualFile())) {
372       final Pattern fileMaskRegExp = createFileMaskRegExp(findModel);
373       // optimization
374       Pair<Boolean, Collection<PsiFile>> fastWords = getFilesForFastWordSearch(findModel, project, psiDirectory, fileMaskRegExp, module);
375       final Collection<PsiFile> filesForFastWordSearch = fastWords.getSecond();
376
377       if (fastWords.getFirst() && canOptimizeForFastWordSearch(findModel)) return filesForFastWordSearch;
378
379       final GlobalSearchScope customScope = toGlobal(project, findModel.getCustomScope());
380
381       class EnumContentIterator implements ContentIterator {
382         final List<PsiFile> myFiles = new ArrayList<PsiFile>(filesForFastWordSearch);
383         final PsiManager psiManager = PsiManager.getInstance(project);
384
385         @Override
386         public boolean processFile(VirtualFile virtualFile) {
387           if (!virtualFile.isDirectory() &&
388               (fileMaskRegExp == null || fileMaskRegExp.matcher(virtualFile.getName()).matches()) &&
389               (customScope == null || customScope.contains(virtualFile))) {
390             final PsiFile psiFile = psiManager.findFile(virtualFile);
391             if (psiFile != null && !filesForFastWordSearch.contains(psiFile)) {
392               myFiles.add(psiFile);
393             }
394           }
395           return true;
396         }
397
398         private Collection<PsiFile> getFiles() {
399           return myFiles;
400         }
401       }
402       final EnumContentIterator iterator = new EnumContentIterator();
403
404       if (psiDirectory == null) {
405         boolean success = fileIndex.iterateContent(iterator);
406         if (success && customScope instanceof GlobalSearchScope && ((GlobalSearchScope)customScope).isSearchInLibraries()) {
407           OrderEnumerator enumerator = module == null ? OrderEnumerator.orderEntries(project) : OrderEnumerator.orderEntries(module);
408           final VirtualFile[] librarySources = enumerator.withoutModuleSourceEntries().withoutDepModules().getSourceRoots();
409           iterateAll(librarySources, (GlobalSearchScope)customScope, iterator);
410         }
411       }
412       else {
413         fileIndex.iterateContentUnderDirectory(psiDirectory.getVirtualFile(), iterator);
414       }
415       return iterator.getFiles();
416     }
417     else {
418       Collection<PsiFile> fileList = new THashSet<PsiFile>();
419
420       addFilesUnderDirectory(psiDirectory,
421                              fileList,
422                              findModel.isWithSubdirectories(),
423                              createFileMaskRegExp(findModel));
424       return fileList;
425     }
426   }
427
428   private static boolean iterateAll(VirtualFile[] files, final GlobalSearchScope searchScope, final ContentIterator iterator) {
429     final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
430     final VirtualFileFilter contentFilter = new VirtualFileFilter() {
431       @Override
432       public boolean accept(final VirtualFile file) {
433         return file.isDirectory() ||
434                !fileTypeManager.isFileIgnored(file) && !file.getFileType().isBinary() && searchScope.contains(file);
435       }
436     };
437     for (VirtualFile file : files) {
438       if (!FileIndexImplUtil.iterateRecursively(file, contentFilter, iterator)) return false;
439     }
440     return true;
441   }
442
443   @Nullable
444   private static GlobalSearchScope toGlobal(Project project, @Nullable SearchScope scope) {
445     if (scope instanceof GlobalSearchScope) {
446       return (GlobalSearchScope)scope;
447     }
448     Set<VirtualFile> files = new HashSet<VirtualFile>();
449     for (PsiElement element : ((LocalSearchScope)scope).getScope()) {
450       PsiFile file = element.getContainingFile();
451       if (file != null) {
452         ContainerUtil.addIfNotNull(files, file.getVirtualFile());
453       }
454     }
455     return GlobalSearchScope.filesScope(project, files);
456   }
457
458   @NotNull
459   private static Pair<Boolean, Collection<PsiFile>> getFilesForFastWordSearch(final FindModel findModel, final Project project,
460                                                                final PsiDirectory psiDirectory, final Pattern fileMaskRegExp,
461                                                                final Module module) {
462     if (DumbService.getInstance(project).isDumb()) {
463       return new Pair<Boolean, Collection<PsiFile>>(false, Collections.<PsiFile>emptyList());
464     }
465
466     PsiManager pm = PsiManager.getInstance(project);
467     CacheManager cacheManager = CacheManager.SERVICE.getInstance(project);
468     SearchScope customScope = findModel.getCustomScope();
469     @NotNull GlobalSearchScope scope = psiDirectory != null
470                                        ? GlobalSearchScopes.directoryScope(psiDirectory, true)
471                                        : module != null
472                                          ? moduleContentScope(module)
473                                          : customScope instanceof GlobalSearchScope
474                                            ? (GlobalSearchScope)customScope
475                                            : GlobalSearchScope.projectScope(project);
476
477     Set<Integer> keys = new THashSet<Integer>(30);
478     Set<PsiFile> resultFiles = new THashSet<PsiFile>();
479     boolean fast = false;
480
481     if (TrigramIndex.ENABLED) {
482       TIntHashSet trigrams = TrigramBuilder.buildTrigram(findModel.getStringToFind());
483       TIntIterator it = trigrams.iterator();
484       while (it.hasNext()) {
485         keys.add(it.next());
486       }
487
488       if (!keys.isEmpty()) {
489         fast = true;
490         List<VirtualFile> hits = new ArrayList<VirtualFile>();
491         FileBasedIndex.getInstance()
492           .getFilesWithKey(TrigramIndex.INDEX_ID, keys, new CommonProcessors.CollectProcessor<VirtualFile>(hits), scope);
493
494         for (VirtualFile hit : hits) {
495           resultFiles.add(pm.findFile(hit));
496         }
497
498         filterMaskedFiles(resultFiles, fileMaskRegExp);
499         if (resultFiles.isEmpty()) return new Pair<Boolean, Collection<PsiFile>>(true, resultFiles);
500       }
501     }
502
503
504     // $ is used to separate words when indexing plain-text files but not when indexing
505     // Java identifiers, so we can't consistently break a string containing $ characters into words
506
507     fast |= findModel.isWholeWordsOnly() && findModel.getStringToFind().indexOf('$') < 0;
508
509     List<String> words = StringUtil.getWordsIn(findModel.getStringToFind());
510
511     // hope long words are rare
512     Collections.sort(words, new Comparator<String>() {
513       @Override
514       public int compare(final String o1, final String o2) {
515         return o2.length() - o1.length();
516       }
517     });
518
519     for (int i = 0; i < words.size(); i++) {
520       String word = words.get(i);
521
522       PsiFile[] files = cacheManager.getFilesWithWord(word, UsageSearchContext.ANY, scope, findModel.isCaseSensitive());
523       if (files.length == 0) {
524         resultFiles.clear();
525         break;
526       }
527
528       final List<PsiFile> psiFiles = Arrays.asList(files);
529
530       if (i == 0 && keys.isEmpty()) {
531         resultFiles.addAll(psiFiles);
532       }
533       else {
534         resultFiles.retainAll(psiFiles);
535       }
536
537       filterMaskedFiles(resultFiles, fileMaskRegExp);
538       if (resultFiles.isEmpty()) break;
539     }
540
541     // in case our word splitting is incorrect
542     PsiFile[] allWordsFiles =
543       cacheManager.getFilesWithWord(findModel.getStringToFind(), UsageSearchContext.ANY, scope, findModel.isCaseSensitive());
544     ContainerUtil.addAll(resultFiles, allWordsFiles);
545
546     filterMaskedFiles(resultFiles, fileMaskRegExp);
547
548     return new Pair<Boolean, Collection<PsiFile>>(fast, resultFiles);
549   }
550
551   private static GlobalSearchScope moduleContentScope(final Module module) {
552     VirtualFile[] contentRoots = ModuleRootManager.getInstance(module).getContentRoots();
553     GlobalSearchScope result = null;
554     PsiManager psiManager = PsiManager.getInstance(module.getProject());
555     for (VirtualFile root : contentRoots) {
556       PsiDirectory directory = psiManager.findDirectory(root);
557       if (directory != null) {
558         GlobalSearchScope moduleContent = GlobalSearchScopes.directoryScope(directory, true);
559         result = result == null ? moduleContent : result.uniteWith(moduleContent);
560       }
561     }
562     if (result == null) {
563       result = GlobalSearchScope.EMPTY_SCOPE;
564     }
565     return result;
566   }
567
568   private static void filterMaskedFiles(final Set<PsiFile> resultFiles, final Pattern fileMaskRegExp) {
569     if (fileMaskRegExp != null) {
570       for (Iterator<PsiFile> iterator = resultFiles.iterator(); iterator.hasNext();) {
571         PsiFile file = iterator.next();
572         if (!fileMaskRegExp.matcher(file.getName()).matches()) {
573           iterator.remove();
574         }
575       }
576     }
577   }
578
579   private static boolean canOptimizeForFastWordSearch(final FindModel findModel) {
580     return !findModel.isRegularExpressions()
581            && (findModel.getCustomScope() == null || findModel.getCustomScope() instanceof GlobalSearchScope);
582   }
583
584   private static int addToUsages(@NotNull Document document, @NotNull Processor<UsageInfo> consumer, @NotNull FindModel findModel,
585                                  @NotNull final PsiFile psiFile, int[] offsetRef, int maxUsages) {
586     int count = 0;
587     CharSequence text = document.getCharsSequence();
588     int textLength = document.getTextLength();
589     int offset = offsetRef[0];
590
591     Project project = psiFile.getProject();
592
593     UsageViewManager usageViewManager = UsageViewManager.getInstance(project);
594     FindManager findManager = FindManager.getInstance(project);
595     while (offset < textLength) {
596       usageViewManager.checkSearchCanceled();
597       FindResult result = findManager.findString(text, offset, findModel, psiFile.getVirtualFile());
598       if (!result.isStringFound()) break;
599
600       final SearchScope customScope = findModel.getCustomScope();
601       if (customScope instanceof LocalSearchScope) {
602         final TextRange range = new TextRange(result.getStartOffset(), result.getEndOffset());
603         if (!((LocalSearchScope)customScope).containsRange(psiFile, range)) break;
604       }
605       UsageInfo info = new UsageInfo(psiFile, result.getStartOffset(), result.getEndOffset());
606       if (!consumer.process(info)) break;
607       count++;
608
609       final int prevOffset = offset;
610       offset = result.getEndOffset();
611
612       if (prevOffset == offset) {
613         // for regular expr the size of the match could be zero -> could be infinite loop in finding usages!
614         ++offset;
615       }
616       if (maxUsages > 0 && count >= maxUsages) {
617         break;
618       }
619     }
620     offsetRef[0] = offset;
621     return count;
622   }
623
624   private static String getTitleForScope(final FindModel findModel) {
625     String result;
626
627     if (findModel.isProjectScope()) {
628       result = FindBundle.message("find.scope.project.title");
629     }
630     else if (findModel.getModuleName() != null) {
631       result = FindBundle.message("find.scope.module.title", findModel.getModuleName());
632     }
633     else if(findModel.getCustomScopeName() != null) {
634       result = findModel.getCustomScopeName();
635     }
636     else {
637       result = FindBundle.message("find.scope.directory.title", findModel.getDirectoryName());
638     }
639
640     if (findModel.getFileFilter() != null) {
641       result = FindBundle.message("find.scope.files.with.mask", result, findModel.getFileFilter());
642     }
643
644     return result;
645   }
646
647   public static UsageViewPresentation setupViewPresentation(final boolean toOpenInNewTab, final FindModel findModelCopy) {
648     final UsageViewPresentation presentation = new UsageViewPresentation();
649
650     final String scope = getTitleForScope(findModelCopy);
651     final String stringToFind = findModelCopy.getStringToFind();
652     presentation.setScopeText(scope);
653     presentation.setTabText(FindBundle.message("find.usage.view.tab.text", stringToFind));
654     presentation.setToolwindowTitle(FindBundle.message("find.usage.view.toolwindow.title", stringToFind, scope));
655     presentation.setUsagesString(FindBundle.message("find.usage.view.usages.text", stringToFind));
656     presentation.setOpenInNewTab(toOpenInNewTab);
657     presentation.setCodeUsages(false);
658
659     return presentation;
660   }
661
662   public static FindUsagesProcessPresentation setupProcessPresentation(final Project project,
663                                                                        final boolean showPanelIfOnlyOneUsage,
664                                                                        final UsageViewPresentation presentation) {
665     FindUsagesProcessPresentation processPresentation = new FindUsagesProcessPresentation();
666     processPresentation.setShowNotFoundMessage(true);
667     processPresentation.setShowPanelIfOnlyOneUsage(showPanelIfOnlyOneUsage);
668     processPresentation.setProgressIndicatorFactory(
669       new Factory<ProgressIndicator>() {
670         @Override
671         public ProgressIndicator create() {
672           return new FindProgressIndicator(project, presentation.getScopeText());
673         }
674       }
675     );
676     return processPresentation;
677   }
678
679   public static class StringUsageTarget implements UsageTarget {
680     private final String myStringToFind;
681
682     private final ItemPresentation myItemPresentation = new ItemPresentation() {
683       @Override
684       public String getPresentableText() {
685         return FindBundle.message("find.usage.target.string.text", myStringToFind);
686       }
687
688       @Override
689       public String getLocationString() {
690         return myStringToFind + "!!";
691       }
692
693       @Override
694       public Icon getIcon(boolean open) {
695         return null;
696       }
697     };
698
699     public StringUsageTarget(String _stringToFind) {
700       myStringToFind = _stringToFind;
701     }
702
703     @Override
704     public void findUsages() {}
705     @Override
706     public void findUsagesInEditor(@NotNull FileEditor editor) {}
707     @Override
708     public void highlightUsages(PsiFile file, Editor editor, boolean clearHighlights) {}
709
710     @Override
711     public boolean isValid() {
712       return true;
713     }
714
715     @Override
716     public boolean isReadOnly() {
717       return true;
718     }
719
720     @Override
721     @Nullable
722     public VirtualFile[] getFiles() {
723       return null;
724     }
725
726     @Override
727     public void update() {
728     }
729
730     @Override
731     public String getName() {
732       return myStringToFind;
733     }
734
735     @Override
736     public ItemPresentation getPresentation() {
737       return myItemPresentation;
738     }
739
740     @Override
741     public void navigate(boolean requestFocus) {
742       throw new UnsupportedOperationException();
743     }
744
745     @Override
746     public boolean canNavigate() {
747       return false;
748     }
749
750     @Override
751     public boolean canNavigateToSource() {
752       return false;
753     }
754   }
755 }