AtomicReference<LookupElement[]> startCompletion(
      final CompletionInitializationContext initContext) {
    boolean sync =
        ApplicationManager.getApplication().isUnitTestMode()
            && !CompletionAutoPopupHandler.ourTestingAutopopup;
    final CompletionThreading strategy = sync ? new SyncCompletion() : new AsyncCompletion();

    strategy.startThread(
        ProgressWrapper.wrap(this),
        new Runnable() {
          @Override
          public void run() {
            scheduleAdvertising();
          }
        });
    final WeighingDelegate weigher = strategy.delegateWeighing(this);

    final AtomicReference<LookupElement[]> data = new AtomicReference<LookupElement[]>(null);
    class CalculateItems implements Runnable {
      @Override
      public void run() {
        try {
          data.set(calculateItems(initContext, weigher));
        } catch (ProcessCanceledException ignore) {
        } catch (Throwable t) {
          LOG.error(t);
          cancel();
        }
      }
    }
    strategy.startThread(this, new CalculateItems());
    return data;
  }
 private static void updateIndicator(@NotNull final String text, final double progressFraction) {
   final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
   if (indicator != null) {
     final ProgressIndicator progressIndicator = ProgressWrapper.unwrap(indicator);
     progressIndicator.setText(text);
     if (progressFraction == -1) {
       progressIndicator.setIndeterminate(true);
     } else {
       progressIndicator.setIndeterminate(false);
       progressIndicator.setFraction(progressFraction);
     }
   }
 }
 // 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;
 }
  @Override
  public <T> boolean invokeConcurrentlyUnderProgress(
      @NotNull List<? extends T> things,
      ProgressIndicator progress,
      boolean failFastOnAcquireReadAction,
      @NotNull final Processor<T> thingProcessor)
      throws ProcessCanceledException {
    if (things.isEmpty()) {
      return true;
    }
    if (things.size() == 1) {
      T t = things.get(0);
      return thingProcessor.process(t);
    }

    // can be already wrapped
    final ProgressWrapper wrapper =
        progress instanceof ProgressWrapper
            ? (ProgressWrapper) progress
            : ProgressWrapper.wrap(progress);
    return invokeConcurrentlyForAll(
        things,
        failFastOnAcquireReadAction,
        new Processor<T>() {
          public boolean process(final T t) {
            final boolean[] result = new boolean[1];
            ProgressManager.getInstance()
                .executeProcessUnderProgress(
                    new Runnable() {
                      public void run() {
                        result[0] = thingProcessor.process(t);
                      }
                    },
                    wrapper);
            return result[0];
          }
        });
  }