From f3fda54acc913d0910640b910d30e55dcc4b3914 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 5 Apr 2011 17:56:32 +0400 Subject: [PATCH] do more work outside EDT, do not stop progress indicator until all usages inserted into the tree --- .../daemon/impl/LocalInspectionsPass.java | 61 +++++++++--------- .../impl/TransferToPooledThreadQueue.java | 11 ++-- .../vfs/encoding/EncodingManagerImpl.java | 5 +- .../intellij/usages/impl/UsageViewImpl.java | 39 +++++++----- .../usages/impl/UsageViewManagerImpl.java | 63 ++++++++++--------- .../util/containers/TransferToEDTQueue.java | 54 ++++++++++++---- 6 files changed, 137 insertions(+), 96 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java index 340df6d95b98..866205f550f7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java @@ -402,42 +402,39 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass severity, problemDescriptor.isAfterEndOfLine(), null, isFileLevel); } - private final TransferToEDTQueue> myTransferToEDTQueue; - { - myTransferToEDTQueue = - new TransferToEDTQueue>(new Processor>() { - private final InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); - private final InjectedLanguageManager ilManager = InjectedLanguageManager.getInstance(myProject); - private final List infos = new ArrayList(2); - private final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); - @Override - public boolean process(Trinity trinity) { - ProgressIndicator indicator = trinity.getThird(); - if (indicator.isCanceled()) { - return false; - } + private final TransferToEDTQueue> myTransferToEDTQueue + = new TransferToEDTQueue>("Apply inspection results", new Processor>() { + private final InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + private final InjectedLanguageManager ilManager = InjectedLanguageManager.getInstance(myProject); + private final List infos = new ArrayList(2); + private final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); + @Override + public boolean process(Trinity 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 emptyActionRegistered = Collections.synchronizedSet(new HashSet()); diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java index 93cd04f40196..87de511bb733 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/TransferToPooledThreadQueue.java @@ -27,14 +27,15 @@ import org.jetbrains.annotations.NotNull; * Usage: {@link #offer(Object)} } : schedules element for processing in a pooled thread */ public class TransferToPooledThreadQueue extends TransferToEDTQueue { - - public TransferToPooledThreadQueue(@NotNull Processor processorInEDT, @NotNull Condition shutUpCondition) { - super(processorInEDT, shutUpCondition); + public TransferToPooledThreadQueue(@NotNull String name, + @NotNull Processor 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); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingManagerImpl.java index 9e40ecfdea81..2933ac43550c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingManagerImpl.java @@ -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 CACHED_CHARSET_FROM_CONTENT = Key.create("CACHED_CHARSET_FROM_CONTENT"); - private final TransferToPooledThreadQueue> myChangedDocuments = new TransferToPooledThreadQueue>(new Processor>() { + private final TransferToPooledThreadQueue> myChangedDocuments = new TransferToPooledThreadQueue>( + "Loading documents", new Processor>() { @Override public boolean process(Reference 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() { diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 08b0a911e00f..279f6321bedf 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -225,7 +225,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra } }); } - myTransferToEDTQueue = new TransferToEDTQueue(new Processor() { + myTransferToEDTQueue = new TransferToEDTQueue("Insert usages", new Processor() { @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 getReadOnlyUsages() { final Set result = new THashSet(); final Set> usages = myUsageNodes.entrySet(); @@ -894,6 +902,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra return result; } + @NotNull private Set getReadOnlyUsagesFiles() { Set usages = getReadOnlyUsages(); Set result = new THashSet(); @@ -987,7 +996,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra return usages; } - private static void collectUsages(DefaultMutableTreeNode node, Set usages) { + private static void collectUsages(@NotNull DefaultMutableTreeNode node, @NotNull Set 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; } diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java index d7c3db66c7ea..e510f7717fc3 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java @@ -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 notFoundActions = myProcessPresentation.getNotFoundActions(); - final String message = UsageViewBundle.message("dialog.no.usages.found.in", - StringUtil.decapitalize(myPresentation.getUsagesString()), - myPresentation.getScopeText()); + public void run() { + final List 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 titles = new ArrayList(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 titles = new ArrayList(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) { diff --git a/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java b/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java index abe2edbb296e..2d83017b15e3 100644 --- a/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java +++ b/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java @@ -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 { + @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) + private final String myName; private final Processor myProcessor; private volatile boolean stopped; private final Condition myShutUpCondition; + private final int myMaxUnitOfWorkThresholdMs; //-1 means indefinite private final Queue myQueue = new ConcurrentLinkedQueue(); 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 { 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 { } }; - public TransferToEDTQueue(@NotNull Processor 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 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 { } 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 { 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; + } }