@Override public void visitFile(PsiFile file) { final VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile == null) { return; } final Project project = file.getProject(); Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document == null) return; final HighlightInfoFilter[] filters = ApplicationManager.getApplication() .getExtensions(HighlightInfoFilter.EXTENSION_POINT_NAME); GeneralHighlightingPass pass = new GeneralHighlightingPass(project, file, document, 0, file.getTextLength(), true) { @NotNull @Override protected HighlightVisitor[] createHighlightVisitors() { return new HighlightVisitor[] { new DefaultHighlightVisitor(project, highlightErrorElements, runAnnotators, true) }; } @Override protected HighlightInfoHolder createInfoHolder(final PsiFile file) { return new HighlightInfoHolder(file, getColorsScheme(), filters) { @Override public boolean add(@Nullable HighlightInfo info) { if (info == null) return true; if (info.type == HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT) return true; if (info.getSeverity() == HighlightSeverity.INFORMATION) return true; result.add(Pair.create(file, info)); return true; } }; } @Override protected void killAbandonedHighlightsUnder( @NotNull TextRange range, @Nullable List<HighlightInfo> holder, @NotNull ProgressIndicator progress) { // do not mess with real editor highlights } @Override protected boolean isFailFastOnAcquireReadAction() { return false; } }; DaemonProgressIndicator progress = new DaemonProgressIndicator(); progress.start(); pass.collectInformation(progress); }
private synchronized DaemonProgressIndicator createUpdateProgress() { DaemonProgressIndicator progress = new DaemonProgressIndicator() { @Override public void stopIfRunning() { super.stopIfRunning(); myProject.getMessageBus().syncPublisher(DAEMON_EVENT_TOPIC).daemonFinished(); } }; progress.start(); myUpdateProgress = progress; return progress; }
public DaemonCodeAnalyzerImpl( @NotNull Project project, DaemonCodeAnalyzerSettings daemonCodeAnalyzerSettings, EditorTracker editorTracker) { myProject = project; mySettings = daemonCodeAnalyzerSettings; myEditorTracker = editorTracker; myLastSettings = (DaemonCodeAnalyzerSettings) mySettings.clone(); myFileStatusMap = new FileStatusMap(myProject); myPassExecutorService = new PassExecutorService(myProject) { @Override protected void afterApplyInformationToEditor( final TextEditorHighlightingPass pass, @NotNull final FileEditor fileEditor, final ProgressIndicator updateProgress) { if (fileEditor instanceof TextEditor) { log(updateProgress, pass, "Apply "); Editor editor = ((TextEditor) fileEditor).getEditor(); repaintErrorStripeRenderer(editor); } } @Override protected boolean isDisposed() { return myDisposed || super.isDisposed(); } }; Disposer.register(project, myPassExecutorService); Disposer.register(project, myFileStatusMap); DaemonProgressIndicator.setDebug(LOG.isDebugEnabled()); }
private synchronized void cancelUpdateProgress(final boolean start, @NonNls String reason) { PassExecutorService.log(myUpdateProgress, null, reason, start); myModificationCount++; if (myUpdateProgress != null) { myUpdateProgress.cancel(); myPassExecutorService.cancelAll(false); myUpdateProgress = null; } }
@TestOnly static Throwable getSavedException(@NotNull DaemonProgressIndicator indicator) { return indicator.getUserData(THROWABLE_KEY); }
private static void saveException( @NotNull Throwable e, @NotNull DaemonProgressIndicator indicator) { indicator.putUserDataIfAbsent(THROWABLE_KEY, e); }
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); } } } }
public synchronized boolean isRunning() { return myUpdateProgress != null && !myUpdateProgress.isCanceled(); }
@TestOnly public List<HighlightInfo> runPasses( @NotNull PsiFile file, @NotNull Document document, @NotNull TextEditor textEditor, @NotNull int[] toIgnore, boolean canChangeDocument, @Nullable Runnable callbackWhileWaiting) { assert myInitialized; assert !myDisposed; Application application = ApplicationManager.getApplication(); application.assertIsDispatchThread(); assert !application.isWriteAccessAllowed(); // pump first so that queued event do not interfere UIUtil.dispatchAllInvocationEvents(); UIUtil.dispatchAllInvocationEvents(); Project project = file.getProject(); setUpdateByTimerEnabled(false); FileStatusMap fileStatusMap = getFileStatusMap(); for (int ignoreId : toIgnore) { fileStatusMap.markFileUpToDate(document, ignoreId); } fileStatusMap.allowDirt(canChangeDocument); TextEditorBackgroundHighlighter highlighter = (TextEditorBackgroundHighlighter) textEditor.getBackgroundHighlighter(); final List<TextEditorHighlightingPass> passes = highlighter.getPasses(toIgnore); HighlightingPass[] array = passes.toArray(new HighlightingPass[passes.size()]); final DaemonProgressIndicator progress = createUpdateProgress(); progress.setDebug(LOG.isDebugEnabled()); myPassExecutorService.submitPasses( Collections.singletonMap((FileEditor) textEditor, array), progress, Job.DEFAULT_PRIORITY); try { while (progress.isRunning()) { try { if (progress.isCanceled() && progress.isRunning()) { // write action sneaked in the AWT. restart waitForTermination(); Throwable savedException = PassExecutorService.getSavedException(progress); if (savedException != null) throw savedException; return runPasses( file, document, textEditor, toIgnore, canChangeDocument, callbackWhileWaiting); } if (callbackWhileWaiting != null) { callbackWhileWaiting.run(); } progress.waitFor(100); UIUtil.dispatchAllInvocationEvents(); Throwable savedException = PassExecutorService.getSavedException(progress); if (savedException != null) throw savedException; } catch (RuntimeException e) { throw e; } catch (Error e) { e.printStackTrace(); throw e; } catch (Throwable e) { e.printStackTrace(); throw new RuntimeException(e); } } UIUtil.dispatchAllInvocationEvents(); UIUtil.dispatchAllInvocationEvents(); return getHighlights(document, null, project); } finally { fileStatusMap.allowDirt(true); waitForTermination(); } }