diff --git a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java index 9f7cec5b1dd5..90015e2d9f38 100644 --- a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java +++ b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java @@ -155,8 +155,8 @@ public class ReplaceInProjectManager { UsageViewPresentation presentation, FindUsagesProcessPresentation processPresentation, final FindManager findManager) { - final ReplaceContext[] context = new ReplaceContext[1]; presentation.setMergeDupLinesAvailable(false); + final ReplaceContext[] context = new ReplaceContext[1]; manager.searchAndShowUsages(new UsageTarget[]{new FindInProjectUtil.StringUsageTarget(findModelCopy.getStringToFind())}, usageSearcherFactory, processPresentation, presentation, new UsageViewManager.UsageViewStateListener() { @Override @@ -335,16 +335,15 @@ public class ReplaceInProjectManager { replaceContext.getUsageView().addButtonToLowerPane(replaceSelectedRunnable, FindBundle.message("find.replace.selected.action")); } - private boolean replaceUsages(final ReplaceContext replaceContext, Collection usages) { - boolean success = true; - int replacedCount = 0; + private boolean replaceUsages(@NotNull ReplaceContext replaceContext, @NotNull Collection usages) { if (!ensureUsagesWritable(replaceContext, usages)) { return true; } + int replacedCount = 0; + boolean success = true; for (final Usage usage : usages) { try { if (replaceUsage(usage, replaceContext.getFindModel(), replaceContext.getExcludedSetCached(), false)) { - replaceContext.getUsageView().removeUsage(usage); replacedCount++; } } @@ -353,6 +352,7 @@ public class ReplaceInProjectManager { success = false; } } + replaceContext.getUsageView().removeUsagesBulk(usages); reportNumberReplacedOccurrences(myProject, replacedCount); return success; } @@ -386,7 +386,7 @@ public class ReplaceInProjectManager { final Document document = ((UsageInfo2UsageAdapter)usage).getDocument(); if (!document.isWritable()) return false; - return ((UsageInfo2UsageAdapter)usage).processRangeMarkers(new Processor() { + boolean result = ((UsageInfo2UsageAdapter)usage).processRangeMarkers(new Processor() { @Override public boolean process(Segment segment) { final int textOffset = segment.getStartOffset(); @@ -405,6 +405,7 @@ public class ReplaceInProjectManager { return true; } }); + return result; } }); @@ -493,7 +494,8 @@ public class ReplaceInProjectManager { if (usageView.getUsages().isEmpty()) { usageView.close(); return true; - } else if (!success) { + } + if (!success) { NOTIFICATION_GROUP.createNotification("One or more malformed replacement strings", MessageType.ERROR).notify(myProject); } return false; diff --git a/platform/usageView/src/com/intellij/usages/UsageView.java b/platform/usageView/src/com/intellij/usages/UsageView.java index 71bea5672888..2736f74ebbba 100644 --- a/platform/usageView/src/com/intellij/usages/UsageView.java +++ b/platform/usageView/src/com/intellij/usages/UsageView.java @@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -83,4 +84,11 @@ public interface UsageView extends Disposable { @NotNull JComponent getComponent(); int getUsagesCount(); + + /** + * Removes all specified usages from the usage view in one heroic swoop. + * Reloads the whole tree model once instead of firing individual remove event for each node. + * Useful for processing huge number of usages faster, e.g. during "find in path/replace all". + */ + void removeUsagesBulk(@NotNull Collection usages); } diff --git a/platform/usageView/src/com/intellij/usages/impl/GroupNode.java b/platform/usageView/src/com/intellij/usages/impl/GroupNode.java index 628b798aa06b..3f02615c502d 100644 --- a/platform/usageView/src/com/intellij/usages/impl/GroupNode.java +++ b/platform/usageView/src/com/intellij/usages/impl/GroupNode.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import java.util.*; @@ -155,6 +156,35 @@ public class GroupNode extends Node implements Navigatable, Comparable usages) { + boolean removed; + synchronized (lock) { + removed = myUsageNodes.removeAll(usages); + } + + Collection groupNodes = mySubgroupNodes.values(); + + for (Iterator iterator = groupNodes.iterator(); iterator.hasNext(); ) { + GroupNode groupNode = iterator.next(); + + if (groupNode.removeUsagesBulk(usages)) { + if (groupNode.getRecursiveUsageCount() == 0) { + MutableTreeNode parent = (MutableTreeNode)groupNode.getParent(); + int childIndex = parent.getIndex(groupNode); + if (childIndex != -1) { + parent.remove(childIndex); + } + iterator.remove(); + } + removed = true; + } + } + if (removed) { + --myRecursiveUsageCount; + } + return removed; + } + private void doUpdate() { ApplicationManager.getApplication().assertIsDispatchThread(); --myRecursiveUsageCount; diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 58ccf7caaea2..bd071d1160b6 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -859,6 +859,36 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra } } + @Override + public void removeUsagesBulk(@NotNull Collection usages) { + final Set nodes = new THashSet(usages.size()); + for (Usage usage : usages) { + UsageNode node = myUsageNodes.remove(usage); + if (node != null && node != NULL_NODE) { + nodes.add(node); + } + } + if (!nodes.isEmpty() && !myPresentation.isDetachedMode()) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + if (isDisposed) return; + DefaultTreeModel treeModel = (DefaultTreeModel)myTree.getModel(); + for (UsageNode node : nodes) { + MutableTreeNode parent = (MutableTreeNode)node.getParent(); + int childIndex = parent.getIndex(node); + if (childIndex != -1) { + parent.remove(childIndex); + } + } + ((GroupNode)myTree.getModel().getRoot()).removeUsagesBulk(nodes); + + treeModel.reload(); + } + }); + } + } + @Override public void includeUsages(@NotNull Usage[] usages) { List nodes = new ArrayList(usages.length);