void (@NotNull List<? extends VirtualFile> files, @NotNull Map<? super Document, ? super List<HighlightInfo>> result, @NotNull ProgressIndicatorEx progress, @Nullable HighlightSeverity minimumSeverity) { ApplicationManager.getApplication().assertIsNonDispatchThread(); ApplicationManager.getApplication().assertReadAccessNotAllowed(); progress.setIndeterminate(false); List<Pair<VirtualFile, DaemonProgressIndicator>> daemonIndicators = Collections.synchronizedList(new ArrayList<>(files.size())); progress.addStateDelegate(new AbstractProgressIndicatorExBase() { @Override public void cancel() { super.cancel(); daemonIndicators.forEach(daemonIndicator -> daemonIndicator.getSecond().cancel()); } }); while (true) { daemonIndicators.clear(); daemonIndicators.addAll(ContainerUtil.map(files, file -> Pair.create(file, new DaemonProgressIndicator()))); Disposable disposable = Disposer.newDisposable(); try { SensitiveProgressWrapper wrapper = new SensitiveProgressWrapper(progress); ReadAction.run(() -> { ApplicationManager.getApplication().addApplicationListener(new ApplicationListener() { @Override public void beforeWriteActionStart(@NotNull Object action) { wrapper.cancel(); daemonIndicators.forEach(daemonIndicator -> daemonIndicator.getSecond().cancel()); } }, disposable); // there is a chance we are racing with the write action, in which case just registered listener might not be called, retry. if (ApplicationManagerEx.getApplicationEx().isWriteActionPending()) { throw new ProcessCanceledException(); } }); AtomicInteger filesCompleted = new AtomicInteger(); JobLauncher.getInstance().invokeConcurrentlyUnderProgress(daemonIndicators, wrapper, pair -> { VirtualFile file = pair.getFirst(); wrapper.setText(ReadAction.compute(() -> ProjectUtil.calcRelativeToProjectPath(file, myProject))); DaemonProgressIndicator daemonIndicator = pair.getSecond(); runMainPasses(file, result, daemonIndicator, minimumSeverity); int completed = filesCompleted.incrementAndGet(); wrapper.setFraction((double)completed / files.size()); return true; }); break; } catch (ProcessCanceledException e) { if (progress.isCanceled()) { throw e; } //retry if one of the daemonIndicators was canceled by started write action } finally { Disposer.dispose(disposable); } WriteAction.runAndWait(() -> {}); // wait until the current write action is finished } }