static void log(
     ProgressIndicator progressIndicator,
     TextEditorHighlightingPass pass,
     @NonNls @NotNull Object... info) {
   if (LOG.isDebugEnabled()) {
     CharSequence docText =
         pass == null || pass.getDocument() == null
             ? ""
             : ": '" + StringUtil.first(pass.getDocument().getCharsSequence(), 10, true) + "'";
     synchronized (PassExecutorService.class) {
       String infos = StringUtil.join(info, Functions.TO_STRING(), " ");
       String message =
           StringUtil.repeatSymbol(' ', getThreadNum() * 4)
               + " "
               + pass
               + " "
               + infos
               + "; progress="
               + (progressIndicator == null ? null : progressIndicator.hashCode())
               + " "
               + (progressIndicator == null ? "?" : progressIndicator.isCanceled() ? "X" : "V")
               + docText;
       LOG.debug(message);
       // System.out.println(message);
     }
   }
 }
  @NotNull
  private TextEditorHighlightingPass convertToTextHighlightingPass(
      @NotNull final HighlightingPass pass,
      final Document document,
      @NotNull AtomicInteger id,
      int previousPassId) {
    TextEditorHighlightingPass textEditorHighlightingPass;
    if (pass instanceof TextEditorHighlightingPass) {
      textEditorHighlightingPass = (TextEditorHighlightingPass) pass;
    } else {
      // run all passes in sequence
      textEditorHighlightingPass =
          new TextEditorHighlightingPass(myProject, document, true) {
            @Override
            public void doCollectInformation(@NotNull ProgressIndicator progress) {
              pass.collectInformation(progress);
            }

            @Override
            public void doApplyInformationToEditor() {
              pass.applyInformationToEditor();
            }
          };
      textEditorHighlightingPass.setId(id.incrementAndGet());
      if (previousPassId != 0) {
        textEditorHighlightingPass.setCompletionPredecessorIds(new int[] {previousPassId});
      }
    }
    return textEditorHighlightingPass;
  }
  public List<HighlightInfo> runMainPasses(
      @NotNull PsiFile psiFile,
      @NotNull Document document,
      @NotNull final ProgressIndicator progress) {
    final List<HighlightInfo> result = new ArrayList<HighlightInfo>();
    final VirtualFile virtualFile = psiFile.getVirtualFile();
    if (virtualFile != null && !virtualFile.getFileType().isBinary()) {

      final List<TextEditorHighlightingPass> passes =
          TextEditorHighlightingPassRegistrarEx.getInstanceEx(myProject)
              .instantiateMainPasses(psiFile, document);

      Collections.sort(
          passes,
          new Comparator<TextEditorHighlightingPass>() {
            @Override
            public int compare(TextEditorHighlightingPass o1, TextEditorHighlightingPass o2) {
              if (o1 instanceof GeneralHighlightingPass) return -1;
              if (o2 instanceof GeneralHighlightingPass) return 1;
              return 0;
            }
          });

      for (TextEditorHighlightingPass pass : passes) {
        pass.doCollectInformation(progress);
        result.addAll(pass.getInfos());
      }
    }

    return result;
  }
 public List<TextEditorHighlightingPass> getPassesToShowProgressFor(Document document) {
   List<TextEditorHighlightingPass> allPasses = myPassExecutorService.getAllSubmittedPasses();
   List<TextEditorHighlightingPass> result =
       new ArrayList<TextEditorHighlightingPass>(allPasses.size());
   for (TextEditorHighlightingPass pass : allPasses) {
     if (pass.getDocument() == document || pass.getDocument() == null) {
       result.add(pass);
     }
   }
   return result;
 }
 private void applyInformationToEditorsLater(
     @NotNull final FileEditor fileEditor,
     @NotNull final TextEditorHighlightingPass pass,
     @NotNull final DaemonProgressIndicator updateProgress,
     @NotNull final AtomicInteger threadsToStartCountdown) {
   ApplicationManager.getApplication()
       .invokeLater(
           (DumbAwareRunnable)
               () -> {
                 if (isDisposed() || myProject.isDisposed()) {
                   updateProgress.cancel();
                 }
                 if (updateProgress.isCanceled()) {
                   log(updateProgress, pass, " is canceled during apply, sorry");
                   return;
                 }
                 Document document = pass.getDocument();
                 try {
                   if (fileEditor.getComponent().isDisplayable()
                       || ApplicationManager.getApplication().isUnitTestMode()) {
                     pass.applyInformationToEditor();
                     FileStatusMap fileStatusMap =
                         DaemonCodeAnalyzerEx.getInstanceEx(myProject).getFileStatusMap();
                     if (document != null) {
                       fileStatusMap.markFileUpToDate(document, pass.getId());
                     }
                     log(updateProgress, pass, " Applied");
                   }
                 } catch (ProcessCanceledException e) {
                   log(updateProgress, pass, "Error " + e);
                   throw e;
                 } catch (RuntimeException e) {
                   VirtualFile file =
                       document == null
                           ? null
                           : FileDocumentManager.getInstance().getFile(document);
                   FileType fileType = file == null ? null : file.getFileType();
                   String message =
                       "Exception while applying information to "
                           + fileEditor
                           + "("
                           + fileType
                           + ")";
                   log(updateProgress, pass, message + e);
                   throw new RuntimeException(message, e);
                 }
                 if (threadsToStartCountdown.decrementAndGet() == 0) {
                   log(updateProgress, pass, "Stopping ");
                   updateProgress.stopIfRunning();
                 } else {
                   log(
                       updateProgress,
                       pass,
                       "Finished but there are passes in the queue: "
                           + threadsToStartCountdown.get());
                 }
               },
           Registry.is("ide.perProjectModality")
               ? ModalityState.defaultModalityState()
               : ModalityState.stateForComponent(fileEditor.getComponent()));
 }
    private void doRun() {
      if (myUpdateProgress.isCanceled()) return;

      log(myUpdateProgress, myPass, "Started. ");

      for (ScheduledPass successor : mySuccessorsOnSubmit) {
        int predecessorsToRun = successor.myRunningPredecessorsCount.decrementAndGet();
        if (predecessorsToRun == 0) {
          submit(successor);
        }
      }

      ProgressManager.getInstance()
          .executeProcessUnderProgress(
              () -> {
                boolean success =
                    ApplicationManagerEx.getApplicationEx()
                        .tryRunReadAction(
                            () -> {
                              try {
                                if (DumbService.getInstance(myProject).isDumb()
                                    && !DumbService.isDumbAware(myPass)) {
                                  return;
                                }

                                if (!myUpdateProgress.isCanceled() && !myProject.isDisposed()) {
                                  myPass.collectInformation(myUpdateProgress);
                                }
                              } catch (ProcessCanceledException e) {
                                log(myUpdateProgress, myPass, "Canceled ");

                                if (!myUpdateProgress.isCanceled()) {
                                  myUpdateProgress.cancel(
                                      e); // in case when some smart asses throw PCE just for fun
                                }
                              } catch (RuntimeException | Error e) {
                                myUpdateProgress.cancel(e);
                                LOG.error(e);
                                throw e;
                              }
                            });

                if (!success) {
                  myUpdateProgress.cancel();
                }
              },
              myUpdateProgress);

      log(myUpdateProgress, myPass, "Finished. ");

      if (!myUpdateProgress.isCanceled()) {
        applyInformationToEditorsLater(
            myFileEditor, myPass, myUpdateProgress, myThreadsToStartCountdown);
        for (ScheduledPass successor : mySuccessorsOnCompletion) {
          int predecessorsToRun = successor.myRunningPredecessorsCount.decrementAndGet();
          if (predecessorsToRun == 0) {
            submit(successor);
          }
        }
      }
    }
  @NotNull
  private ScheduledPass createScheduledPass(
      @NotNull FileEditor fileEditor,
      @NotNull TextEditorHighlightingPass pass,
      @NotNull Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted,
      @NotNull List<TextEditorHighlightingPass> textEditorHighlightingPasses,
      @NotNull List<ScheduledPass> freePasses,
      @NotNull List<ScheduledPass> dependentPasses,
      @NotNull DaemonProgressIndicator updateProgress,
      @NotNull AtomicInteger threadsToStartCountdown) {
    int passId = pass.getId();
    Pair<FileEditor, Integer> key = Pair.create(fileEditor, passId);
    ScheduledPass scheduledPass = toBeSubmitted.get(key);
    if (scheduledPass != null) return scheduledPass;
    scheduledPass = new ScheduledPass(fileEditor, pass, updateProgress, threadsToStartCountdown);
    threadsToStartCountdown.incrementAndGet();
    toBeSubmitted.put(key, scheduledPass);
    for (int predecessorId : pass.getCompletionPredecessorIds()) {
      ScheduledPass predecessor =
          findOrCreatePredecessorPass(
              fileEditor,
              toBeSubmitted,
              textEditorHighlightingPasses,
              freePasses,
              dependentPasses,
              updateProgress,
              threadsToStartCountdown,
              predecessorId);
      if (predecessor != null) {
        predecessor.addSuccessorOnCompletion(scheduledPass);
      }
    }
    for (int predecessorId : pass.getStartingPredecessorIds()) {
      ScheduledPass predecessor =
          findOrCreatePredecessorPass(
              fileEditor,
              toBeSubmitted,
              textEditorHighlightingPasses,
              freePasses,
              dependentPasses,
              updateProgress,
              threadsToStartCountdown,
              predecessorId);
      if (predecessor != null) {
        predecessor.addSuccessorOnSubmit(scheduledPass);
      }
    }
    if (scheduledPass.myRunningPredecessorsCount.get() == 0
        && !freePasses.contains(scheduledPass)) {
      freePasses.add(scheduledPass);
    } else if (!dependentPasses.contains(scheduledPass)) {
      dependentPasses.add(scheduledPass);
    }

    if (pass.isRunIntentionPassAfter() && fileEditor instanceof TextEditor) {
      Editor editor = ((TextEditor) fileEditor).getEditor();
      ShowIntentionsPass ip = new ShowIntentionsPass(myProject, editor, -1);
      ip.setId(nextPassId.incrementAndGet());
      ip.setCompletionPredecessorIds(new int[] {scheduledPass.myPass.getId()});

      createScheduledPass(
          fileEditor,
          ip,
          toBeSubmitted,
          textEditorHighlightingPasses,
          freePasses,
          dependentPasses,
          updateProgress,
          threadsToStartCountdown);
    }

    return scheduledPass;
  }
  void submitPasses(
      @NotNull Map<FileEditor, HighlightingPass[]> passesMap,
      @NotNull DaemonProgressIndicator updateProgress) {
    if (isDisposed()) return;

    // null keys are ok
    MultiMap<Document, FileEditor> documentToEditors = MultiMap.createSet();
    MultiMap<FileEditor, TextEditorHighlightingPass> documentBoundPasses = MultiMap.createSmart();
    MultiMap<FileEditor, EditorBoundHighlightingPass> editorBoundPasses = MultiMap.createSmart();
    List<Pair<FileEditor, TextEditorHighlightingPass>> passesWithNoDocuments = new ArrayList<>();
    Set<VirtualFile> vFiles = new HashSet<>();

    for (Map.Entry<FileEditor, HighlightingPass[]> entry : passesMap.entrySet()) {
      FileEditor fileEditor = entry.getKey();
      HighlightingPass[] passes = entry.getValue();
      Document document;
      if (fileEditor instanceof TextEditor) {
        Editor editor = ((TextEditor) fileEditor).getEditor();
        LOG.assertTrue(!(editor instanceof EditorWindow));
        document = editor.getDocument();
      } else {
        VirtualFile virtualFile =
            ((FileEditorManagerEx) FileEditorManager.getInstance(myProject)).getFile(fileEditor);
        document =
            virtualFile == null ? null : FileDocumentManager.getInstance().getDocument(virtualFile);
      }
      if (document != null) {
        vFiles.add(FileDocumentManager.getInstance().getFile(document));
      }

      int prevId = 0;
      for (final HighlightingPass pass : passes) {
        if (pass instanceof EditorBoundHighlightingPass) {
          EditorBoundHighlightingPass editorPass = (EditorBoundHighlightingPass) pass;
          editorPass.setId(
              nextPassId.incrementAndGet()); // have to make ids unique for this document
          editorBoundPasses.putValue(fileEditor, editorPass);
        } else {
          TextEditorHighlightingPass textEditorHighlightingPass =
              convertToTextHighlightingPass(pass, document, nextPassId, prevId);
          document = textEditorHighlightingPass.getDocument();
          documentBoundPasses.putValue(fileEditor, textEditorHighlightingPass);
          if (document == null) {
            passesWithNoDocuments.add(Pair.create(fileEditor, textEditorHighlightingPass));
          } else {
            documentToEditors.putValue(document, fileEditor);
          }
          prevId = textEditorHighlightingPass.getId();
        }
      }
    }

    List<ScheduledPass> freePasses = new ArrayList<>(documentToEditors.size() * 5);
    List<ScheduledPass> dependentPasses = new ArrayList<>(documentToEditors.size() * 10);
    // (fileEditor, passId) -> created pass
    Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted = new THashMap<>(passesMap.size());

    final AtomicInteger threadsToStartCountdown = new AtomicInteger(0);
    for (Map.Entry<Document, Collection<FileEditor>> entry : documentToEditors.entrySet()) {
      Collection<FileEditor> fileEditors = entry.getValue();
      Document document = entry.getKey();
      FileEditor preferredFileEditor = getPreferredFileEditor(document, fileEditors);
      List<TextEditorHighlightingPass> passes =
          (List<TextEditorHighlightingPass>) documentBoundPasses.get(preferredFileEditor);
      if (passes.isEmpty()) {
        continue;
      }
      sortById(passes);
      for (TextEditorHighlightingPass currentPass : passes) {
        createScheduledPass(
            preferredFileEditor,
            currentPass,
            toBeSubmitted,
            passes,
            freePasses,
            dependentPasses,
            updateProgress,
            threadsToStartCountdown);
      }
    }

    for (Map.Entry<FileEditor, Collection<EditorBoundHighlightingPass>> entry :
        editorBoundPasses.entrySet()) {
      FileEditor fileEditor = entry.getKey();
      Collection<EditorBoundHighlightingPass> createdEditorBoundPasses = entry.getValue();
      List<TextEditorHighlightingPass> createdDocumentBoundPasses =
          (List<TextEditorHighlightingPass>) documentBoundPasses.get(fileEditor);
      List<TextEditorHighlightingPass> allCreatedPasses =
          new ArrayList<>(createdDocumentBoundPasses);
      allCreatedPasses.addAll(createdEditorBoundPasses);

      for (EditorBoundHighlightingPass pass : createdEditorBoundPasses) {
        createScheduledPass(
            fileEditor,
            pass,
            toBeSubmitted,
            allCreatedPasses,
            freePasses,
            dependentPasses,
            updateProgress,
            threadsToStartCountdown);
      }
    }

    for (Pair<FileEditor, TextEditorHighlightingPass> pair : passesWithNoDocuments) {
      FileEditor fileEditor = pair.first;
      TextEditorHighlightingPass pass = pair.second;
      createScheduledPass(
          fileEditor,
          pass,
          toBeSubmitted,
          ContainerUtil.emptyList(),
          freePasses,
          dependentPasses,
          updateProgress,
          threadsToStartCountdown);
    }

    if (CHECK_CONSISTENCY && !ApplicationInfoImpl.isInPerformanceTest()) {
      assertConsistency(freePasses, toBeSubmitted, threadsToStartCountdown);
    }

    log(
        updateProgress,
        null,
        vFiles + " ----- starting " + threadsToStartCountdown.get(),
        freePasses);

    for (ScheduledPass dependentPass : dependentPasses) {
      mySubmittedPasses.put(dependentPass, Job.NULL_JOB);
    }
    for (ScheduledPass freePass : freePasses) {
      submit(freePass);
    }
  }