void (@NotNull List<? extends PsiElement> elements1, @NotNull List<? extends PsiElement> elements2, @NotNull ProgressIndicator progress, @NotNull PsiLanguageInjectionHost.InjectedPsiVisitor visitor) { ApplicationManager.getApplication().assertReadAccessAllowed(); InjectedLanguageManagerImpl injectedLanguageManager = InjectedLanguageManagerImpl.getInstanceImpl(myProject); List<DocumentWindow> cachedInjected = injectedLanguageManager.getCachedInjectedDocumentsInRange(myFile, myFile.getTextRange()); Collection<PsiElement> hosts = new HashSet<>(elements1.size() + elements2.size() + cachedInjected.size()); PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(myProject); //rehighlight all injected PSI regardless the range, //since change in one place can lead to invalidation of injected PSI in (completely) other place. for (DocumentWindow documentRange : cachedInjected) { ProgressManager.checkCanceled(); if (!documentRange.isValid()) continue; PsiFile file = psiDocumentManager.getPsiFile(documentRange); if (file == null) continue; PsiElement context = injectedLanguageManager.getInjectionHost(file); if (context != null && context.isValid() && !file.getProject().isDisposed() && (myUpdateAll || myRestrictRange.contains(context.getTextRange()))) { // consider strict if partial update if (myReducedRanges != null && !ContainerUtil.exists(myReducedRanges, reducedRange -> reducedRange.contains(context.getTextRange()))) { // skip if not in reduced continue; } hosts.add(context); } } Processor<PsiElement> collectInjectableProcessor = new CommonProcessors.CollectProcessor<>(hosts) { @Override public boolean process(PsiElement t) { ProgressManager.checkCanceled(); if (InjectedLanguageUtil.isInjectable(t, false)) { super.process(t); } return true; } }; injectedLanguageManager.processInjectableElements(elements1, collectInjectableProcessor); injectedLanguageManager.processInjectableElements(elements2, collectInjectableProcessor); // the most expensive process is running injectors for these hosts, comparing to highlighting the resulting injected fragments, // so instead of showing "highlighted 1% of injected fragments", show "ran injectors for 1% of hosts" setProgressLimit(hosts.size()); Set<PsiElement> visitedInjected = ConcurrentCollectionFactory.createConcurrentSet(); // in case of concatenation, multiple hosts can return the same injected fragment. have to visit it only once if (!JobLauncher.getInstance().invokeConcurrentlyUnderProgress(new ArrayList<>(hosts), progress, element -> { ApplicationManager.getApplication().assertReadAccessAllowed(); injectedLanguageManager.enumerateEx(element, myFile, false, (injectedPsi, places) -> { if (visitedInjected.add(injectedPsi)) { visitor.visit(injectedPsi, places); } }); advanceProgress(1); return true; })) { throw new ProcessCanceledException(); } }