@Nullable
  public static VirtualFile getDirectory(@NotNull final FindModel findModel) {
    String directoryName = findModel.getDirectoryName();
    if (findModel.isProjectScope() || StringUtil.isEmpty(directoryName)) {
      return null;
    }

    String path = directoryName.replace(File.separatorChar, '/');
    VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);
    if (virtualFile == null || !virtualFile.isDirectory()) {
      virtualFile = null;
      for (LocalFileProvider provider :
          ((VirtualFileManagerEx) VirtualFileManager.getInstance()).getLocalFileProviders()) {
        VirtualFile file = provider.findLocalVirtualFileByPath(path);
        if (file != null && file.isDirectory()) {
          if (file.getChildren().length > 0) {
            virtualFile = file;
            break;
          }
          if (virtualFile == null) {
            virtualFile = file;
          }
        }
      }
    }
    return virtualFile;
  }
  @NotNull
  public static UsageViewPresentation setupViewPresentation(
      final boolean toOpenInNewTab, @NotNull FindModel findModel) {
    final UsageViewPresentation presentation = new UsageViewPresentation();

    final String scope = getTitleForScope(findModel);
    final String stringToFind = findModel.getStringToFind();
    presentation.setScopeText(scope);
    if (stringToFind.isEmpty()) {
      presentation.setTabText("Files");
      presentation.setToolwindowTitle(BundleBase.format("Files in {0}", scope));
      presentation.setUsagesString("files");
    } else {
      FindModel.SearchContext searchContext = findModel.getSearchContext();
      String contextText = "";
      if (searchContext != FindModel.SearchContext.ANY) {
        contextText =
            FindBundle.message(
                "find.context.presentation.scope.label",
                FindDialog.getPresentableName(searchContext));
      }
      presentation.setTabText(
          FindBundle.message("find.usage.view.tab.text", stringToFind, contextText));
      presentation.setToolwindowTitle(
          FindBundle.message("find.usage.view.toolwindow.title", stringToFind, scope, contextText));
      presentation.setUsagesString(FindBundle.message("find.usage.view.usages.text", stringToFind));
      presentation.setUsagesWord(FindBundle.message("occurrence"));
      presentation.setCodeUsagesString(FindBundle.message("found.occurrences"));
      presentation.setContextText(contextText);
    }
    presentation.setOpenInNewTab(toOpenInNewTab);
    presentation.setCodeUsages(false);
    presentation.setUsageTypeFilteringAvailable(true);

    return presentation;
  }
  private static int addToUsages(
      @NotNull Document document,
      @NotNull Processor<UsageInfo> consumer,
      @NotNull FindModel findModel,
      @NotNull final PsiFile psiFile,
      @NotNull int[] offsetRef,
      int maxUsages) {
    int count = 0;
    CharSequence text = document.getCharsSequence();
    int textLength = document.getTextLength();
    int offset = offsetRef[0];

    Project project = psiFile.getProject();

    FindManager findManager = FindManager.getInstance(project);
    while (offset < textLength) {
      FindResult result = findManager.findString(text, offset, findModel, psiFile.getVirtualFile());
      if (!result.isStringFound()) break;

      final SearchScope customScope = findModel.getCustomScope();
      if (customScope instanceof LocalSearchScope) {
        final TextRange range = new TextRange(result.getStartOffset(), result.getEndOffset());
        if (!((LocalSearchScope) customScope).containsRange(psiFile, range)) break;
      }
      UsageInfo info = new FindResultUsageInfo(findManager, psiFile, offset, findModel, result);
      if (!consumer.process(info)) {
        throw new ProcessCanceledException();
      }
      count++;

      final int prevOffset = offset;
      offset = result.getEndOffset();

      if (prevOffset == offset) {
        // for regular expr the size of the match could be zero -> could be infinite loop in finding
        // usages!
        ++offset;
      }
      if (maxUsages > 0 && count >= maxUsages) {
        break;
      }
    }
    offsetRef[0] = offset;
    return count;
  }
  public static void setDirectoryName(@NotNull FindModel model, @NotNull DataContext dataContext) {
    PsiElement psiElement = null;
    Project project = CommonDataKeys.PROJECT.getData(dataContext);

    if (project != null && !DumbServiceImpl.getInstance(project).isDumb()) {
      try {
        psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
      } catch (IndexNotReadyException ignore) {
      }
    }

    String directoryName = null;

    if (psiElement instanceof PsiDirectory) {
      directoryName = ((PsiDirectory) psiElement).getVirtualFile().getPresentableUrl();
    }

    if (directoryName == null && psiElement instanceof PsiDirectoryContainer) {
      final PsiDirectory[] directories = ((PsiDirectoryContainer) psiElement).getDirectories();
      directoryName =
          directories.length == 1 ? directories[0].getVirtualFile().getPresentableUrl() : null;
    }

    Module module = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
    if (module != null) {
      model.setModuleName(module.getName());
    }

    Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
    if (model.getModuleName() == null || editor == null) {
      model.setDirectoryName(directoryName);
      model.setProjectScope(
          directoryName == null && module == null && !model.isCustomScope() || editor != null);
      if (directoryName != null) {
        model.setCustomScope(false); // to select "Directory: " radio button
      }

      // for convenience set directory name to directory of current file, note that we doesn't
      // change default projectScope
      if (directoryName == null) {
        VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);
        if (virtualFile != null && !virtualFile.isDirectory())
          virtualFile = virtualFile.getParent();
        if (virtualFile != null) model.setDirectoryName(virtualFile.getPresentableUrl());
      }
    }
  }
  @NotNull
  private static String getTitleForScope(@NotNull final FindModel findModel) {
    String scopeName;
    if (findModel.isProjectScope()) {
      scopeName = FindBundle.message("find.scope.project.title");
    } else if (findModel.getModuleName() != null) {
      scopeName = FindBundle.message("find.scope.module.title", findModel.getModuleName());
    } else if (findModel.getCustomScopeName() != null) {
      scopeName = findModel.getCustomScopeName();
    } else {
      scopeName = FindBundle.message("find.scope.directory.title", findModel.getDirectoryName());
    }

    String result = scopeName;
    if (findModel.getFileFilter() != null) {
      result += " " + FindBundle.message("find.scope.files.with.mask", findModel.getFileFilter());
    }

    return result;
  }
 // returns number of hits
 static int processUsagesInFile(
     @NotNull final PsiFile psiFile,
     @NotNull final FindModel findModel,
     @NotNull final Processor<UsageInfo> consumer) {
   if (findModel.getStringToFind().isEmpty()) {
     if (!ApplicationManager.getApplication()
         .runReadAction((Computable<Boolean>) () -> consumer.process(new UsageInfo(psiFile)))) {
       throw new ProcessCanceledException();
     }
     return 1;
   }
   final VirtualFile virtualFile = psiFile.getVirtualFile();
   if (virtualFile == null) return 0;
   if (virtualFile.getFileType().isBinary()) return 0; // do not decompile .class files
   final Document document =
       ApplicationManager.getApplication()
           .runReadAction(
               (Computable<Document>)
                   () ->
                       virtualFile.isValid()
                           ? FileDocumentManager.getInstance().getDocument(virtualFile)
                           : null);
   if (document == null) return 0;
   final int[] offset = {0};
   int count = 0;
   int found;
   ProgressIndicator indicator =
       ProgressWrapper.unwrap(ProgressManager.getInstance().getProgressIndicator());
   TooManyUsagesStatus tooManyUsagesStatus = TooManyUsagesStatus.getFrom(indicator);
   do {
     tooManyUsagesStatus.pauseProcessingIfTooManyUsages(); // wait for user out of read action
     found =
         ApplicationManager.getApplication()
             .runReadAction(
                 (Computable<Integer>)
                     () -> {
                       if (!psiFile.isValid()) return 0;
                       return addToUsages(
                           document, consumer, findModel, psiFile, offset, USAGES_PER_READ_ACTION);
                     });
     count += found;
   } while (found != 0);
   return count;
 }
 @NotNull
 static SearchScope getScopeFromModel(@NotNull Project project, @NotNull FindModel findModel) {
   SearchScope customScope = findModel.getCustomScope();
   VirtualFile directory = getDirectory(findModel);
   Module module =
       findModel.getModuleName() == null
           ? null
           : ModuleManager.getInstance(project).findModuleByName(findModel.getModuleName());
   return findModel.isCustomScope() && customScope != null
       ? customScope.intersectWith(GlobalSearchScope.allScope(project))
       :
       // we don't have to check for myProjectFileIndex.isExcluded(file) here like
       // FindInProjectTask.collectFilesInScope() does
       // because all found usages are guaranteed to be not in excluded dir
       directory != null
           ? forDirectory(project, findModel.isWithSubdirectories(), directory)
           : module != null
               ? module.getModuleContentScope()
               : findModel.isProjectScope()
                   ? ProjectScope.getContentScope(project)
                   : GlobalSearchScope.allScope(project);
 }
 @Override
 public String getName() {
   return myFindModel.getStringToFind().isEmpty()
       ? myFindModel.getFileFilter()
       : myFindModel.getStringToFind();
 }