inspection tool window: optimize getting sorted descriptors and checking for readonly
authorDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Thu, 28 Apr 2016 08:44:16 +0000 (11:44 +0300)
committerDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Thu, 28 Apr 2016 14:19:23 +0000 (17:19 +0300)
platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java
platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java
platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java
platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java
platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java
platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewPanelFactory.java
platform/platform-resources-en/src/messages/InspectionsBundle.properties

index 5bc4d6ad5f07227066baa5f022ab14898c22fe32..d99ef3bd36f961053ae5bcef330e323aeeb831e1 100644 (file)
@@ -80,7 +80,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider
   public QuickFixAction[] getQuickFixes(@NotNull final InspectionToolWrapper toolWrapper, @NotNull final InspectionTree tree) {
     final RefEntity[] refEntities = tree.getSelectedElements();
     InspectionToolPresentation presentation = tree.getContext().getPresentation(toolWrapper);
-    return refEntities.length == 0 ? null : presentation.getQuickFixes(refEntities, tree.getSelectedDescriptors(false));
+    return refEntities.length == 0 ? null : presentation.getQuickFixes(refEntities, tree.getSelectedDescriptors());
   }
 
 
index e2c20c73a859322d766126d65ea7022bd3972732..bdf3efbef189d20d247398663d2875a5d36faaf3 100644 (file)
@@ -19,6 +19,7 @@ package com.intellij.codeInspection.ex;
 import com.intellij.codeInsight.FileModificationService;
 import com.intellij.codeInspection.CommonProblemDescriptor;
 import com.intellij.codeInspection.InspectionManager;
+import com.intellij.codeInspection.InspectionsBundle;
 import com.intellij.codeInspection.ProblemDescriptor;
 import com.intellij.codeInspection.reference.RefElement;
 import com.intellij.codeInspection.reference.RefEntity;
@@ -30,11 +31,14 @@ import com.intellij.ide.DataManager;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ReadAction;
 import com.intellij.openapi.command.CommandProcessor;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
 import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -94,7 +98,7 @@ public class QuickFixAction extends AnAction implements CustomComponentAction {
     }
 
     if (!isProblemDescriptorsAcceptable() && tree.getSelectedElements().length > 0 ||
-        isProblemDescriptorsAcceptable() && tree.getSelectedDescriptors(false).length > 0) {
+        isProblemDescriptorsAcceptable() && tree.getSelectedDescriptors().length > 0) {
       e.getPresentation().setVisible(true);
       e.getPresentation().setEnabled(true);
     }
@@ -112,11 +116,18 @@ public class QuickFixAction extends AnAction implements CustomComponentAction {
   public void actionPerformed(final AnActionEvent e) {
     final InspectionResultsView view = getInvoker(e);
     final InspectionTree tree = view.getTree();
-    final CommonProblemDescriptor[] descriptors;
     try {
-      view.setApplyingFix(true);
-      if (isProblemDescriptorsAcceptable() && (descriptors = tree.getSelectedDescriptors(true)).length > 0) {
-        doApplyFix(view.getProject(), descriptors, tree.getContext());
+      Ref<CommonProblemDescriptor[]> descriptors = Ref.create();
+      Set<VirtualFile> readOnlyFiles = new THashSet<>();
+      if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ReadAction.run(() -> {
+        final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+        indicator.setText("Checking problem descriptors...");
+        descriptors.set(tree.getSelectedDescriptors(true, readOnlyFiles));
+      }), InspectionsBundle.message("preparing.for.apply.fix"), true, e.getProject())) {
+        return;
+      }
+      if (isProblemDescriptorsAcceptable() && descriptors.get().length > 0) {
+        doApplyFix(view.getProject(), descriptors.get(), readOnlyFiles, tree.getContext());
       } else {
         doApplyFix(getSelectedElements(e), view);
       }
@@ -134,19 +145,11 @@ public class QuickFixAction extends AnAction implements CustomComponentAction {
 
   private void doApplyFix(@NotNull final Project project,
                           @NotNull final CommonProblemDescriptor[] descriptors,
+                          @NotNull Set<VirtualFile> readOnlyFiles,
                           @NotNull final GlobalInspectionContextImpl context) {
-    final Set<VirtualFile> readOnlyFiles = new THashSet<VirtualFile>();
-    for (CommonProblemDescriptor descriptor : descriptors) {
-      final PsiElement psiElement = descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getPsiElement() : null;
-      if (psiElement != null && !psiElement.isWritable()) {
-        readOnlyFiles.add(psiElement.getContainingFile().getVirtualFile());
-      }
-    }
-
     if (!FileModificationService.getInstance().prepareVirtualFilesForWrite(project, readOnlyFiles)) return;
     
     final RefManagerImpl refManager = (RefManagerImpl)context.getRefManager();
-
     final boolean initial = refManager.isInProcess();
 
     refManager.inspectionReadActionFinished();
index ab5716c9c03673f8a55bf9e64178c140d47f7ef8..ef66066ab7f21a45129a8e5c49ed2ea39e8e9835 100644 (file)
@@ -96,7 +96,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
 
     GlobalInspectionContextImpl context = tree.getContext();
     InspectionToolPresentation presentation = context.getPresentation(toolWrapper);
-    return presentation.extractActiveFixes(selectedRefElements, actions, tree.getSelectedDescriptors(false));
+    return presentation.extractActiveFixes(selectedRefElements, actions, tree.getSelectedDescriptors());
   }
 
   @Override
index d3afab5262f567c01ed019a17622bc43def8bff8..defe2dbd80cda7983b0cd15150b4f8a3784224df 100644 (file)
@@ -507,7 +507,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
         !(((RefElement)selectedEntity).getElement() instanceof PsiDirectory)) {
       PsiElement selectedElement = ((RefElement)selectedEntity).getElement();
       if (problemCount == 1) {
-        CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors(false);
+        CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors();
         if (descriptors.length != 0) {
           final CommonProblemDescriptor descriptor = descriptors[0];
           if (descriptor instanceof ProblemDescriptorBase) {
index 60ffb40dc6b898b8f14fe8ec6f0e302986fd1dee..242d3ead0b3e19883c77b91d68fe9c413b3a4fb0 100644 (file)
@@ -27,8 +27,11 @@ import com.intellij.codeInspection.ProblemDescriptor;
 import com.intellij.codeInspection.ex.GlobalInspectionContextImpl;
 import com.intellij.codeInspection.ex.InspectionToolWrapper;
 import com.intellij.codeInspection.reference.RefEntity;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionsConfigTreeComparator;
+import com.intellij.psi.PsiElement;
 import com.intellij.psi.util.PsiUtilCore;
 import com.intellij.ui.TreeSpeedSearch;
 import com.intellij.ui.treeStructure.Tree;
@@ -50,8 +53,11 @@ import javax.swing.tree.ExpandVetoException;
 import javax.swing.tree.TreeNode;
 import javax.swing.tree.TreePath;
 import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class InspectionTree extends Tree {
+  private static final Logger LOG = Logger.getInstance(InspectionTree.class);
   private static final Comparator<CommonProblemDescriptor> DESCRIPTOR_COMPARATOR = (c1, c2) -> {
     if (c1 instanceof ProblemDescriptor && c2 instanceof ProblemDescriptor) {
       return PsiUtilCore.compareElementsByPosition(((ProblemDescriptor)c2).getPsiElement(),
@@ -212,13 +218,17 @@ public class InspectionTree extends Tree {
     }
   }
 
-  public CommonProblemDescriptor[] getSelectedDescriptors(boolean sortedByPosition) {
+  public CommonProblemDescriptor[] getSelectedDescriptors() {
+    return getSelectedDescriptors(false, null);
+  }
+
+  public CommonProblemDescriptor[] getSelectedDescriptors(boolean sortedByPosition, @Nullable Set<VirtualFile> readOnlyFilesSink) {
     final TreePath[] paths = getSelectionPaths();
     if (paths == null) return CommonProblemDescriptor.EMPTY_ARRAY;
     final TreePath[] selectionPaths = TreeUtil.selectMaximals(paths);
     final LinkedHashSet<CommonProblemDescriptor> descriptors = new LinkedHashSet<CommonProblemDescriptor>();
 
-    MultiMap<Object, CommonProblemDescriptor> parentToChildNode = new MultiMap<>();
+    MultiMap<Object, ProblemDescriptionNode> parentToChildNode = new MultiMap<>();
     final List<InspectionTreeNode> nonDescriptorNodes = new SmartList<>();
     for (TreePath path : selectionPaths) {
       final Object[] pathAsArray = path.getPath();
@@ -226,11 +236,10 @@ public class InspectionTree extends Tree {
       final Object node = pathAsArray[length - 1];
       if (node instanceof ProblemDescriptionNode) {
         if (isNodeValidAndIncluded((ProblemDescriptionNode)node)) {
-          final CommonProblemDescriptor descriptor = ((ProblemDescriptionNode)node).getDescriptor();
           if (length >= 2) {
-            parentToChildNode.putValue(pathAsArray[length - 2], descriptor);
+            parentToChildNode.putValue(pathAsArray[length - 2], (ProblemDescriptionNode)node);
           } else {
-            parentToChildNode.putValue(node, descriptor);
+            parentToChildNode.putValue(node, (ProblemDescriptionNode)node);
           }
         }
       } else {
@@ -239,22 +248,29 @@ public class InspectionTree extends Tree {
     }
 
     for (InspectionTreeNode node : nonDescriptorNodes) {
-      processChildDescriptorsDeep(node, descriptors, sortedByPosition);
+      processChildDescriptorsDeep(node, descriptors, sortedByPosition, readOnlyFilesSink);
     }
 
-    for (Map.Entry<Object, Collection<CommonProblemDescriptor>> entry : parentToChildNode.entrySet()) {
-      final Collection<CommonProblemDescriptor> siblings = entry.getValue();
+    for (Map.Entry<Object, Collection<ProblemDescriptionNode>> entry : parentToChildNode.entrySet()) {
+      final Collection<ProblemDescriptionNode> siblings = entry.getValue();
       if (siblings.size() == 1) {
-        @SuppressWarnings("ConstantConditions")
-        final CommonProblemDescriptor descriptor = ContainerUtil.getFirstItem(siblings);
+        final ProblemDescriptionNode descriptorNode = ContainerUtil.getFirstItem(siblings);
+        LOG.assertTrue(descriptorNode != null);
+        CommonProblemDescriptor descriptor = descriptorNode.getDescriptor();
         if (descriptor != null) {
           descriptors.add(descriptor);
+          if (readOnlyFilesSink != null) {
+            checkDescriptorFileIsWritable(descriptor, readOnlyFilesSink);
+          }
         }
       } else {
+        Stream<CommonProblemDescriptor> descriptorStream = siblings.stream().map(ProblemDescriptionNode::getDescriptor);
         if (sortedByPosition) {
-          siblings.stream().sorted(DESCRIPTOR_COMPARATOR).forEach(descriptors::add);
-        } else {
-          descriptors.addAll(siblings);
+          descriptorStream = descriptorStream.sorted(DESCRIPTOR_COMPARATOR);
+        }
+        descriptorStream = descriptorStream.filter(descriptors::add);
+        if (readOnlyFilesSink != null) {
+          checkDescriptorsFileIsWritable(descriptorStream.collect(Collectors.toList()), readOnlyFilesSink);
         }
       }
     }
@@ -311,7 +327,8 @@ public class InspectionTree extends Tree {
 
   private void processChildDescriptorsDeep(InspectionTreeNode node,
                                            LinkedHashSet<CommonProblemDescriptor> descriptors,
-                                           boolean sortedByPosition) {
+                                           boolean sortedByPosition,
+                                           @Nullable Set<VirtualFile> readOnlyFilesSink) {
     List<CommonProblemDescriptor> descriptorChildren = null;
     for (int i = 0; i < node.getChildCount(); i++) {
       final TreeNode child = node.getChildAt(i);
@@ -328,7 +345,7 @@ public class InspectionTree extends Tree {
         }
       }
       else {
-        processChildDescriptorsDeep((InspectionTreeNode)child, descriptors, sortedByPosition);
+        processChildDescriptorsDeep((InspectionTreeNode)child, descriptors, sortedByPosition, readOnlyFilesSink);
       }
     }
 
@@ -336,6 +353,10 @@ public class InspectionTree extends Tree {
       if (descriptorChildren.size() > 1) {
         Collections.sort(descriptorChildren, DESCRIPTOR_COMPARATOR);
       }
+      if (readOnlyFilesSink != null) {
+        checkDescriptorsFileIsWritable(descriptorChildren, readOnlyFilesSink);
+      }
+
       descriptors.addAll(descriptorChildren);
     }
   }
@@ -389,4 +410,23 @@ public class InspectionTree extends Tree {
   public GlobalInspectionContextImpl getContext() {
     return myContext;
   }
+
+  private static void checkDescriptorsFileIsWritable(@NotNull Collection<CommonProblemDescriptor> descriptors, @NotNull Set<VirtualFile> readOnlySink) {
+    for (CommonProblemDescriptor descriptor : descriptors) {
+      if (checkDescriptorFileIsWritable(descriptor, readOnlySink)) {
+        return;
+      }
+    }
+  }
+
+  private static boolean checkDescriptorFileIsWritable(@NotNull CommonProblemDescriptor descriptor, @NotNull Set<VirtualFile> readOnlySink) {
+    if (descriptor instanceof ProblemDescriptor) {
+      PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement();
+      if (psiElement != null && !psiElement.isWritable()) {
+        readOnlySink.add(psiElement.getContainingFile().getVirtualFile());
+        return true;
+      }
+    }
+    return false;
+  }
 }
index c58bf82520a856c9a5574468b151748b7dfb2f47..0c15fe13528871d27101e80312e06a8097a78784 100644 (file)
@@ -66,7 +66,7 @@ public class QuickFixPreviewPanelFactory {
       myView = view;
       myWrapper = view.getTree().getSelectedToolWrapper();
       LOG.assertTrue(myWrapper != null);
-      CommonProblemDescriptor[] descriptors = myView.getTree().getSelectedDescriptors(false);
+      CommonProblemDescriptor[] descriptors = myView.getTree().getSelectedDescriptors();
       if (editor != null) {
         new ProblemPreviewEditorPresentation(editor, view.getProject(), descriptors);
       }
index 192eba08e023aee36cd988fcdc2bd81800dba24c..996ecead056a852568ac2b52b51d950df22015cb 100644 (file)
@@ -711,3 +711,4 @@ todo.comment.problem.descriptor=TODO comment <code>#ref</code> #loc
 long.line.display.name=Line is longer than allowed by code style
 
 inspection.default.annotation.param=Default annotation parameter value
+preparing.for.apply.fix=Preparing to Apply Fix...