do more work outside EDT, do not stop progress indicator until all usages inserted into the tree

This commit is contained in:
Alexey Kudravtsev
2011-04-05 17:56:32 +04:00
parent 47334c3d15
commit f3fda54acc
6 changed files with 137 additions and 96 deletions
@@ -402,42 +402,39 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
severity, problemDescriptor.isAfterEndOfLine(), null, isFileLevel);
}
private final TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>> myTransferToEDTQueue;
{
myTransferToEDTQueue =
new TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>(new Processor<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>() {
private final InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
private final InjectedLanguageManager ilManager = InjectedLanguageManager.getInstance(myProject);
private final List<HighlightInfo> infos = new ArrayList<HighlightInfo>(2);
private final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
@Override
public boolean process(Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator> trinity) {
ProgressIndicator indicator = trinity.getThird();
if (indicator.isCanceled()) {
return false;
}
private final TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>> myTransferToEDTQueue
= new TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>("Apply inspection results", new Processor<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>() {
private final InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
private final InjectedLanguageManager ilManager = InjectedLanguageManager.getInstance(myProject);
private final List<HighlightInfo> infos = new ArrayList<HighlightInfo>(2);
private final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
@Override
public boolean process(Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator> trinity) {
ProgressIndicator indicator = trinity.getThird();
if (indicator.isCanceled()) {
return false;
}
ProblemDescriptor descriptor = trinity.first;
LocalInspectionTool tool = trinity.second;
PsiElement psiElement = descriptor.getPsiElement();
if (psiElement == null) return true;
PsiFile file = psiElement.getContainingFile();
Document thisDocument = documentManager.getDocument(file);
ProblemDescriptor descriptor = trinity.first;
LocalInspectionTool tool = trinity.second;
PsiElement psiElement = descriptor.getPsiElement();
if (psiElement == null) return true;
PsiFile file = psiElement.getContainingFile();
Document thisDocument = documentManager.getDocument(file);
HighlightSeverity severity = inspectionProfile.getErrorLevel(HighlightDisplayKey.find(tool.getShortName()), file).getSeverity();
HighlightSeverity severity = inspectionProfile.getErrorLevel(HighlightDisplayKey.find(tool.getShortName()), file).getSeverity();
infos.clear();
createHighlightsForDescriptor(infos, emptyActionRegistered, ilManager, file, thisDocument, tool, severity, descriptor, psiElement);
for (HighlightInfo info : infos) {
final EditorColorsScheme colorsScheme = getColorsScheme();
UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, myDocument, myFile, myStartOffset, myEndOffset,
info, colorsScheme, getId());
}
infos.clear();
createHighlightsForDescriptor(infos, emptyActionRegistered, ilManager, file, thisDocument, tool, severity, descriptor, psiElement);
for (HighlightInfo info : infos) {
final EditorColorsScheme colorsScheme = getColorsScheme();
UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, myDocument, myFile, myStartOffset, myEndOffset,
info, colorsScheme, getId());
}
return true;
}
}, myProject.getDisposed());
}
return true;
}
}, myProject.getDisposed(), 200);
private final Set<TextRange> emptyActionRegistered = Collections.synchronizedSet(new HashSet<TextRange>());
@@ -27,14 +27,15 @@ import org.jetbrains.annotations.NotNull;
* Usage: {@link #offer(Object)} } : schedules element for processing in a pooled thread
*/
public class TransferToPooledThreadQueue<T> extends TransferToEDTQueue<T> {
public TransferToPooledThreadQueue(@NotNull Processor<T> processorInEDT, @NotNull Condition<?> shutUpCondition) {
super(processorInEDT, shutUpCondition);
public TransferToPooledThreadQueue(@NotNull String name,
@NotNull Processor<T> processorInEDT,
@NotNull Condition<?> shutUpCondition,
int maxUnitOfWorkThresholdMs) {
super(name, processorInEDT, shutUpCondition, maxUnitOfWorkThresholdMs);
}
@Override
protected void schedule(Runnable updateRunnable) {
protected void schedule(@NotNull Runnable updateRunnable) {
JobScheduler.getScheduler().execute(updateRunnable);
//ApplicationManager.getApplication().executeOnPooledThread(updateRunnable);
}
}
@@ -76,7 +76,8 @@ public class EncodingManagerImpl extends EncodingManager implements PersistentSt
private final Alarm updateEncodingFromContent = new Alarm(Alarm.ThreadToUse.OWN_THREAD, this);
private static final Key<Charset> CACHED_CHARSET_FROM_CONTENT = Key.create("CACHED_CHARSET_FROM_CONTENT");
private final TransferToPooledThreadQueue<Reference<Document>> myChangedDocuments = new TransferToPooledThreadQueue<Reference<Document>>(new Processor<Reference<Document>>() {
private final TransferToPooledThreadQueue<Reference<Document>> myChangedDocuments = new TransferToPooledThreadQueue<Reference<Document>>(
"Loading documents", new Processor<Reference<Document>>() {
@Override
public boolean process(Reference<Document> ref) {
Document document = ref.get();
@@ -84,7 +85,7 @@ public class EncodingManagerImpl extends EncodingManager implements PersistentSt
handleDocument(document);
return true;
}
}, ApplicationManager.getApplication().getDisposed());
}, ApplicationManager.getApplication().getDisposed(), -1); // drain the whole queue, do not reschedule
public EncodingManagerImpl(EditorFactory editorFactory) {
editorFactory.getEventMulticaster().addDocumentListener(new DocumentAdapter() {
@@ -225,7 +225,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
}
});
}
myTransferToEDTQueue = new TransferToEDTQueue<Usage>(new Processor<Usage>() {
myTransferToEDTQueue = new TransferToEDTQueue<Usage>("Insert usages", new Processor<Usage>() {
@Override
public boolean process(Usage usage) {
appendUsage(usage);
@@ -236,7 +236,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
public boolean value(Object o) {
return isDisposed || project.isDisposed() || com.intellij.usages.UsageViewManager.getInstance(project).searchHasBeenCancelled();
}
});
},200);
}
private void setupCentralPanel() {
@@ -639,7 +639,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
return indicator == null || !indicator.isCanceled();
}
});
drainQueuedUsageNodes();
setSearchInProgress(false);
}
});
@@ -666,14 +666,23 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
public void appendUsageLater(@NotNull Usage usage) {
myTransferToEDTQueue.offer(usage);
}
public void drainQueuedUsageNodes() {
assert !ApplicationManager.getApplication().isDispatchThread() : Thread.currentThread();
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
myTransferToEDTQueue.drain();
}
});
}
private volatile boolean myIsFirstVisibleUsageFound = false;
public void appendUsage(@NotNull Usage usage) {
doAppendUsage(usage);
}
public UsageNode doAppendUsage(Usage usage) {
public UsageNode doAppendUsage(@NotNull Usage usage) {
// invoke in ReadAction to be be sure that usages are not invalidated while the tree is being built
ApplicationManager.getApplication().assertReadAccessAllowed();
if (!usage.isValid()) {
@@ -747,7 +756,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
return myUsageNodes.size();
}
public void setContent(Content content) {
public void setContent(@NotNull Content content) {
myContent = content;
content.setDisposer(this);
}
@@ -760,7 +769,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
}
}
private void checkNodeValidity(DefaultMutableTreeNode node) {
private void checkNodeValidity(@NotNull DefaultMutableTreeNode node) {
Enumeration enumeration = node.children();
while (enumeration.hasMoreElements()) {
checkNodeValidity((DefaultMutableTreeNode)enumeration.nextElement());
@@ -823,7 +832,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
}
}
private void showNode(final UsageNode node) {
private void showNode(@NotNull final UsageNode node) {
if (!myPresentation.isDetachedMode()) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
@@ -851,9 +860,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
final String commandName,
final String cannotMakeString,
@NotNull String shortDescription) {
addButtonToLowerPane(new MyPerformOperationRunnable(cannotMakeString, processRunnable, commandName),
shortDescription);
addButtonToLowerPane(new MyPerformOperationRunnable(cannotMakeString, processRunnable, commandName), shortDescription);
}
private boolean allTargetsAreValid() {
@@ -881,6 +888,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
!ReadonlyStatusHandler.getInstance(myProject).ensureFilesWritable(VfsUtil.toVirtualFileArray(readOnlyUsages)).hasReadonlyFiles();
}
@NotNull
private Set<Usage> getReadOnlyUsages() {
final Set<Usage> result = new THashSet<Usage>();
final Set<Map.Entry<Usage,UsageNode>> usages = myUsageNodes.entrySet();
@@ -894,6 +902,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
return result;
}
@NotNull
private Set<VirtualFile> getReadOnlyUsagesFiles() {
Set<Usage> usages = getReadOnlyUsages();
Set<VirtualFile> result = new THashSet<VirtualFile>();
@@ -987,7 +996,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
return usages;
}
private static void collectUsages(DefaultMutableTreeNode node, Set<Usage> usages) {
private static void collectUsages(@NotNull DefaultMutableTreeNode node, @NotNull Set<Usage> usages) {
if (node instanceof UsageNode) {
UsageNode usageNode = (UsageNode)node;
final Usage usage = usageNode.getUsage();
@@ -1022,7 +1031,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
}
@Nullable
private static Navigatable getNavigatableForNode(DefaultMutableTreeNode node) {
private static Navigatable getNavigatableForNode(@NotNull DefaultMutableTreeNode node) {
Object userObject = node.getUserObject();
if (userObject instanceof Navigatable) {
final Navigatable navigatable = (Navigatable)userObject;
@@ -1058,7 +1067,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
private class MyPanel extends JPanel implements TypeSafeDataProvider, OccurenceNavigator,Disposable {
@Nullable private OccurenceNavigatorSupport mySupport;
private MyPanel(JTree tree) {
private MyPanel(@NotNull JTree tree) {
mySupport = new OccurenceNavigatorSupport(tree) {
protected Navigatable createDescriptorForNode(DefaultMutableTreeNode node) {
if (node.getChildCount() > 0) return null;
@@ -1208,7 +1217,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
private final Usage myUsage;
private final boolean mySelected;
private UsageState(final Usage usage, boolean isSelected) {
private UsageState(@NotNull Usage usage, boolean isSelected) {
myUsage = usage;
mySelected = isSelected;
}
@@ -158,11 +158,9 @@ public class UsageViewManagerImpl extends UsageViewManager {
catch (ProcessCanceledException e) {
//ignore
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
runnable.endSearchForUsages();
}
}, ModalityState.NON_MODAL);
finally {
runnable.endSearchForUsages();
}
}
});
}
@@ -335,43 +333,45 @@ public class UsageViewManagerImpl extends UsageViewManager {
}
private void endSearchForUsages() {
assert !ApplicationManager.getApplication().isDispatchThread() : Thread.currentThread();
int usageCount = myUsageCountWithoutDefinition.get();
if (usageCount == 0 && myProcessPresentation.isShowNotFoundMessage()) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
final List<Action> notFoundActions = myProcessPresentation.getNotFoundActions();
final String message = UsageViewBundle.message("dialog.no.usages.found.in",
StringUtil.decapitalize(myPresentation.getUsagesString()),
myPresentation.getScopeText());
public void run() {
final List<Action> notFoundActions = myProcessPresentation.getNotFoundActions();
final String message = UsageViewBundle.message("dialog.no.usages.found.in",
StringUtil.decapitalize(myPresentation.getUsagesString()),
myPresentation.getScopeText());
if (notFoundActions == null || notFoundActions.isEmpty()) {
ToolWindowManager.getInstance(myProject).notifyByBalloon(ToolWindowId.FIND, MessageType.INFO,
XmlStringUtil.escapeString(message),
IconLoader.getIcon("/actions/find.png"), null);
}
else {
List<String> titles = new ArrayList<String>(notFoundActions.size() + 1);
titles.add(UsageViewBundle.message("dialog.button.ok"));
for (Action action : notFoundActions) {
Object value = action.getValue(FindUsagesProcessPresentation.NAME_WITH_MNEMONIC_KEY);
if (value == null) value = action.getValue(Action.NAME);
titles.add((String)value);
if (notFoundActions == null || notFoundActions.isEmpty()) {
ToolWindowManager.getInstance(myProject).notifyByBalloon(ToolWindowId.FIND, MessageType.INFO,
XmlStringUtil.escapeString(message),
IconLoader.getIcon("/actions/find.png"), null);
}
else {
List<String> titles = new ArrayList<String>(notFoundActions.size() + 1);
titles.add(UsageViewBundle.message("dialog.button.ok"));
for (Action action : notFoundActions) {
Object value = action.getValue(FindUsagesProcessPresentation.NAME_WITH_MNEMONIC_KEY);
if (value == null) value = action.getValue(Action.NAME);
int option = Messages.showDialog(myProject, message, UsageViewBundle.message("dialog.title.information"),
ArrayUtil.toStringArray(titles), 0, Messages.getInformationIcon());
titles.add((String)value);
}
if (option > 0) {
notFoundActions.get(option - 1).actionPerformed(new ActionEvent(this, 0, titles.get(option)));
int option = Messages.showDialog(myProject, message, UsageViewBundle.message("dialog.title.information"),
ArrayUtil.toStringArray(titles), 0, Messages.getInformationIcon());
if (option > 0) {
notFoundActions.get(option - 1).actionPerformed(new ActionEvent(this, 0, titles.get(option)));
}
}
}
}
}, ModalityState.NON_MODAL, myProject.getDisposed());
}, ModalityState.NON_MODAL, myProject.getDisposed());
}
else if (usageCount == 1 && !myProcessPresentation.isShowPanelIfOnlyOneUsage()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
Usage usage = myFirstUsage.get();
if (usage.canNavigate()) {
usage.navigate(true);
@@ -382,7 +382,10 @@ public class UsageViewManagerImpl extends UsageViewManager {
}
else {
final UsageViewImpl usageView = myUsageViewRef.get();
if (usageView != null) usageView.setSearchInProgress(false);
if (usageView != null) {
usageView.drainQueuedUsageNodes();
usageView.setSearchInProgress(false);
}
}
if (myListener != null) {
@@ -17,6 +17,7 @@ package com.intellij.util.containers;
import com.intellij.openapi.util.Condition;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -30,13 +31,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Usage: {@link TransferToEDTQueue#offer(Object)} } : schedules element for processing in EDT (via invokeLater)
*/
public class TransferToEDTQueue<T> {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private final String myName;
private final Processor<T> myProcessor;
private volatile boolean stopped;
private final Condition<?> myShutUpCondition;
private final int myMaxUnitOfWorkThresholdMs; //-1 means indefinite
private final Queue<T> myQueue = new ConcurrentLinkedQueue<T>();
private final AtomicBoolean invokeLaterScheduled = new AtomicBoolean();
private static final long MAX_UNIT_OF_WORK_THRESHOLD_MS = 200; // no more than 200 ms delay
private final Runnable myUpdateRunnable = new Runnable() {
@Override
public void run() {
@@ -49,15 +52,14 @@ public class TransferToEDTQueue<T> {
long start = System.currentTimeMillis();
int processed = 0;
while (true) {
T thing = myQueue.poll();
if (thing == null) break;
if (!myProcessor.process(thing)) {
stop();
return;
if (processNext()) {
processed++;
}
else {
break;
}
processed++;
long finish = System.currentTimeMillis();
if (finish - start > MAX_UNIT_OF_WORK_THRESHOLD_MS) break;
if (myMaxUnitOfWorkThresholdMs != -1 && finish - start > myMaxUnitOfWorkThresholdMs) break;
}
if (!myQueue.isEmpty()) {
scheduleUpdate();
@@ -65,9 +67,21 @@ public class TransferToEDTQueue<T> {
}
};
public TransferToEDTQueue(@NotNull Processor<T> processorInEDT, @NotNull Condition<?> shutUpCondition) {
myProcessor = processorInEDT;
private boolean processNext() {
T thing = myQueue.poll();
if (thing == null) return false;
if (!myProcessor.process(thing)) {
stop();
return false;
}
return true;
}
public TransferToEDTQueue(@NotNull @NonNls String name, @NotNull Processor<T> processor, @NotNull Condition<?> shutUpCondition, int maxUnitOfWorkThresholdMs) {
myName = name;
myProcessor = processor;
myShutUpCondition = shutUpCondition;
myMaxUnitOfWorkThresholdMs = maxUnitOfWorkThresholdMs;
}
public void offer(@NotNull T thing) {
@@ -76,12 +90,12 @@ public class TransferToEDTQueue<T> {
}
private void scheduleUpdate() {
if (invokeLaterScheduled.compareAndSet(false, true)) {
if (!stopped && invokeLaterScheduled.compareAndSet(false, true)) {
schedule(myUpdateRunnable);
}
}
protected void schedule(Runnable updateRunnable) {
protected void schedule(@NotNull Runnable updateRunnable) {
SwingUtilities.invokeLater(updateRunnable);
}
@@ -89,4 +103,20 @@ public class TransferToEDTQueue<T> {
stopped = true;
myQueue.clear();
}
// process all queue in current thread
public void drain() {
int processed = 0;
long start = System.currentTimeMillis();
while (true) {
if (processNext()) {
processed++;
}
else {
break;
}
}
long finish = System.currentTimeMillis();
int i = 0;
}
}