run injections in background upon commit document

+ closure friendly API
This commit is contained in:
Alexey Kudravtsev
2011-08-31 16:32:11 +04:00
parent fecfa36bfd
commit 6b20885666
21 changed files with 262 additions and 179 deletions
@@ -113,11 +113,11 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
private void registerReferencesFromInjectedFragments(final PsiElement element) {
InjectedLanguageUtil.enumerate(element, myFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
InjectedLanguageUtil.enumerate(element, myFile, false, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull final PsiFile injectedPsi, @NotNull final List<PsiLanguageInjectionHost.Shred> places) {
injectedPsi.accept(REGISTER_REFERENCES_VISITOR);
}
}, false);
});
}
public boolean analyze(@NotNull final Runnable action, final boolean updateWholeFile, @NotNull final PsiFile file) {
@@ -131,7 +131,7 @@ public class VfsUtilTest extends IdeaTestCase {
public void testAsyncRefresh() throws Throwable {
final Throwable[] ex = {null};
JobUtil.invokeConcurrentlyUnderProgress(Arrays.asList(new Object[8]), new Processor<Object>() {
JobUtil.invokeConcurrentlyUnderProgress(Arrays.asList(new Object[8]), ProgressManager.getInstance().getProgressIndicator(), false, new Processor<Object>() {
@Override
public boolean process(Object o) {
try {
@@ -142,7 +142,7 @@ public class VfsUtilTest extends IdeaTestCase {
}
return true;
}
}, false, ProgressManager.getInstance().getProgressIndicator());
});
if (ex[0] != null) throw ex[0];
}
@@ -467,7 +467,7 @@ public class VirtualFilePointerTest extends IdeaTestCase {
private static void doit(final VirtualFilePointer pointer) {
if (((VirtualFilePointerImpl)pointer).isDisposed()) return;
boolean b = JobUtil.invokeConcurrentlyUnderProgress(Collections.nCopies(10, null), new Processor<Object>() {
boolean b = JobUtil.invokeConcurrentlyUnderProgress(Collections.nCopies(10, null), null, false, new Processor<Object>() {
@Override
public boolean process(Object o) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@@ -481,7 +481,7 @@ public class VirtualFilePointerTest extends IdeaTestCase {
return true;
}
}, false, null);
});
assertTrue(b);
}
@@ -314,14 +314,14 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
}
};
if (!JobUtil.invokeConcurrentlyUnderProgress(new ArrayList<PsiElement>(hosts), new Processor<PsiElement>() {
if (!JobUtil.invokeConcurrentlyUnderProgress(new ArrayList<PsiElement>(hosts), progress, false, new Processor<PsiElement>() {
@Override
public boolean process(PsiElement element) {
progress.checkCanceled();
InjectedLanguageUtil.enumerate(element, myFile, visitor, false);
InjectedLanguageUtil.enumerate(element, myFile, false, visitor);
return true;
}
}, false, progress)) throw new ProcessCanceledException();
})) throw new ProcessCanceledException();
}
// returns false if canceled
@@ -332,7 +332,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(myProject);
final TextAttributes injectedAttributes = myGlobalScheme.getAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT);
return JobUtil.invokeConcurrentlyUnderProgress(new ArrayList<PsiFile>(injectedFiles), new Processor<PsiFile>() {
return JobUtil.invokeConcurrentlyUnderProgress(new ArrayList<PsiFile>(injectedFiles), progress, myFailFastOnAcquireReadAction, new Processor<PsiFile>() {
public boolean process(final PsiFile injectedPsi) {
DocumentWindow documentWindow = (DocumentWindow)PsiDocumentManager.getInstance(myProject).getCachedDocument(injectedPsi);
if (documentWindow == null) return true;
@@ -383,7 +383,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
return true;
}
}, myFailFastOnAcquireReadAction, progress);
});
}
private static TextRange getFixedTextRange(@NotNull DocumentWindow documentWindow, int startOffset) {
@@ -148,11 +148,11 @@ public class LineMarkersPass extends ProgressableTextEditorHighlightingPass impl
final Set<PsiFile> injectedFiles = new THashSet<PsiFile>();
for (PsiElement element : elements) {
InjectedLanguageUtil.enumerate(element, file, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
InjectedLanguageUtil.enumerate(element, file, false, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull final PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
injectedFiles.add(injectedPsi);
}
}, false);
});
}
for (PsiFile injectedPsi : injectedFiles) {
final Project project = injectedPsi.getProject();
@@ -239,7 +239,7 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
@NotNull final List<PsiElement> elements,
@NotNull final LocalInspectionToolSession session,
@NotNull final List<Trinity<LocalInspectionTool, ProblemsHolder, PsiElementVisitor>> init) {
boolean result = JobUtil.invokeConcurrentlyUnderProgress(tools, new Processor<LocalInspectionTool>() {
boolean result = JobUtil.invokeConcurrentlyUnderProgress(tools, indicator, myFailFastOnAcquireReadAction, new Processor<LocalInspectionTool>() {
public boolean process(final LocalInspectionTool tool) {
indicator.checkCanceled();
@@ -268,7 +268,7 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
applyIncrementally[0] = false; // do not apply incrementally outside visible range
return true;
}
}, myFailFastOnAcquireReadAction, indicator);
});
if (!result) throw new ProcessCanceledException();
inspectInjectedPsi(elements, tools, isOnTheFly, indicator, iManager, true);
}
@@ -321,7 +321,7 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
return true;
}
};
boolean result = JobUtil.invokeConcurrentlyUnderProgress(init, processor, myFailFastOnAcquireReadAction, indicator);
boolean result = JobUtil.invokeConcurrentlyUnderProgress(init, indicator, myFailFastOnAcquireReadAction, processor);
if (!result) {
throw new ProcessCanceledException();
}
@@ -345,19 +345,19 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
final boolean inVisibleRange) {
final Set<PsiFile> injected = new THashSet<PsiFile>();
for (PsiElement element : elements) {
InjectedLanguageUtil.enumerate(element, myFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
InjectedLanguageUtil.enumerate(element, myFile, false, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
injected.add(injectedPsi);
}
}, false);
});
}
if (injected.isEmpty()) return;
if (!JobUtil.invokeConcurrentlyUnderProgress(new ArrayList<PsiFile>(injected), new Processor<PsiFile>() {
if (!JobUtil.invokeConcurrentlyUnderProgress(new ArrayList<PsiFile>(injected), indicator, myFailFastOnAcquireReadAction, new Processor<PsiFile>() {
public boolean process(final PsiFile injectedPsi) {
doInspectInjectedPsi(injectedPsi, tools, onTheFly, indicator, iManager, inVisibleRange);
return true;
}
}, myFailFastOnAcquireReadAction, indicator)) throw new ProcessCanceledException();
})) throw new ProcessCanceledException();
}
@Nullable
@@ -267,7 +267,7 @@ public abstract class PassExecutorService implements Disposable {
private void submit(final ScheduledPass pass) {
if (!pass.myUpdateProgress.isCanceled()) {
Job<Void> job = JobUtil.submitToJobThread(pass, pass.myJobPriority, new Consumer<Future>() {
Job<Void> job = JobUtil.submitToJobThread(pass.myJobPriority, pass, new Consumer<Future>() {
@Override
public void consume(Future future) {
try {
@@ -107,7 +107,7 @@ public class BraceHighlightingHandler {
final Project project = editor.getProject();
if (project == null) return;
final int offset = editor.getCaretModel().getOffset();
JobUtil.submitToJobThread(new Runnable() {
JobUtil.submitToJobThread(Job.DEFAULT_PRIORITY, new Runnable() {
public void run() {
final PsiFile injected;
try {
@@ -145,7 +145,7 @@ public class BraceHighlightingHandler {
}
}, ModalityState.stateForComponent(editor.getComponent()));
}
}, Job.DEFAULT_PRIORITY);
});
}
private static boolean isReallyDisposed(Editor editor, Project project) {
@@ -546,7 +546,7 @@ public class GlobalInspectionContextImpl extends UserDataHolderBase implements G
}
pass.doInspectInBatch((InspectionManagerEx)manager, lTools);
JobUtil.invokeConcurrentlyUnderProgress(globalSimpleTools, new Processor<Tools>() {
JobUtil.invokeConcurrentlyUnderProgress(globalSimpleTools, myProgressIndicator, false, new Processor<Tools>() {
@Override
public boolean process(Tools tools) {
GlobalInspectionToolWrapper toolWrapper = (GlobalInspectionToolWrapper)tools.getTool();
@@ -557,7 +557,7 @@ public class GlobalInspectionContextImpl extends UserDataHolderBase implements G
CONVERT, toolWrapper);
return true;
}
}, false, myProgressIndicator);
});
}
catch (ProcessCanceledException e) {
throw e;
@@ -54,7 +54,7 @@ public abstract class ContributorsBasedGotoByModel implements ChooseByNameModel
public String[] getNames(final boolean checkBoxState) {
final Set<String> names = new ConcurrentHashSet<String>();
JobUtil.invokeConcurrentlyUnderProgress(filterDumb(myContributors), new Processor<ChooseByNameContributor>() {
JobUtil.invokeConcurrentlyUnderProgress(filterDumb(myContributors), ProgressManager.getInstance().getProgressIndicator(), false, new Processor<ChooseByNameContributor>() {
@Override
public boolean process(ChooseByNameContributor contributor) {
try {
@@ -71,7 +71,7 @@ public abstract class ContributorsBasedGotoByModel implements ChooseByNameModel
}
return true;
}
}, false, ProgressManager.getInstance().getProgressIndicator());
});
return ArrayUtil.toStringArray(names);
}
@@ -101,7 +101,7 @@ public abstract class ContributorsBasedGotoByModel implements ChooseByNameModel
public Object[] getElementsByName(final String name, final boolean checkBoxState, final String pattern) {
final List<NavigationItem> items = Collections.synchronizedList(new ArrayList<NavigationItem>());
JobUtil.invokeConcurrentlyUnderProgress(filterDumb(myContributors), new Processor<ChooseByNameContributor>() {
JobUtil.invokeConcurrentlyUnderProgress(filterDumb(myContributors), ProgressManager.getInstance().getProgressIndicator(), false, new Processor<ChooseByNameContributor>() {
@Override
public boolean process(ChooseByNameContributor contributor) {
try {
@@ -124,7 +124,7 @@ public abstract class ContributorsBasedGotoByModel implements ChooseByNameModel
}
return true;
}
}, false, ProgressManager.getInstance().getProgressIndicator());
});
return ArrayUtil.toObjectArray(items);
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
@@ -44,6 +43,7 @@ import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.text.BlockSupport;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.containers.Queue;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
@@ -52,7 +52,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -127,20 +126,23 @@ public class DocumentCommitThread implements Runnable, Disposable {
useIndicator(null);
}
public boolean queueCommit(@NonNls @NotNull String reason, @NotNull Document document, @NotNull Project project, @Nullable DocumentEvent event) {
log("queueCommit called", document, false, reason, event);
public boolean queueCommit(@NotNull Project project, @NotNull Document document, @NonNls @NotNull Object reason) {
log("queueCommit called", document, false, reason);
assert !isDisposed : "already disposed";
if (!project.isInitialized()) return false;
PsiFile psiFile = PsiDocumentManager.getInstance(project).getCachedPsiFile(document);
if (psiFile == null) return false;
boolean added = doQueue(document, project, getCommitStage(document), reason, event);
boolean added = doQueue(document, project, getCommitStage(document), reason);
log("doQueue called", document, false, added);
return added;
}
private boolean doQueue(@NotNull Document document, @NotNull Project project, CommitStage start, @NonNls @NotNull String reason, @Nullable DocumentEvent event) {
private boolean doQueue(@NotNull Document document,
@NotNull Project project,
CommitStage start,
@NonNls @NotNull Object reason) {
synchronized (documentsToCommit) {
if (!changeCommitStage(document, start, CommitStage.QUEUED_TO_COMMIT, false)) return false;
@@ -163,7 +165,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
}
ProgressIndicator indicator = new ProgressIndicatorBase();
indicator.start();
documentsToCommit.addLast(new CommitTask(document, project, indicator, reason, event));
documentsToCommit.addLast(new CommitTask(document, project, indicator, reason));
log("Queued", document, false, reason);
return true;
}
@@ -172,6 +174,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
private final StringBuilder log = new StringBuilder();
void log(@NonNls String msg, Document document, boolean synchronously, @NonNls Object... args) {
if (debug()) {
@NonNls
String s = (SwingUtilities.isEventDispatchThread() ? " " : "") +
msg + (synchronously ? " (sync)" : "") +
(document == null ? "" : "; Document: " + System.identityHashCode(document) +
@@ -218,19 +221,16 @@ public class DocumentCommitThread implements Runnable, Disposable {
// running = false means document was removed from the queue, should ignore.
// canceled = true means commit was canceled, should reschedule for later.
private final ProgressIndicator indicator; // progress to commit this doc under.
private final String reason;
@Nullable private final DocumentEvent event;
private final Object reason;
private CommitTask(@NotNull Document document,
@NotNull Project project,
@NotNull ProgressIndicator indicator,
@NotNull String reason,
@Nullable DocumentEvent event) {
@NotNull Object reason) {
this.document = document;
this.project = project;
this.indicator = indicator;
this.reason = reason;
this.event = event;
}
}
@@ -275,7 +275,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
CommitStage commitStage = getCommitStage(document);
Document[] uncommitted = null;
if (commitStage != CommitStage.QUEUED_TO_COMMIT
|| project.isDisposed() || !ArrayUtil.contains(document,(uncommitted = PsiDocumentManager.getInstance(project).getUncommittedDocuments()))) {
|| project.isDisposed() || !ArrayUtil.contains(document, uncommitted = PsiDocumentManager.getInstance(project).getUncommittedDocuments())) {
List<Document> documents = uncommitted == null ? null : Arrays.asList(uncommitted);
log("Abandon and proceeding to next",document, false, commitStage, documents);
continue;
@@ -292,7 +292,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
Runnable finishRunnable = null;
if (!success && !indicator.isCanceled()) {
try {
finishRunnable = commit(document, project, null, indicator, false, task.reason, task.event);
finishRunnable = commit(document, project, null, indicator, false, task.reason);
success = finishRunnable != null;
log("DCT.commit returned", document, false, finishRunnable, indicator);
}
@@ -312,8 +312,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
}
}
catch (ProcessCanceledException e) {
int i = 0; // leave queue unchanged
cancel(e);
cancel(e); // leave queue unchanged
log("PCE", document, false, e);
success = false;
}
@@ -332,7 +331,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
// reset status for queue back successfully
changeCommitStage(document, CommitStage.WAITING_FOR_PSI_APPLY, CommitStage.QUEUED_TO_COMMIT, false);
changeCommitStage(document, CommitStage.COMMITTED, CommitStage.QUEUED_TO_COMMIT, false);
doQueue(document, project, CommitStage.QUEUED_TO_COMMIT, "re-added on failure", null);
doQueue(document, project, CommitStage.QUEUED_TO_COMMIT, "re-added on failure");
}
}
}
@@ -371,7 +370,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
ProgressIndicatorBase indicator = new ProgressIndicatorBase();
indicator.start();
log("About to commit sync", document, true, indicator);
Runnable finish = commit(document, project, excludeFile, indicator, true, "Sync commit", null);
Runnable finish = commit(document, project, excludeFile, indicator, true, "Sync commit");
log("Committed sync", document, true, finish, indicator);
assert finish != null;
@@ -383,13 +382,12 @@ public class DocumentCommitThread implements Runnable, Disposable {
final PsiFile excludeFile,
@NotNull final ProgressIndicator indicator,
final boolean synchronously,
@NotNull final String reason,
@Nullable final DocumentEvent event) {
@NotNull final Object reason) {
final Runnable[] success = new Runnable[1];
((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(new Runnable() {
@Override
public void run() {
success[0] = commitUnderProgress(document, project, excludeFile, indicator, synchronously, reason, event);
success[0] = commitUnderProgress(document, project, excludeFile, indicator, synchronously, reason);
}
}, indicator);
return success[0];
@@ -411,9 +409,8 @@ public class DocumentCommitThread implements Runnable, Disposable {
final PsiFile excludeFile,
@NotNull final ProgressIndicator indicator,
final boolean synchronously,
@NotNull final String reason,
@Nullable final DocumentEvent event) {
final List<Processor<Document>> finishRunnables = new ArrayList<Processor<Document>>();
@NotNull final Object reason) {
final List<Processor<Document>> finishRunnables = new SmartList<Processor<Document>>();
Runnable runnable = new Runnable() {
public void run() {
if (project.isDisposed()) return;
@@ -432,7 +429,7 @@ public class DocumentCommitThread implements Runnable, Disposable {
}
};
if (synchronously) {
ApplicationManager.getApplication().assertIsDispatchThread();
ApplicationManager.getApplication().assertWriteAccessAllowed();
runnable.run();
}
else {
@@ -458,45 +455,38 @@ public class DocumentCommitThread implements Runnable, Disposable {
@Override
public void run() {
if (project.isDisposed()) return;
ApplicationManager.getApplication().runWriteAction(new CommitToPsiFileAction(document, project) {
public void run() {
if (project.isDisposed()) return;
PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project);
CommitStage stage = getCommitStage(document);
log("Finish", document, synchronously, project);
if (stage != (synchronously ? CommitStage.ABOUT_TO_BE_SYNC_COMMITTED : CommitStage.WAITING_FOR_PSI_APPLY)) {
return; // there must be a synchronous commit sneaked in between queued commit and finish commit, or just document changed meanwhile
}
PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project);
boolean success = false;
try {
success = documentManager.finishCommit(document, finishRunnables, synchronously, reason, event);
log("Finished", document, synchronously, success, Arrays.asList(documentManager.getUncommittedDocuments()));
if (synchronously) {
assert success;
}
}
finally {
if (success) {
CommitStage before = getCommitStage(document);
boolean changed = synchronously || changeCommitStage(document, CommitStage.WAITING_FOR_PSI_APPLY, CommitStage.COMMITTED, false);
assert changed : "Before: " + before + "; after: " + getCommitStage(document);
}
}
List<Document> unc = Arrays.asList(documentManager.getUncommittedDocuments());
log("after call finish commit",document, synchronously, unc, success);
if (synchronously || success) {
assert !unc.contains(document) : unc;
}
if (!success) {
// add document back to the queue
log("Re-adding back", document, synchronously, success, documentManager.isCommitted(document), Arrays.asList(documentManager.getUncommittedDocuments()));
boolean addedBack = queueCommit("Re-added back", document, project, null);
assert addedBack;
}
CommitStage stage = getCommitStage(document);
log("Finish", document, synchronously, project);
if (stage != (synchronously ? CommitStage.ABOUT_TO_BE_SYNC_COMMITTED : CommitStage.WAITING_FOR_PSI_APPLY)) {
return; // there must be a synchronous commit sneaked in between queued commit and finish commit, or just document changed meanwhile
}
boolean success = false;
try {
success = documentManager.finishCommit(document, finishRunnables, synchronously, reason);
log("Finished", document, synchronously, success, Arrays.asList(documentManager.getUncommittedDocuments()));
if (synchronously) {
assert success;
}
});
}
finally {
if (success) {
success = synchronously || changeCommitStage(document, CommitStage.WAITING_FOR_PSI_APPLY, CommitStage.COMMITTED, false);
}
}
List<Document> unc = Arrays.asList(documentManager.getUncommittedDocuments());
log("after call finish commit",document, synchronously, unc, success);
if (synchronously || success) {
assert !unc.contains(document) : unc;
}
if (!success) {
// add document back to the queue
boolean addedBack = queueCommit(project, document, "Re-added back");
assert addedBack;
}
}
};
return finishRunnable;
@@ -18,7 +18,9 @@ package com.intellij.psi.impl;
import com.intellij.AppTopics;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.lang.ASTNode;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationAdapter;
import com.intellij.openapi.application.ApplicationManager;
@@ -46,7 +48,7 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl;
import com.intellij.psi.text.BlockSupport;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
@@ -312,59 +314,66 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
}
boolean finishCommit(@NotNull final Document document,
@NotNull List<Processor<Document>> finishRunnables,
boolean synchronously,
@NotNull String reason,
@Nullable DocumentEvent event) {
@NotNull final List<Processor<Document>> finishRunnables,
final boolean synchronously,
@NotNull Object reason) {
if (myProject.isDisposed()) return false;
ApplicationManager.getApplication().assertWriteAccessAllowed();
assert !(document instanceof DocumentWindow);
myIsCommitInProgress = true;
boolean success = true;
try {
final FileViewProvider viewProvider = getCachedViewProvider(document);
if (viewProvider != null) {
for (Processor<Document> finishRunnable : finishRunnables) {
success = finishRunnable.process(document);
if (synchronously) {
assert success;
}
if (!success) {
break;
final boolean[] ok = {true};
ApplicationManager.getApplication().runWriteAction(new CommitToPsiFileAction(document, myProject) {
@Override
public void run() {
if (myProject.isDisposed()) return;
EditorWindow.disposeInvalidEditors(); // in write action
assert !(document instanceof DocumentWindow);
myIsCommitInProgress = true;
boolean success = true;
try {
final FileViewProvider viewProvider = getCachedViewProvider(document);
if (viewProvider != null) {
for (Processor<Document> finishRunnable : finishRunnables) {
success = finishRunnable.process(document);
if (synchronously) {
assert success;
}
if (!success) {
break;
}
}
viewProvider.contentsSynchronized();
}
ok[0] = success;
}
finally {
myDocumentCommitThread.log("in PDI.finishDoc: ", document, synchronously, success, myUncommittedDocuments);
if (success) {
myUncommittedDocuments.remove(document);
myDocumentCommitThread.log("in PDI.finishDoc: removed doc", document, synchronously, success, myUncommittedDocuments);
}
myIsCommitInProgress = false;
myDocumentCommitThread.log("in PDI.finishDoc: exit", document, synchronously, success, myUncommittedDocuments);
}
viewProvider.contentsSynchronized();
}
}
finally {
myDocumentCommitThread.log("in PDI.finishDoc: ",document, synchronously, success, myUncommittedDocuments);
if (success) {
myUncommittedDocuments.remove(document);
myDocumentCommitThread.log("in PDI.finishDoc: removed doc",document, synchronously, success, myUncommittedDocuments);
InjectedLanguageUtil.commitAllInjectedDocuments(document, myProject);
}
myIsCommitInProgress = false;
myDocumentCommitThread.log("in PDI.finishDoc: exit",document, synchronously, success, myUncommittedDocuments);
}
});
if (success) {
if (ok[0]) {
((InjectedLanguageManagerImpl)InjectedLanguageManager.getInstance(myProject)).startRunInjectors(document, synchronously);
// run after commit actions outside write action
runAfterCommitActions(document);
if (DebugUtil.DO_EXPENSIVE_CHECKS) {
checkAllElementsValid(document, reason, event);
checkAllElementsValid(document, reason);
}
}
return success;
return ok[0];
}
private void checkAllElementsValid(@NotNull Document document, @NotNull final String reason, @Nullable final DocumentEvent event) {
private void checkAllElementsValid(@NotNull Document document, @NotNull final Object reason) {
final PsiFile psiFile = getCachedPsiFile(document);
if (psiFile != null) {
psiFile.accept(new PsiRecursiveElementWalkingVisitor() {
@Override
public void visitElement(PsiElement element) {
if (!element.isValid()) {
LOG.error("Commit to '"+psiFile.getVirtualFile()+"' lead to invalid element: "+element+ "; Reason: '"+reason+"'; Doc change event: "+event);
LOG.error("Commit to '"+psiFile.getVirtualFile()+"' lead to invalid element: "+element+ "; Reason: '"+reason+"'");
}
}
});
@@ -616,7 +625,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
if (commitNecessary) {
myUncommittedDocuments.add(document);
myDocumentCommitThread.queueCommit("Document changed", document, myProject, event);
myDocumentCommitThread.queueCommit(myProject, document, event);
}
// Consider that it's worth to perform complete re-parse instead of merge if the whole document text is replaced and
@@ -184,11 +184,11 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
PsiElement[] scopeElements = scope.getScope();
final boolean ignoreInjectedPsi = scope.isIgnoreInjectedPsi();
return JobUtil.invokeConcurrentlyUnderProgress(Arrays.asList(scopeElements), new Processor<PsiElement>() {
return JobUtil.invokeConcurrentlyUnderProgress(Arrays.asList(scopeElements), progress, false, new Processor<PsiElement>() {
public boolean process(PsiElement scopeElement) {
return processElementsWithWordInScopeElement(scopeElement, processor, text, caseSensitively, ignoreInjectedPsi, progress);
}
}, false, progress);
});
}
}
@@ -250,7 +250,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
final AtomicBoolean pceThrown = new AtomicBoolean(false);
final int size = files.size();
boolean completed = JobUtil.invokeConcurrentlyUnderProgress(files, new Processor<VirtualFile>() {
boolean completed = JobUtil.invokeConcurrentlyUnderProgress(files, progress, false, new Processor<VirtualFile>() {
public boolean process(final VirtualFile vfile) {
final PsiFile file = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
public PsiFile compute() {
@@ -288,7 +288,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
}
return !canceled.get();
}
}, false, progress);
});
if (pceThrown.get()) {
throw new ProcessCanceledException();
@@ -65,7 +65,7 @@ class InjectedSelfElementInfo extends SelfElementInfo {
final Ref<PsiElement> result = new Ref<PsiElement>();
final InjectedLanguageManager manager = InjectedLanguageManager.getInstance(getProject());
InjectedLanguageUtil.enumerate(hostContext, hostContext.getContainingFile(), new PsiLanguageInjectionHost.InjectedPsiVisitor() {
InjectedLanguageUtil.enumerate(hostContext, hostContext.getContainingFile(), true, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
@Override
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
if (result.get() != null) return;
@@ -78,7 +78,7 @@ class InjectedSelfElementInfo extends SelfElementInfo {
result.set(element);
}
}
}, true);
});
return result.get();
}
@@ -143,26 +143,21 @@ public class PostprocessReformattingAspect implements PomModelAspect, Disposable
private void decrementPostponedCounter() {
if (--myPostponedCounter == 0) {
if (!ApplicationManager.getApplication().isWriteAccessAllowed()) {
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
doPostponedFormatting();
}
else {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
doPostponedFormatting();
}
});
}
else {
doPostponedFormatting();
}
//myDisabled = true;
}
}
private final Object LOCK = new Object();
private void atomic(Runnable r) {
synchronized (LOCK) {
ProgressManager.getInstance().executeNonCancelableSection(r);
}
ProgressManager.getInstance().executeNonCancelableSection(r);
}
public void update(final PomModelEvent event) {
@@ -98,14 +98,14 @@ public class InjectedFileViewProvider extends SingleRootFileViewProvider {
PsiElement elementCopy = hostPsiFileCopy.getViewProvider().findElementAt(firstTextRange.getStartOffset(), hostFileLanguage);
assert elementCopy != null;
final Ref<FileViewProvider> provider = new Ref<FileViewProvider>();
InjectedLanguageUtil.enumerate(elementCopy, hostPsiFileCopy, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
InjectedLanguageUtil.enumerate(elementCopy, hostPsiFileCopy, true, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
Document document = documentManager.getCachedDocument(injectedPsi);
if (document instanceof DocumentWindowImpl && oldDocumentWindow.areRangesEqual((DocumentWindowImpl)document)) {
provider.set(injectedPsi.getViewProvider());
}
}
}, true);
});
return provider.get();
}
@@ -16,6 +16,11 @@
package com.intellij.psi.impl.source.tree.injected;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator;
import com.intellij.concurrency.Job;
import com.intellij.concurrency.JobUtil;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.injected.editor.DocumentWindowImpl;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.lang.Language;
@@ -23,21 +28,24 @@ import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.injection.MultiHostInjector;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.ExtensionPointListener;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDocumentManagerImpl;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.ConcurrentHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -51,11 +59,12 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* @author cdr
*/
public class InjectedLanguageManagerImpl extends InjectedLanguageManager {
public class InjectedLanguageManagerImpl extends InjectedLanguageManager implements Disposable{
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl");
private final Project myProject;
private final DumbService myDumbService;
private final AtomicReference<MultiHostInjector> myPsiManagerRegisteredInjectorsAdapter = new AtomicReference<MultiHostInjector>();
private volatile DaemonProgressIndicator myProgress;
public static InjectedLanguageManagerImpl getInstanceImpl(Project project) {
return (InjectedLanguageManagerImpl)InjectedLanguageManager.getInstance(project);
@@ -74,7 +83,7 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager {
public void extensionRemoved(@NotNull MultiHostInjector injector, @Nullable PluginDescriptor pluginDescriptor) {
unregisterMultiHostInjector(injector);
}
});
},this);
final ExtensionPointListener<LanguageInjector> myListener = new ExtensionPointListener<LanguageInjector>() {
public void extensionAdded(@NotNull LanguageInjector extension, @Nullable PluginDescriptor pluginDescriptor) {
psiManagerInjectorsChanged();
@@ -85,14 +94,91 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager {
}
};
final ExtensionPoint<LanguageInjector> psiManagerPoint = Extensions.getRootArea().getExtensionPoint(LanguageInjector.EXTENSION_POINT_NAME);
psiManagerPoint.addExtensionPointListener(myListener);
Disposer.register(project, new Disposable() {
public void dispose() {
psiManagerPoint.removeExtensionPointListener(myListener);
psiManagerPoint.addExtensionPointListener(myListener,this);
myProgress = new DaemonProgressIndicator();
project.getMessageBus().connect(this).subscribe(DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC, new DaemonCodeAnalyzer.DaemonListener() {
@Override
public void daemonFinished() {
}
@Override
public void daemonCancelEventOccurred() {
myProgress.cancel();
}
});
}
@Override
public void dispose() {
}
public boolean startRunInjectors(@NotNull Document hostDocument, final boolean synchronously) {
if (myProject.isDisposed()) return true;
assert synchronously || !ApplicationManager.getApplication().isWriteAccessAllowed();
// use cached to avoid recreate PSI in alien project
final PsiFile hostPsiFile = PsiDocumentManager.getInstance(myProject).getCachedPsiFile(hostDocument);
if (hostPsiFile == null) return true;
final List<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile);
if (injected.isEmpty()) return true;
if (myProgress.isCanceled()) {
myProgress = new DaemonProgressIndicator();
}
final Computable<Boolean> commitRunnable = new Computable<Boolean>() {
@Override
public Boolean compute() {
return JobUtil.invokeConcurrentlyUnderProgress(injected, myProgress, !synchronously, new Processor<DocumentWindow>() {
@Override
public boolean process(DocumentWindow documentWindow) {
ProgressManager.checkCanceled();
RangeMarker rangeMarker = documentWindow.getHostRanges()[0];
PsiElement element = rangeMarker.isValid() ? hostPsiFile.findElementAt(rangeMarker.getStartOffset()) : null;
if (element == null) {
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() {
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
stillInjectedDocument[0] = (DocumentWindow)injectedPsi.getViewProvider().getDocument();
PsiDocumentManagerImpl.checkConsistency(injectedPsi, stillInjectedDocument[0]);
}
});
if (stillInjectedDocument[0] == null) {
injected.remove(documentWindow);
}
else if (stillInjectedDocument[0] != documentWindow) {
injected.remove(documentWindow);
injected.add(stillInjectedDocument[0]);
}
return true;
}
});
}
};
if (synchronously) {
return commitRunnable.compute();
}
else {
JobUtil.submitToJobThread(Job.DEFAULT_PRIORITY, new Runnable() {
@Override
public void run() {
ApplicationManagerEx.getApplicationEx().tryRunReadAction(new Runnable() {
public void run() {
commitRunnable.compute();
}
});
}
});
return true;
}
}
public void psiManagerInjectorsChanged() {
LanguageInjector[] extensions = Extensions.getExtensions(LanguageInjector.EXTENSION_POINT_NAME);
if (extensions.length == 0) {
@@ -210,7 +296,7 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager {
* @param rangeToEdit range in encoded(raw) PSI
* @return list of ranges in encoded (raw) PSI
*/
@SuppressWarnings({"ConstantConditions"})
@SuppressWarnings({"ConstantConditions", "unchecked"})
@NotNull
public List<TextRange> intersectWithAllEditableFragments(@NotNull PsiFile injectedPsi, @NotNull TextRange rangeToEdit) {
Place shreds = InjectedLanguageUtil.getShreds(injectedPsi);
@@ -104,10 +104,13 @@ public class InjectedLanguageUtil {
public static void enumerate(@NotNull PsiElement host, @NotNull PsiLanguageInjectionHost.InjectedPsiVisitor visitor) {
PsiFile containingFile = host.getContainingFile();
enumerate(host, containingFile, visitor, true);
enumerate(host, containingFile, true, visitor);
}
public static void enumerate(@NotNull PsiElement host, @NotNull PsiFile containingFile, @NotNull PsiLanguageInjectionHost.InjectedPsiVisitor visitor, boolean probeUp) {
public static void enumerate(@NotNull PsiElement host,
@NotNull PsiFile containingFile,
boolean probeUp,
@NotNull PsiLanguageInjectionHost.InjectedPsiVisitor visitor) {
//do not inject into nonphysical files except during completion
if (!containingFile.isPhysical() && containingFile.getOriginalFile() == containingFile) {
final PsiElement context = containingFile.getContext();
@@ -193,7 +196,6 @@ public class InjectedLanguageUtil {
final Project project = psiManager.getProject();
InjectedLanguageManagerImpl injectedManager = InjectedLanguageManagerImpl.getInstanceImpl(project);
if (injectedManager == null) return null; //for tests
long modificationCount = psiManager.getModificationTracker().getModificationCount();
MultiHostRegistrarImpl registrar = null;
PsiElement current = element;
nextParent:
@@ -290,7 +292,7 @@ public class InjectedLanguageUtil {
private static PsiElement findInside(@NotNull PsiElement element, @NotNull PsiFile hostFile, final int hostOffset, @NotNull final PsiDocumentManager documentManager) {
final Ref<PsiElement> out = new Ref<PsiElement>();
enumerate(element, hostFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
enumerate(element, hostFile, true, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
for (PsiLanguageInjectionHost.Shred place : places) {
TextRange hostRange = place.host.getTextRange();
@@ -303,7 +305,7 @@ public class InjectedLanguageUtil {
}
}
}
}, true);
});
return out.get();
}
@@ -340,12 +342,12 @@ public class InjectedLanguageUtil {
}
final DocumentWindow[] stillInjectedDocument = {null};
// it is here where the reparse happens and old file contents replaced
enumerate(element, hostPsiFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
enumerate(element, hostPsiFile, true, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
stillInjectedDocument[0] = (DocumentWindow)injectedPsi.getViewProvider().getDocument();
PsiDocumentManagerImpl.checkConsistency(injectedPsi, stillInjectedDocument[0]);
}
}, true);
});
if (stillInjectedDocument[0] == null) {
injected.remove(i);
}
@@ -106,7 +106,7 @@ public class DeferredIconImpl<T> implements DeferredIcon {
paintingParentRec.set(((PaintingParent)pp).getChildRec(c));
}
JobUtil.submitToJobThread(new Runnable() {
JobUtil.submitToJobThread(Job.DEFAULT_PRIORITY, new Runnable() {
public void run() {
int oldWidth = myDelegateIcon.getIconWidth();
myDelegateIcon = evaluate();
@@ -153,7 +153,7 @@ public class DeferredIconImpl<T> implements DeferredIcon {
}
});
}
}, Job.DEFAULT_PRIORITY);
});
}
}
@@ -41,8 +41,8 @@ public class JobUtil {
}
private static <T> boolean invokeConcurrentlyForAll(@NotNull final List<T> things,
@NotNull final Processor<T> thingProcessor,
boolean failFastOnAcquireReadAction) throws ProcessCanceledException {
boolean failFastOnAcquireReadAction,
@NotNull final Processor<T> thingProcessor) throws ProcessCanceledException {
final Job<String> job = new JobImpl<String>(Job.DEFAULT_PRIORITY, failFastOnAcquireReadAction);
final int chunkSize = Math.max(1, things.size() / JobSchedulerImpl.CORES_COUNT / 20);
@@ -84,10 +84,11 @@ public class JobUtil {
/**
* Schedules concurrent execution of #thingProcessor over each element of #things and waits for completion
* With checkCanceled in each thread delegated to our current progress
*
* @param things to process concurrently
* @param thingProcessor to be invoked concurrently on each element from the collection
* @param failFastOnAcquireReadAction if true, returns false when failed to acquire read action
* @param progress
* @param failFastOnAcquireReadAction if true, returns false when failed to acquire read action
* @param thingProcessor to be invoked concurrently on each element from the collection
* @return false if tasks have been canceled
* or at least one processor returned false
* or threw exception
@@ -95,9 +96,9 @@ public class JobUtil {
* @throws ProcessCanceledException if at least one task has thrown ProcessCanceledException
*/
public static <T> boolean invokeConcurrentlyUnderProgress(@NotNull List<T> things,
@NotNull final Processor<T> thingProcessor,
ProgressIndicator progress,
boolean failFastOnAcquireReadAction,
ProgressIndicator progress) throws ProcessCanceledException {
@NotNull final Processor<T> thingProcessor) throws ProcessCanceledException {
if (things.isEmpty()) {
return true;
}
@@ -108,7 +109,7 @@ public class JobUtil {
// can be already wrapped
final ProgressWrapper wrapper = progress instanceof ProgressWrapper ? (ProgressWrapper)progress : ProgressWrapper.wrap(progress);
return invokeConcurrentlyForAll(things, new Processor<T>() {
return invokeConcurrentlyForAll(things, failFastOnAcquireReadAction, new Processor<T>() {
public boolean process(final T t) {
final boolean[] result = new boolean[1];
((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(new Runnable() {
@@ -118,14 +119,14 @@ public class JobUtil {
}, wrapper);
return result[0];
}
}, failFastOnAcquireReadAction);
});
}
public static Job<Void> submitToJobThread(@NotNull final Runnable action, int priority) {
return submitToJobThread(action, priority, null);
public static Job<Void> submitToJobThread(int priority, @NotNull final Runnable action) {
return submitToJobThread(priority, action, null);
}
public static Job<Void> submitToJobThread(@NotNull final Runnable action, int priority, Consumer<Future> onDoneCallback) {
public static Job<Void> submitToJobThread(int priority, @NotNull final Runnable action, Consumer<Future> onDoneCallback) {
final JobImpl<Void> job = new JobImpl<Void>(priority, false);
Callable<Void> callable = new Callable<Void>() {
public Void call() throws Exception {
@@ -163,7 +163,7 @@ public class DuplicatePropertyInspection extends GlobalSimpleInspectionTool {
final ProgressIndicator progress = ProgressWrapper.wrap(original);
ProgressManager.getInstance().runProcess(new Runnable() {
public void run() {
if (!JobUtil.invokeConcurrentlyUnderProgress(properties, new Processor<IProperty>() {
if (!JobUtil.invokeConcurrentlyUnderProgress(properties, progress, false, new Processor<IProperty>() {
public boolean process(final IProperty property) {
if (original != null) {
if (original.isCanceled()) return false;
@@ -173,7 +173,7 @@ public class DuplicatePropertyInspection extends GlobalSimpleInspectionTool {
processTextUsages(processedKeyToFiles, property.getUnescapedKey(), processedValueToFiles, searchHelper, scope);
return true;
}
}, false, progress)) throw new ProcessCanceledException();
})) throw new ProcessCanceledException();
List<ProblemDescriptor> problemDescriptors = new ArrayList<ProblemDescriptor>();
Map<String, Set<String>> keyToDifferentValues = new HashMap<String, Set<String>>();