@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); }
public void startRunInjectors(@NotNull final Document hostDocument, final boolean synchronously) { if (myProject.isDisposed()) return; if (!synchronously && ApplicationManager.getApplication().isWriteAccessAllowed()) return; // use cached to avoid recreate PSI in alien project final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); final PsiFile hostPsiFile = documentManager.getCachedPsiFile(hostDocument); if (hostPsiFile == null) return; final CopyOnWriteArrayList<DocumentWindow> injected = (CopyOnWriteArrayList<DocumentWindow>) InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile); if (injected.isEmpty()) return; if (myProgress.isCanceled()) { myProgress = new DaemonProgressIndicator(); } final Processor<DocumentWindow> commitProcessor = new Processor<DocumentWindow>() { @Override public boolean process(DocumentWindow documentWindow) { if (myProject.isDisposed()) return false; ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null && indicator.isCanceled()) return false; if (documentManager.isUncommited(hostDocument) || !hostPsiFile.isValid()) return false; // will be committed later Segment[] ranges = documentWindow.getHostRanges(); Segment rangeMarker = ranges.length > 0 ? ranges[0] : null; PsiElement element = rangeMarker == null ? null : hostPsiFile.findElementAt(rangeMarker.getStartOffset()); if (element == null) { synchronized (PsiLock.LOCK) { injected.remove(documentWindow); } return true; } final DocumentWindow[] stillInjectedDocument = {null}; // it is here where the reparse happens and old file contents replaced InjectedLanguageUtil.enumerate( element, hostPsiFile, true, new PsiLanguageInjectionHost.InjectedPsiVisitor() { @Override public void visit( @NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) { stillInjectedDocument[0] = (DocumentWindow) injectedPsi.getViewProvider().getDocument(); PsiDocumentManagerImpl.checkConsistency(injectedPsi, stillInjectedDocument[0]); } }); synchronized (PsiLock.LOCK) { if (stillInjectedDocument[0] == null) { injected.remove(documentWindow); } else if (stillInjectedDocument[0] != documentWindow) { injected.remove(documentWindow); injected.addIfAbsent(stillInjectedDocument[0]); } } return true; } }; final Runnable commitInjectionsRunnable = new Runnable() { @Override public void run() { if (myProgress.isCanceled()) return; JobLauncher.getInstance() .invokeConcurrentlyUnderProgress( new ArrayList<DocumentWindow>(injected), myProgress, !synchronously, commitProcessor); } }; if (synchronously) { if (Thread.holdsLock(PsiLock.LOCK)) { // hack for the case when docCommit was called from within PSI modification, e.g. in // formatter. // we can't spawn threads to do injections there, otherwise a deadlock is imminent ContainerUtil.process(new ArrayList<DocumentWindow>(injected), commitProcessor); } else { commitInjectionsRunnable.run(); } } else { JobLauncher.getInstance() .submitToJobThread( Job.DEFAULT_PRIORITY, new Runnable() { @Override public void run() { ApplicationManagerEx.getApplicationEx() .tryRunReadAction(commitInjectionsRunnable); } }); } }
@Override public void startRunInjectors(@NotNull final Document hostDocument, final boolean synchronously) { if (myProject.isDisposed()) return; if (!synchronously && ApplicationManager.getApplication().isWriteAccessAllowed()) return; // use cached to avoid recreate PSI in alien project final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); final PsiFile hostPsiFile = documentManager.getCachedPsiFile(hostDocument); if (hostPsiFile == null) return; final ConcurrentList<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile); if (injected.isEmpty()) return; if (myProgress.isCanceled()) { myProgress = new DaemonProgressIndicator(); } final Set<DocumentWindow> newDocuments = Collections.synchronizedSet(new THashSet<>()); final Processor<DocumentWindow> commitProcessor = documentWindow -> { if (myProject.isDisposed()) return false; ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null && indicator.isCanceled()) return false; if (documentManager.isUncommited(hostDocument) || !hostPsiFile.isValid()) return false; // will be committed later // it is here where the reparse happens and old file contents replaced InjectedLanguageUtil.enumerate( documentWindow, hostPsiFile, (injectedPsi, places) -> { DocumentWindow newDocument = (DocumentWindow) injectedPsi.getViewProvider().getDocument(); if (newDocument != null) { PsiDocumentManagerBase.checkConsistency(injectedPsi, newDocument); newDocuments.add(newDocument); } }); return true; }; final Runnable commitInjectionsRunnable = () -> { if (myProgress.isCanceled()) return; JobLauncher.getInstance() .invokeConcurrentlyUnderProgress( new ArrayList<>(injected), myProgress, true, commitProcessor); synchronized (ourInjectionPsiLock) { injected.clear(); injected.addAll(newDocuments); } }; if (synchronously) { if (Thread.holdsLock(PsiLock.LOCK)) { // hack for the case when docCommit was called from within PSI modification, e.g. in // formatter. // we can't spawn threads to do injections there, otherwise a deadlock is imminent ContainerUtil.process(new ArrayList<>(injected), commitProcessor); } else { commitInjectionsRunnable.run(); } } else { JobLauncher.getInstance() .submitToJobThread( () -> ApplicationManagerEx.getApplicationEx() .tryRunReadAction(commitInjectionsRunnable), null); } }
@Override public void dispose() { myProgress.cancel(); EditorWindowImpl.disposeInvalidEditors(); }