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); } }