@NotNull
  private static UsageSearcher createUsageSearcher(PsiElement2UsageTargetAdapter @NotNull [] primaryTargets,
                                                   PsiElement2UsageTargetAdapter @NotNull [] secondaryTargets,
                                                   @NotNull FindUsagesHandlerBase handler,
                                                   @NotNull FindUsagesOptions options,
                                                   PsiFile scopeFile) throws PsiInvalidElementAccessException {
    ReadAction.run(() -> {
      PsiElement[] primaryElements = PsiElement2UsageTargetAdapter.convertToPsiElements(primaryTargets);
      PsiElement[] secondaryElements = PsiElement2UsageTargetAdapter.convertToPsiElements(secondaryTargets);

      ContainerUtil
        .concat(primaryElements, secondaryElements)
        .forEach(psi -> {
          if (psi == null || !psi.isValid()) throw new PsiInvalidElementAccessException(psi);
        });
    });

    FindUsagesOptions optionsClone = options.clone();
    return processor -> {
      Project project = ReadAction.compute(() -> scopeFile != null ? scopeFile.getProject() : primaryTargets[0].getProject());
      ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();

      runUpdate(primaryTargets, indicator);
      runUpdate(secondaryTargets, indicator);

      PsiElement[] primaryElements = ReadAction.compute(() -> PsiElement2UsageTargetAdapter.convertToPsiElements(primaryTargets));
      PsiElement[] secondaryElements = ReadAction.compute(() -> PsiElement2UsageTargetAdapter.convertToPsiElements(secondaryTargets));

      LOG.assertTrue(indicator != null, "Must run under progress. see ProgressManager.run*");

      ((PsiManagerImpl)PsiManager.getInstance(project)).dropResolveCacheRegularly(indicator);

      if (scopeFile != null) {
        optionsClone.searchScope = new LocalSearchScope(scopeFile);
      }
      ClusteringSearchSession clusteringSearchSession = ClusteringSearchSession.createClusteringSessionIfEnabled();
      Processor<UsageInfo> usageInfoProcessor = new CommonProcessors.UniqueProcessor<>(usageInfo -> {
        Usage usage = ReadAction.compute(
          () -> clusteringSearchSession != null
                ? UsageInfoToUsageConverter.convertToSimilarUsage(primaryElements, usageInfo, clusteringSearchSession)
                : UsageInfoToUsageConverter.convert(primaryElements, usageInfo)
        );
        return processor.process(usage);
      });
      PsiElement[] elements = ArrayUtil.mergeArrays(primaryElements, secondaryElements, PsiElement.ARRAY_FACTORY);

      optionsClone.fastTrack = new SearchRequestCollector(new SearchSession(elements));
      if (optionsClone.searchScope instanceof GlobalSearchScope) {
        // we will search in project scope always but warn if some usage is out of scope
        optionsClone.searchScope = optionsClone.searchScope.union(GlobalSearchScope.projectScope(project));
      }
      try {
        for (PsiElement element :elements) {
          if (!handler.processElementUsages(element, usageInfoProcessor, optionsClone)) {
            return;
          }

          for (CustomUsageSearcher searcher : CustomUsageSearcher.EP_NAME.getExtensionList()) {
            try {
              searcher.processElementUsages(element, processor, optionsClone);
            }
            catch (IndexNotReadyException e) {
              DumbService.getInstance(element.getProject()).showDumbModeNotificationForFunctionality(
                FindBundle.message("notification.find.usages.is.not.available.during.indexing"),
                DumbModeBlockedFunctionality.FindUsages);
            }
            catch (ProcessCanceledException e) {
              throw e;
            }
            catch (Exception e) {
              LOG.error(e);
            }
          }
        }

        PsiSearchHelper.getInstance(project)
          .processRequests(optionsClone.fastTrack, ref -> {
            UsageInfo info = ReadAction.compute(() -> {
              if (!ref.getElement().isValid()) return null;
              return new UsageInfo(ref);
            });
            return info == null || usageInfoProcessor.process(info);
          });
      }
      finally {
        optionsClone.fastTrack = null;
      }
    };
  }
@NotNull
  private static UsageSearcher createUsageSearcher(PsiElement2UsageTargetAdapter @NotNull [] primaryTargets,
                                                   PsiElement2UsageTargetAdapter @NotNull [] secondaryTargets,
                                                   @NotNull FindUsagesHandlerBase handler,
                                                   @NotNull FindUsagesOptions options,
                                                   PsiFile scopeFile)