From 263b9687d20f42c5d4e90a17c48e7c4ac5083560 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 20 Apr 2016 13:25:06 +0300 Subject: [PATCH] make "whole file" inspections restart even in case of other file inside codeblock modifications (to make possible to write inspections like 'unused symbols') --- .../impl/DaemonRespondToChangesTest.java | 126 +++++++++++------- .../WholeFileLocalInspectionsPassFactory.java | 47 +++---- 2 files changed, 101 insertions(+), 72 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java index 57088c72c084..747147986dd3 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java @@ -500,71 +500,99 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { assertEquals("Field can be converted to a local variable", infos.get(0).getDescription()); } + private static class MyWholeInspection extends LocalInspectionTool { + private final List visited = Collections.synchronizedList(new ArrayList<>()); + + @Nls + @NotNull + @Override + public String getGroupDisplayName() { + return "fegna"; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return getGroupDisplayName(); + } + + @NotNull + @Override + public String getShortName() { + return getGroupDisplayName(); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new PsiElementVisitor() { + @Override + public void visitFile(PsiFile file) { + TimeoutUtil.sleep(1000); // make it run longer that LIP + super.visitFile(file); + } + + @Override + public void visitElement(PsiElement element) { + visited.add(element); + super.visitElement(element); + } + }; + } + + @Override + public boolean runForWholeFile() { + return true; + } + } + public void testWholeFileInspectionRestartedOnAllElements() throws Exception { - final List visited = Collections.synchronizedList(new ArrayList<>()); - final LocalInspectionTool tool = new LocalInspectionTool() { - @Nls - @NotNull - @Override - public String getGroupDisplayName() { - return "fegna"; - } - - @Nls - @NotNull - @Override - public String getDisplayName() { - return getGroupDisplayName(); - } - - @NotNull - @Override - public String getShortName() { - return getGroupDisplayName(); - } - - @NotNull - @Override - public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - return new PsiElementVisitor() { - @Override - public void visitFile(PsiFile file) { - TimeoutUtil.sleep(1000); // make it run longer that LIP - super.visitFile(file); - } - - @Override - public void visitElement(PsiElement element) { - visited.add(element); - super.visitElement(element); - } - }; - } - - @Override - public boolean runForWholeFile() { - return true; - } - }; + MyWholeInspection tool = new MyWholeInspection(); enableInspectionTool(tool); disposeOnTearDown(() -> disableInspectionTool(tool.getShortName())); configureByText(JavaFileType.INSTANCE, "class X { void f() { } }"); List infos = doHighlighting(HighlightSeverity.WARNING); assertEmpty(infos); - int visitedCount = visited.size(); - visited.clear(); + int visitedCount = tool.visited.size(); + tool.visited.clear(); type(" "); // white space modification infos = doHighlighting(HighlightSeverity.WARNING); assertEmpty(infos); - int countAfter = visited.size(); + int countAfter = tool.visited.size(); assertTrue("visitedCount = "+visitedCount+"; countAfter="+countAfter, countAfter >= visitedCount); } - + public void testWholeFileInspectionRestartedEvenIfThereWasAModificationInsideCodeBlockInOtherFile() throws Exception { + MyWholeInspection tool = new MyWholeInspection(); + + enableInspectionTool(tool); + disposeOnTearDown(() -> disableInspectionTool(tool.getShortName())); + + PsiFile file = configureByText(JavaFileType.INSTANCE, "class X { void f() { } }"); + PsiFile otherFile = createFile(myModule, file.getContainingDirectory().getVirtualFile(), "otherFile.txt", "xxx"); + List infos = doHighlighting(HighlightSeverity.WARNING); + assertEmpty(infos); + int visitedCount = tool.visited.size(); + assertTrue(tool.visited.toString(), visitedCount > 0); + tool.visited.clear(); + + Document otherDocument = PsiDocumentManager.getInstance(getProject()).getDocument(otherFile); + WriteCommandAction.runWriteCommandAction(getProject(), () -> { + otherDocument.setText("zzz"); + }); + + infos = doHighlighting(HighlightSeverity.WARNING); + assertEmpty(infos); + + int countAfter = tool.visited.size(); + assertTrue(tool.visited.toString(), countAfter > 0); + } + public void testOverriddenMethodMarkers() throws Exception { configureByFile(BASE_PATH + getTestName(false) + ".java"); highlightErrors(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java index 45b5825a2548..bd78d1e6e7ca 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,33 +24,33 @@ import com.intellij.codeInsight.daemon.DaemonBundle; import com.intellij.codeInspection.InspectionManager; import com.intellij.codeInspection.ex.InspectionProfileWrapper; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; -import com.intellij.openapi.Disposable; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.TextRange; import com.intellij.profile.Profile; import com.intellij.profile.ProfileChangeAdapter; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * @author cdr */ public class WholeFileLocalInspectionsPassFactory extends AbstractProjectComponent implements TextEditorHighlightingPassFactory { - private final Map myFileTools = ContainerUtil.createConcurrentWeakMap(); - public InspectionProjectProfileManager myProfileManager; + private final Map myFileToolsCache = ContainerUtil.createConcurrentWeakMap(); + private final InspectionProjectProfileManager myProfileManager; + private volatile long myPsiModificationCount; public WholeFileLocalInspectionsPassFactory(Project project, TextEditorHighlightingPassRegistrar highlightingPassRegistrar, final InspectionProjectProfileManager profileManager) { @@ -72,30 +72,28 @@ public class WholeFileLocalInspectionsPassFactory extends AbstractProjectCompone final ProfileChangeAdapter myProfilesListener = new ProfileChangeAdapter() { @Override public void profileChanged(Profile profile) { - myFileTools.clear(); + myFileToolsCache.clear(); } @Override public void profileActivated(Profile oldProfile, Profile profile) { - myFileTools.clear(); + myFileToolsCache.clear(); } }; myProfileManager.addProfilesListener(myProfilesListener, myProject); - Disposer.register(myProject, new Disposable() { - @Override - public void dispose() { - myFileTools.clear(); - } - }); + Disposer.register(myProject, myFileToolsCache::clear); } @Override @Nullable public TextEditorHighlightingPass createHighlightingPass(@NotNull final PsiFile file, @NotNull final Editor editor) { - TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.LOCAL_INSPECTIONS); - if (textRange == null || - !InspectionProjectProfileManager.getInstance(file.getProject()).isProfileLoaded() || - myFileTools.containsKey(file) && !myFileTools.get(file)) { + final long psiModificationCount = PsiManager.getInstance(myProject).getModificationTracker().getModificationCount(); + if (psiModificationCount == myPsiModificationCount) { + return null; //optimization + } + + if (!InspectionProjectProfileManager.getInstance(file.getProject()).isProfileLoaded() || + myFileToolsCache.containsKey(file) && !myFileToolsCache.get(file)) { return null; } @@ -105,11 +103,8 @@ public class WholeFileLocalInspectionsPassFactory extends AbstractProjectCompone @Override List getInspectionTools(@NotNull InspectionProfileWrapper profile) { List tools = super.getInspectionTools(profile); - List result = new ArrayList(tools.size()); - for (LocalInspectionToolWrapper tool : tools) { - if (tool.runForWholeFile()) result.add(tool); - } - myFileTools.put(file, !result.isEmpty()); + List result = tools.stream().filter(LocalInspectionToolWrapper::runForWholeFile).collect(Collectors.toList()); + myFileToolsCache.put(file, !result.isEmpty()); return result; } @@ -127,6 +122,12 @@ public class WholeFileLocalInspectionsPassFactory extends AbstractProjectCompone @NotNull List wrappers) { // already inspected in LIP } + + @Override + protected void applyInformationWithProgress() { + super.applyInformationWithProgress(); + myPsiModificationCount = PsiManager.getInstance(myProject).getModificationTracker().getModificationCount(); + } }; }