@Override
  protected void runTools(@NotNull AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections) {
    IJTracer tracer = TelemetryManager.getInstance().getTracer(GlobalInspectionScopeKt.GlobalInspectionScope);
    runToolsSpan = tracer.spanBuilder("globalInspections").setNoParent().startSpan();
    myInspectionStartedTimestamp = System.currentTimeMillis();
    ProgressIndicator progressIndicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
    if (!(progressIndicator instanceof ProgressIndicatorWithDelayedPresentation)) {
      throw new IncorrectOperationException("Must be run under ProgressWindow");
    }
    if (!isOfflineInspections) {
      ApplicationManager.getApplication().assertIsNonDispatchThread();
    }
    if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
      throw new IncorrectOperationException("Must not start inspections from within write action");
    }
    // in offline inspection application we don't care about global read action
    if (!isOfflineInspections && ApplicationManager.getApplication().isReadAccessAllowed()) {
      throw new IncorrectOperationException("Must not start inspections from within global read action");
    }
    InspectionManager inspectionManager = InspectionManager.getInstance(getProject());
    ((RefManagerImpl)getRefManager()).initializeAnnotators();
    List<Tools> globalTools = new ArrayList<>();
    List<Tools> localTools = new ArrayList<>();
    List<Tools> globalSimpleTools = new ArrayList<>();
    initializeTools(globalTools, localTools, globalSimpleTools);

    runGlobalTools(scope, inspectionManager, globalTools, isOfflineInspections);
    TraceUtil.runWithSpanThrows(tracer, "externalInspectionsAnalysis", (__) -> {
      runExternalTools();
    });

    if (runGlobalToolsOnly || localTools.isEmpty() && globalSimpleTools.isEmpty()) return;

    SearchScope searchScope = ReadAction.compute(scope::toSearchScope);
    Set<VirtualFile> localScopeFiles = searchScope instanceof LocalSearchScope ? new HashSet<>() : null;
    for (Tools tools : globalSimpleTools) {
      GlobalInspectionToolWrapper toolWrapper = (GlobalInspectionToolWrapper)tools.getTool();
      if (toolWrapper.getTool() instanceof  GlobalSimpleInspectionTool tool) {
        tool.inspectionStarted(inspectionManager, this, getPresentation(toolWrapper));
      }
    }

    boolean headlessEnvironment = ApplicationManager.getApplication().isHeadlessEnvironment();

    Map<String, InspectionToolWrapper<?, ?>> map = getInspectionWrappersMap(localTools);

    BlockingQueue<VirtualFile> filesToInspect = new ArrayBlockingQueue<>(1000);
    // use the original progress indicator here since we don't want it to cancel on write action start
    ProgressIndicator fileScanningIndicator = new SensitiveProgressWrapper(progressIndicator);
    Future<?> future = startIterateScopeInBackground(scope, fileScanningIndicator, headlessEnvironment, localScopeFiles, filesToInspect);

    var enabledInspectionsProvider = createEnabledInspectionsProvider(localTools, globalSimpleTools, getProject());
    Processor<VirtualFile> processor = buildProcessor(scope, enabledInspectionsProvider, searchScope, inspectionManager, map);
    var localInspectionsSpan = tracer.spanBuilder("localInspectionsAnalysis").startSpan();
    try {
      Queue<VirtualFile> filesFailedToInspect = new LinkedBlockingQueue<>();
      while (true) {
        Disposable disposable = Disposer.newDisposable();
        ProgressIndicator wrapper = new DaemonProgressIndicator();
        dependentIndicators.add(wrapper);
        try {
          setupCancelOnWriteProgress(disposable, wrapper);
          // use wrapper here to cancel early when write action start but do not affect the original indicator
          ((JobLauncherImpl)JobLauncher.getInstance()).processQueue(filesToInspect, filesFailedToInspect, wrapper, TOMBSTONE, processor);
          break;
        }
        catch (ProcessCanceledException e) {
          progressIndicator.checkCanceled();
          // PCE may be thrown from inside wrapper when write action started.
          // Go on with the write action and then resume processing the rest of the queue.
          if (!isOfflineInspections) {
            ApplicationManager.getApplication().assertReadAccessNotAllowed();
            ApplicationManager.getApplication().assertIsNonDispatchThread();
          }

          // wait for write action to complete
          ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance());
        }
        finally {
          dependentIndicators.remove(wrapper);
          Disposer.dispose(disposable);
        }
      }
    }
    finally {
      fileScanningIndicator.cancel(); // tell file scanning thread to stop
      filesToInspect.clear(); // let file scanning thread a chance to put TOMBSTONE and complete
      try {
        future.get(30, TimeUnit.SECONDS);
      }
      catch (Exception e) {
        LOG.error("Thread dump: \n" + ThreadDumper.dumpThreadsToString(), e);
      }
      localInspectionsSpan.end();
    }

    ProgressManager.checkCanceled();

    for (Tools tools : globalSimpleTools) {
      GlobalInspectionToolWrapper toolWrapper = (GlobalInspectionToolWrapper)tools.getTool();
      if (toolWrapper.getTool() instanceof GlobalSimpleInspectionTool tool) {
        ProblemDescriptionsProcessor problemDescriptionProcessor = getProblemDescriptionProcessor(toolWrapper, map);
        tool.inspectionFinished(inspectionManager, this, problemDescriptionProcessor);
      }
    }

    addProblemsToView(globalSimpleTools);
  }
@Override
  protected void runTools(@NotNull AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections)