mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-06 06:21:16 +07:00
1057 lines
45 KiB
Java
1057 lines
45 KiB
Java
/*
|
|
* Copyright 2000-2017 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.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.intellij.codeInspection.ex;
|
|
|
|
import com.intellij.analysis.AnalysisScope;
|
|
import com.intellij.analysis.AnalysisUIOptions;
|
|
import com.intellij.analysis.PerformAnalysisInBackgroundOption;
|
|
import com.intellij.codeInsight.FileModificationService;
|
|
import com.intellij.codeInsight.daemon.ProblemHighlightFilter;
|
|
import com.intellij.codeInsight.daemon.impl.HighlightInfoProcessor;
|
|
import com.intellij.codeInsight.daemon.impl.LocalInspectionsPass;
|
|
import com.intellij.codeInspection.*;
|
|
import com.intellij.codeInspection.actions.CleanupInspectionIntention;
|
|
import com.intellij.codeInspection.lang.GlobalInspectionContextExtension;
|
|
import com.intellij.codeInspection.reference.RefElement;
|
|
import com.intellij.codeInspection.reference.RefEntity;
|
|
import com.intellij.codeInspection.reference.RefManagerImpl;
|
|
import com.intellij.codeInspection.reference.RefVisitor;
|
|
import com.intellij.codeInspection.ui.DefaultInspectionToolPresentation;
|
|
import com.intellij.codeInspection.ui.InspectionResultsView;
|
|
import com.intellij.codeInspection.ui.InspectionToolPresentation;
|
|
import com.intellij.codeInspection.ui.InspectionTreeState;
|
|
import com.intellij.concurrency.JobLauncher;
|
|
import com.intellij.concurrency.JobLauncherImpl;
|
|
import com.intellij.concurrency.SensitiveProgressWrapper;
|
|
import com.intellij.diagnostic.ThreadDumper;
|
|
import com.intellij.lang.annotation.ProblemGroup;
|
|
import com.intellij.lang.injection.InjectedLanguageManager;
|
|
import com.intellij.notification.NotificationGroup;
|
|
import com.intellij.openapi.Disposable;
|
|
import com.intellij.openapi.actionSystem.ToggleAction;
|
|
import com.intellij.openapi.application.*;
|
|
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
|
import com.intellij.openapi.components.PathMacroManager;
|
|
import com.intellij.openapi.diagnostic.Logger;
|
|
import com.intellij.openapi.editor.Document;
|
|
import com.intellij.openapi.progress.*;
|
|
import com.intellij.openapi.progress.impl.CoreProgressManager;
|
|
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
|
import com.intellij.openapi.project.IndexNotReadyException;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.project.ProjectUtil;
|
|
import com.intellij.openapi.project.ProjectUtilCore;
|
|
import com.intellij.openapi.roots.FileIndex;
|
|
import com.intellij.openapi.roots.ProjectRootManager;
|
|
import com.intellij.openapi.ui.MessageType;
|
|
import com.intellij.openapi.util.*;
|
|
import com.intellij.openapi.util.io.FileUtil;
|
|
import com.intellij.openapi.util.text.StringUtil;
|
|
import com.intellij.openapi.vfs.CharsetToolkit;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.openapi.wm.ToolWindowId;
|
|
import com.intellij.openapi.wm.ToolWindowManager;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.search.LocalSearchScope;
|
|
import com.intellij.psi.search.SearchScope;
|
|
import com.intellij.psi.search.scope.packageSet.NamedScope;
|
|
import com.intellij.psi.util.PsiTreeUtil;
|
|
import com.intellij.psi.util.PsiUtilCore;
|
|
import com.intellij.ui.GuiUtils;
|
|
import com.intellij.ui.content.*;
|
|
import com.intellij.util.ConcurrencyUtil;
|
|
import com.intellij.util.IncorrectOperationException;
|
|
import com.intellij.util.Processor;
|
|
import com.intellij.util.TripleFunction;
|
|
import com.intellij.util.containers.ContainerUtil;
|
|
import com.intellij.util.containers.HashMap;
|
|
import com.intellij.util.containers.HashSet;
|
|
import com.intellij.util.ui.UIUtil;
|
|
import gnu.trove.THashSet;
|
|
import org.jdom.Element;
|
|
import org.jetbrains.annotations.NonNls;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStreamWriter;
|
|
import java.lang.reflect.Constructor;
|
|
import java.util.*;
|
|
import java.util.concurrent.*;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
public class GlobalInspectionContextImpl extends GlobalInspectionContextBase implements GlobalInspectionContext {
|
|
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl");
|
|
|
|
public static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup("Inspection Results", ToolWindowId.INSPECTION);
|
|
|
|
private final NotNullLazyValue<ContentManager> myContentManager;
|
|
private volatile InspectionResultsView myView;
|
|
private Content myContent;
|
|
private volatile boolean myViewClosed = true;
|
|
private long myInspectionStartedTimestamp;
|
|
|
|
@NotNull
|
|
private AnalysisUIOptions myUIOptions;
|
|
private InspectionTreeState myTreeState;
|
|
|
|
public GlobalInspectionContextImpl(@NotNull Project project, @NotNull NotNullLazyValue<ContentManager> contentManager) {
|
|
super(project);
|
|
myUIOptions = AnalysisUIOptions.getInstance(project).copy();
|
|
myContentManager = contentManager;
|
|
}
|
|
|
|
@NotNull
|
|
private ContentManager getContentManager() {
|
|
return myContentManager.getValue();
|
|
}
|
|
|
|
public void setTreeState(InspectionTreeState treeState) {
|
|
myTreeState = treeState;
|
|
}
|
|
|
|
public void addView(@NotNull InspectionResultsView view,
|
|
@NotNull String title,
|
|
boolean isOffline) {
|
|
LOG.assertTrue(myContent == null, "GlobalInspectionContext is busy under other view now");
|
|
myContentManager.getValue().addContentManagerListener(new ContentManagerAdapter() {
|
|
@Override
|
|
public void contentRemoved(ContentManagerEvent event) {
|
|
if (event.getContent() == myContent) {
|
|
if (myView != null) {
|
|
close(false);
|
|
}
|
|
myContent = null;
|
|
}
|
|
}
|
|
});
|
|
|
|
myView = view;
|
|
if (!isOffline) {
|
|
myView.setUpdating(true);
|
|
}
|
|
if (myTreeState != null) {
|
|
myView.getTree().setTreeState(myTreeState);
|
|
}
|
|
myContent = ContentFactory.SERVICE.getInstance().createContent(view, title, false);
|
|
|
|
myContent.setDisposer(myView);
|
|
|
|
ContentManager contentManager = getContentManager();
|
|
contentManager.addContent(myContent);
|
|
contentManager.setSelectedContent(myContent);
|
|
|
|
ToolWindowManager.getInstance(getProject()).getToolWindow(ToolWindowId.INSPECTION).activate(null);
|
|
}
|
|
|
|
public void addView(@NotNull InspectionResultsView view) {
|
|
addView(view, InspectionsBundle.message(view.isSingleInspectionRun() ?
|
|
"inspection.results.for.inspection.toolwindow.title" :
|
|
"inspection.results.for.profile.toolwindow.title",
|
|
view.getCurrentProfileName(), getCurrentScope().getShortenName()), false);
|
|
|
|
}
|
|
|
|
@Override
|
|
public void doInspections(@NotNull final AnalysisScope scope) {
|
|
if (myContent != null) {
|
|
getContentManager().removeContent(myContent, true);
|
|
}
|
|
super.doInspections(scope);
|
|
}
|
|
|
|
public void launchInspectionsOffline(@NotNull final AnalysisScope scope,
|
|
@Nullable final String outputPath,
|
|
final boolean runGlobalToolsOnly,
|
|
@NotNull final List<File> inspectionsResults) {
|
|
performInspectionsWithProgressAndExportResults(scope, runGlobalToolsOnly, true, outputPath, inspectionsResults);
|
|
}
|
|
|
|
public void performInspectionsWithProgressAndExportResults(@NotNull final AnalysisScope scope,
|
|
final boolean runGlobalToolsOnly,
|
|
final boolean isOfflineInspections,
|
|
@Nullable final String outputPath,
|
|
@NotNull final List<File> inspectionsResults) {
|
|
cleanupTools();
|
|
setCurrentScope(scope);
|
|
|
|
final Runnable action = () -> {
|
|
DefaultInspectionToolPresentation.setOutputPath(outputPath);
|
|
try {
|
|
performInspectionsWithProgress(scope, runGlobalToolsOnly, isOfflineInspections);
|
|
exportResults(inspectionsResults, outputPath);
|
|
}
|
|
finally {
|
|
DefaultInspectionToolPresentation.setOutputPath(null);
|
|
}
|
|
};
|
|
if (isOfflineInspections) {
|
|
ApplicationManager.getApplication().runReadAction(action);
|
|
}
|
|
else {
|
|
action.run();
|
|
}
|
|
}
|
|
|
|
private void exportResults(@NotNull List<File> inspectionsResults, @Nullable String outputPath) {
|
|
@NonNls final String ext = ".xml";
|
|
final Map<Element, Tools> globalTools = new HashMap<>();
|
|
for (Map.Entry<String,Tools> entry : getTools().entrySet()) {
|
|
final Tools sameTools = entry.getValue();
|
|
boolean hasProblems = false;
|
|
String toolName = entry.getKey();
|
|
if (sameTools != null) {
|
|
for (ScopeToolState toolDescr : sameTools.getTools()) {
|
|
InspectionToolWrapper toolWrapper = toolDescr.getTool();
|
|
if (toolWrapper instanceof LocalInspectionToolWrapper) {
|
|
hasProblems = new File(outputPath, toolName + ext).exists();
|
|
}
|
|
else {
|
|
InspectionToolPresentation presentation = getPresentation(toolWrapper);
|
|
presentation.updateContent();
|
|
if (presentation.hasReportedProblems()) {
|
|
final Element root = new Element(InspectionsBundle.message("inspection.problems"));
|
|
globalTools.put(root, sameTools);
|
|
LOG.assertTrue(!hasProblems, toolName);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (hasProblems) {
|
|
try {
|
|
new File(outputPath).mkdirs();
|
|
final File file = new File(outputPath, toolName + ext);
|
|
inspectionsResults.add(file);
|
|
FileUtil
|
|
.writeToFile(file, ("</" + InspectionsBundle.message("inspection.problems") + ">").getBytes(CharsetToolkit.UTF8_CHARSET), true);
|
|
}
|
|
catch (IOException e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
getRefManager().iterate(new RefVisitor() {
|
|
@Override
|
|
public void visitElement(@NotNull final RefEntity refEntity) {
|
|
for (Map.Entry<Element, Tools> entry : globalTools.entrySet()) {
|
|
Tools tools = entry.getValue();
|
|
Element element = entry.getKey();
|
|
for (ScopeToolState state : tools.getTools()) {
|
|
try {
|
|
InspectionToolWrapper toolWrapper = state.getTool();
|
|
InspectionToolPresentation presentation = getPresentation(toolWrapper);
|
|
presentation.exportResults(element, refEntity, d -> false);
|
|
}
|
|
catch (Throwable e) {
|
|
LOG.error("Problem when exporting: " + refEntity.getExternalName(), e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
for (Map.Entry<Element, Tools> entry : globalTools.entrySet()) {
|
|
final String toolName = entry.getValue().getShortName();
|
|
Element element = entry.getKey();
|
|
element.setAttribute(LOCAL_TOOL_ATTRIBUTE, Boolean.toString(false));
|
|
final org.jdom.Document doc = new org.jdom.Document(element);
|
|
PathMacroManager.getInstance(getProject()).collapsePaths(doc.getRootElement());
|
|
try {
|
|
new File(outputPath).mkdirs();
|
|
final File file = new File(outputPath, toolName + ext);
|
|
inspectionsResults.add(file);
|
|
|
|
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), CharsetToolkit.UTF8_CHARSET)) {
|
|
JDOMUtil.writeDocument(doc, writer, "\n");
|
|
}
|
|
}
|
|
catch (IOException e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ignoreElement(@NotNull InspectionProfileEntry tool, @NotNull PsiElement element) {
|
|
final RefElement refElement = getRefManager().getReference(element);
|
|
final Tools tools = getTools().get(tool.getShortName());
|
|
if (tools != null){
|
|
for (ScopeToolState state : tools.getTools()) {
|
|
InspectionToolWrapper toolWrapper = state.getTool();
|
|
ignoreElementRecursively(toolWrapper, refElement);
|
|
}
|
|
}
|
|
}
|
|
|
|
public InspectionResultsView getView() {
|
|
return myView;
|
|
}
|
|
|
|
private void ignoreElementRecursively(@NotNull InspectionToolWrapper toolWrapper, final RefEntity refElement) {
|
|
if (refElement != null) {
|
|
InspectionToolPresentation presentation = getPresentation(toolWrapper);
|
|
presentation.ignoreCurrentElement(refElement);
|
|
final List<RefEntity> children = refElement.getChildren();
|
|
for (RefEntity child : children) {
|
|
ignoreElementRecursively(toolWrapper, child);
|
|
}
|
|
}
|
|
}
|
|
|
|
@NotNull
|
|
public AnalysisUIOptions getUIOptions() {
|
|
return myUIOptions;
|
|
}
|
|
|
|
public void setSplitterProportion(final float proportion) {
|
|
myUIOptions.SPLITTER_PROPORTION = proportion;
|
|
}
|
|
|
|
@NotNull
|
|
public ToggleAction createToggleAutoscrollAction() {
|
|
return myUIOptions.getAutoScrollToSourceHandler().createToggleAction();
|
|
}
|
|
|
|
@Override
|
|
protected void launchInspections(@NotNull final AnalysisScope scope) {
|
|
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
|
myUIOptions = AnalysisUIOptions.getInstance(getProject()).copy();
|
|
}
|
|
myViewClosed = false;
|
|
super.launchInspections(scope);
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
protected PerformInBackgroundOption createOption() {
|
|
return new PerformAnalysisInBackgroundOption(getProject());
|
|
}
|
|
|
|
@Override
|
|
protected void notifyInspectionsFinished(@NotNull final AnalysisScope scope) {
|
|
if (ApplicationManager.getApplication().isUnitTestMode()) return;
|
|
UIUtil.invokeLaterIfNeeded(() -> {
|
|
long elapsed = System.currentTimeMillis() - myInspectionStartedTimestamp;
|
|
LOG.info("Code inspection finished. Took "+elapsed+"ms");
|
|
if (getProject().isDisposed()) return;
|
|
|
|
InspectionResultsView view = myView == null ? new InspectionResultsView(this, createContentProvider()) : null;
|
|
if (!(myView == null ? view : myView).hasProblems()) {
|
|
NOTIFICATION_GROUP.createNotification(InspectionsBundle.message("inspection.no.problems.message",
|
|
scope.getFileCount(),
|
|
scope.getShortenName()),
|
|
MessageType.INFO).notify(getProject());
|
|
close(true);
|
|
if (view != null) {
|
|
Disposer.dispose(view);
|
|
}
|
|
}
|
|
else if (view != null && !view.isDisposed() && getCurrentScope() != null) {
|
|
addView(view);
|
|
view.update();
|
|
}
|
|
if (myView != null) {
|
|
myView.setUpdating(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
protected void runTools(@NotNull final AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections) {
|
|
myInspectionStartedTimestamp = System.currentTimeMillis();
|
|
final ProgressIndicator progressIndicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
|
|
if (progressIndicator == null) {
|
|
throw new IncorrectOperationException("Must be run under progress");
|
|
}
|
|
if (!isOfflineInspections && ApplicationManager.getApplication().isDispatchThread()) {
|
|
throw new IncorrectOperationException("Must not start inspections from within EDT");
|
|
}
|
|
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
|
|
throw new IncorrectOperationException("Must not start inspections from within write action");
|
|
}
|
|
// in offline inspection application we don't care about global read action
|
|
if (!isOfflineInspections && ApplicationManager.getApplication().isReadAccessAllowed()) {
|
|
throw new IncorrectOperationException("Must not start inspections from within global read action");
|
|
}
|
|
final InspectionManager inspectionManager = InspectionManager.getInstance(getProject());
|
|
((RefManagerImpl)getRefManager()).initializeAnnotators();
|
|
final List<Tools> globalTools = new ArrayList<>();
|
|
final List<Tools> localTools = new ArrayList<>();
|
|
final List<Tools> globalSimpleTools = new ArrayList<>();
|
|
initializeTools(globalTools, localTools, globalSimpleTools);
|
|
appendPairedInspectionsForUnfairTools(globalTools, globalSimpleTools, localTools);
|
|
|
|
runGlobalTools(scope, inspectionManager, globalTools, isOfflineInspections);
|
|
|
|
if (runGlobalToolsOnly || localTools.isEmpty() && globalSimpleTools.isEmpty()) return;
|
|
|
|
SearchScope searchScope = ReadAction.compute(scope::toSearchScope);
|
|
final Set<VirtualFile> localScopeFiles = searchScope instanceof LocalSearchScope ? new THashSet<>() : null;
|
|
for (Tools tools : globalSimpleTools) {
|
|
GlobalInspectionToolWrapper toolWrapper = (GlobalInspectionToolWrapper)tools.getTool();
|
|
GlobalSimpleInspectionTool tool = (GlobalSimpleInspectionTool)toolWrapper.getTool();
|
|
tool.inspectionStarted(inspectionManager, this, getPresentation(toolWrapper));
|
|
}
|
|
|
|
final boolean headlessEnvironment = ApplicationManager.getApplication().isHeadlessEnvironment();
|
|
final Map<String, InspectionToolWrapper> map = getInspectionWrappersMap(localTools);
|
|
|
|
final BlockingQueue<PsiFile> filesToInspect = new ArrayBlockingQueue<>(1000);
|
|
// use original progress indicator here since we don't want it to cancel on write action start
|
|
ProgressIndicator iteratingIndicator = new SensitiveProgressWrapper(progressIndicator);
|
|
Future<?> future = startIterateScopeInBackground(scope, localScopeFiles, headlessEnvironment, filesToInspect, iteratingIndicator);
|
|
|
|
Processor<PsiFile> processor = file -> {
|
|
ProgressManager.checkCanceled();
|
|
if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(() -> {
|
|
if (!file.isValid()) {
|
|
return;
|
|
}
|
|
VirtualFile virtualFile = file.getVirtualFile();
|
|
if (!scope.contains(virtualFile)) {
|
|
LOG.error(file.getName()+"; scope: "+scope+"; "+virtualFile);
|
|
}
|
|
inspectFile(file, getEffectiveRange(searchScope, file), inspectionManager, localTools, globalSimpleTools, map);
|
|
})) {
|
|
throw new ProcessCanceledException();
|
|
}
|
|
|
|
boolean includeDoNotShow = includeDoNotShow(getCurrentProfile());
|
|
Stream.concat(getWrappersFromTools(localTools, file, includeDoNotShow).stream(),
|
|
getWrappersFromTools(globalSimpleTools, file, includeDoNotShow).stream())
|
|
.filter(wrapper -> wrapper.getTool() instanceof ExternalAnnotatorBatchInspection)
|
|
.forEach(wrapper -> {
|
|
ProblemDescriptor[] descriptors = ((ExternalAnnotatorBatchInspection)wrapper.getTool()).checkFile(file, this, inspectionManager);
|
|
InspectionToolPresentation toolPresentation = getPresentation(wrapper);
|
|
ReadAction.run(() -> LocalDescriptorsUtil.addProblemDescriptors(Arrays.asList(descriptors), false, this, null, CONVERT, toolPresentation));
|
|
});
|
|
|
|
return true;
|
|
};
|
|
try {
|
|
final Queue<PsiFile> filesFailedToInspect = new LinkedBlockingQueue<>();
|
|
while (true) {
|
|
Disposable disposable = Disposer.newDisposable();
|
|
ProgressIndicator wrapper = new SensitiveProgressWrapper(progressIndicator);
|
|
|
|
try {
|
|
// avoid "attach listener"/"write action" race
|
|
ReadAction.run(() -> {
|
|
wrapper.start();
|
|
ProgressIndicatorUtils.forceWriteActionPriority(wrapper, disposable);
|
|
// there is a chance we are racing with write action, in which case just registered listener might not be called, retry.
|
|
if (ApplicationManagerEx.getApplicationEx().isWriteActionPending()) {
|
|
throw new ProcessCanceledException();
|
|
}
|
|
});
|
|
// use wrapper here to cancel early when write action start but do not affect the original indicator
|
|
((JobLauncherImpl)JobLauncher.getInstance()).processQueue(filesToInspect, filesFailedToInspect, wrapper, TOMBSTONE, processor);
|
|
break;
|
|
}
|
|
catch (ProcessCanceledException ignored) {
|
|
progressIndicator.checkCanceled();
|
|
// PCE may be thrown from inside wrapper when write action started
|
|
// go on with the write and then resume processing the rest of the queue
|
|
assert !ApplicationManager.getApplication().isReadAccessAllowed();
|
|
assert !ApplicationManager.getApplication().isDispatchThread();
|
|
|
|
// wait for write action to complete
|
|
ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance());
|
|
}
|
|
finally {
|
|
Disposer.dispose(disposable);
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
iteratingIndicator.cancel(); // tell file scanning thread to stop
|
|
filesToInspect.clear(); // let file scanning thread a chance to put TOMBSTONE and complete
|
|
try {
|
|
future.get(30, TimeUnit.SECONDS);
|
|
}
|
|
catch (Exception e) {
|
|
LOG.error("Thread dump: \n"+ThreadDumper.dumpThreadsToString(), e);
|
|
}
|
|
}
|
|
|
|
ProgressManager.checkCanceled();
|
|
|
|
for (Tools tools : globalSimpleTools) {
|
|
GlobalInspectionToolWrapper toolWrapper = (GlobalInspectionToolWrapper)tools.getTool();
|
|
GlobalSimpleInspectionTool tool = (GlobalSimpleInspectionTool)toolWrapper.getTool();
|
|
ProblemDescriptionsProcessor problemDescriptionProcessor = getProblemDescriptionProcessor(toolWrapper, map);
|
|
tool.inspectionFinished(inspectionManager, this, problemDescriptionProcessor);
|
|
|
|
}
|
|
|
|
addProblemsToView(globalSimpleTools);
|
|
}
|
|
|
|
private static TextRange getEffectiveRange(SearchScope searchScope, PsiFile file) {
|
|
if (searchScope instanceof LocalSearchScope) {
|
|
PsiElement[] scopeFileElements = Arrays.stream(((LocalSearchScope)searchScope).getScope())
|
|
.filter(e -> e.getContainingFile() == file)
|
|
.toArray(PsiElement[]::new);
|
|
if (scopeFileElements.length > 0) {
|
|
int start = -1;
|
|
int end = -1;
|
|
for (PsiElement scopeElement : scopeFileElements) {
|
|
TextRange elementRange = scopeElement.getTextRange();
|
|
start = start == -1 ? elementRange.getStartOffset() : Math.min(elementRange.getStartOffset(), start);
|
|
end = end == -1 ? elementRange.getEndOffset() : Math.max(elementRange.getEndOffset(), end);
|
|
}
|
|
return new TextRange(start, end);
|
|
}
|
|
}
|
|
return new TextRange(0, file.getTextLength());
|
|
}
|
|
|
|
private void inspectFile(@NotNull final PsiFile file,
|
|
@NotNull final TextRange range,
|
|
@NotNull final InspectionManager inspectionManager,
|
|
@NotNull List<Tools> localTools,
|
|
@NotNull List<Tools> globalSimpleTools,
|
|
@NotNull final Map<String, InspectionToolWrapper> wrappersMap) {
|
|
Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
|
|
if (document == null) return;
|
|
|
|
VirtualFile virtualFile = file.getVirtualFile();
|
|
String url = ProjectUtilCore.displayUrlRelativeToProject(virtualFile, virtualFile.getPresentableUrl(), getProject(), true, false);
|
|
incrementJobDoneAmount(getStdJobDescriptors().LOCAL_ANALYSIS, url);
|
|
|
|
final LocalInspectionsPass pass = new LocalInspectionsPass(file, document, range.getStartOffset(),
|
|
range.getEndOffset(), LocalInspectionsPass.EMPTY_PRIORITY_RANGE, true,
|
|
HighlightInfoProcessor.getEmpty());
|
|
try {
|
|
boolean includeDoNotShow = includeDoNotShow(getCurrentProfile());
|
|
final List<LocalInspectionToolWrapper> lTools = getWrappersFromTools(localTools, file, includeDoNotShow);
|
|
List<LocalInspectionToolWrapper> nonExternalAnnotators = lTools.stream().filter(wrapper -> !(wrapper.getTool() instanceof ExternalAnnotatorBatchInspection)).collect(Collectors.toList());
|
|
pass.doInspectInBatch(this, inspectionManager, nonExternalAnnotators);
|
|
|
|
List<GlobalInspectionToolWrapper> globalSTools = getWrappersFromTools(globalSimpleTools, file, includeDoNotShow);
|
|
final List<GlobalInspectionToolWrapper> tools = globalSTools.stream()
|
|
.filter(wrapper -> !(wrapper.getTool() instanceof ExternalAnnotatorBatchInspection)).collect(Collectors.toList());
|
|
JobLauncher.getInstance().invokeConcurrentlyUnderProgress(tools, myProgressIndicator, false, toolWrapper -> {
|
|
GlobalSimpleInspectionTool tool = (GlobalSimpleInspectionTool)toolWrapper.getTool();
|
|
ProblemsHolder holder = new ProblemsHolder(inspectionManager, file, false);
|
|
ProblemDescriptionsProcessor problemDescriptionProcessor = getProblemDescriptionProcessor(toolWrapper, wrappersMap);
|
|
tool.checkFile(file, inspectionManager, holder, this, problemDescriptionProcessor);
|
|
InspectionToolPresentation toolPresentation = getPresentation(toolWrapper);
|
|
LocalDescriptorsUtil.addProblemDescriptors(holder.getResults(), false, this, null, CONVERT, toolPresentation);
|
|
return true;
|
|
});
|
|
}
|
|
catch (ProcessCanceledException e) {
|
|
final Throwable cause = e.getCause();
|
|
if (cause == null) {
|
|
throw e;
|
|
}
|
|
LOG.error("In file: " + file, cause);
|
|
}
|
|
catch (IndexNotReadyException e) {
|
|
throw e;
|
|
}
|
|
catch (Throwable e) {
|
|
LOG.error("In file: " + file.getName(), e);
|
|
}
|
|
finally {
|
|
InjectedLanguageManager.getInstance(getProject()).dropFileCaches(file);
|
|
}
|
|
}
|
|
|
|
protected boolean includeDoNotShow(final InspectionProfile profile) {
|
|
return profile.getSingleTool() != null;
|
|
}
|
|
|
|
private static final PsiFile TOMBSTONE = PsiUtilCore.NULL_PSI_FILE;
|
|
|
|
@NotNull
|
|
private Future<?> startIterateScopeInBackground(@NotNull final AnalysisScope scope,
|
|
@Nullable final Collection<VirtualFile> localScopeFiles,
|
|
final boolean headlessEnvironment,
|
|
@NotNull final BlockingQueue<PsiFile> outFilesToInspect,
|
|
@NotNull final ProgressIndicator progressIndicator) {
|
|
Task.Backgroundable task = new Task.Backgroundable(getProject(), "Scanning Files to Inspect") {
|
|
@Override
|
|
public void run(@NotNull ProgressIndicator indicator) {
|
|
try {
|
|
final FileIndex fileIndex = ProjectRootManager.getInstance(getProject()).getFileIndex();
|
|
scope.accept(file -> {
|
|
ProgressManager.checkCanceled();
|
|
if (ProjectUtil.isProjectOrWorkspaceFile(file) || !fileIndex.isInContent(file)) return true;
|
|
|
|
PsiFile psiFile = ReadAction.compute(() -> {
|
|
if (getProject().isDisposed()) throw new ProcessCanceledException();
|
|
PsiFile psi = PsiManager.getInstance(getProject()).findFile(file);
|
|
Document document = psi == null ? null : shouldProcess(psi, headlessEnvironment, localScopeFiles);
|
|
if (document != null) {
|
|
return psi;
|
|
}
|
|
return null;
|
|
});
|
|
// do not inspect binary files
|
|
if (psiFile != null) {
|
|
try {
|
|
if (ApplicationManager.getApplication().isReadAccessAllowed()) {
|
|
throw new IllegalStateException("Must not have read action");
|
|
}
|
|
outFilesToInspect.put(psiFile);
|
|
}
|
|
catch (InterruptedException e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
ProgressManager.checkCanceled();
|
|
return true;
|
|
});
|
|
}
|
|
catch (ProcessCanceledException e) {
|
|
// ignore, but put tombstone
|
|
}
|
|
finally {
|
|
try {
|
|
outFilesToInspect.put(TOMBSTONE);
|
|
}
|
|
catch (InterruptedException e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
return ((CoreProgressManager)ProgressManager.getInstance()).runProcessWithProgressAsynchronously(task, progressIndicator, null);
|
|
}
|
|
|
|
private Document shouldProcess(@NotNull PsiFile file, boolean headlessEnvironment, @Nullable Collection<VirtualFile> localScopeFiles) {
|
|
final VirtualFile virtualFile = file.getVirtualFile();
|
|
if (virtualFile == null) return null;
|
|
if (isBinary(file)) return null; //do not inspect binary files
|
|
|
|
if (myViewClosed && !headlessEnvironment) {
|
|
throw new ProcessCanceledException();
|
|
}
|
|
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Running local inspections on " + virtualFile.getPath());
|
|
}
|
|
|
|
if (SingleRootFileViewProvider.isTooLargeForIntelligence(virtualFile)) return null;
|
|
if (localScopeFiles != null && !localScopeFiles.add(virtualFile)) return null;
|
|
if (!ProblemHighlightFilter.shouldProcessFileInBatch(file)) return null;
|
|
|
|
return PsiDocumentManager.getInstance(getProject()).getDocument(file);
|
|
}
|
|
|
|
private void runGlobalTools(@NotNull final AnalysisScope scope,
|
|
@NotNull final InspectionManager inspectionManager,
|
|
@NotNull List<Tools> globalTools,
|
|
boolean isOfflineInspections) {
|
|
LOG.assertTrue(!ApplicationManager.getApplication().isReadAccessAllowed() || isOfflineInspections, "Must not run under read action, too unresponsive");
|
|
final List<InspectionToolWrapper> needRepeatSearchRequest = new ArrayList<>();
|
|
|
|
final boolean canBeExternalUsages = !(scope.getScopeType() == AnalysisScope.PROJECT && scope.isIncludeTestSource());
|
|
for (Tools tools : globalTools) {
|
|
for (ScopeToolState state : tools.getTools()) {
|
|
final InspectionToolWrapper toolWrapper = state.getTool();
|
|
final GlobalInspectionTool tool = (GlobalInspectionTool)toolWrapper.getTool();
|
|
final InspectionToolPresentation toolPresentation = getPresentation(toolWrapper);
|
|
try {
|
|
if (tool.isGraphNeeded()) {
|
|
try {
|
|
((RefManagerImpl)getRefManager()).findAllDeclarations();
|
|
}
|
|
catch (Throwable e) {
|
|
getStdJobDescriptors().BUILD_GRAPH.setDoneAmount(0);
|
|
throw e;
|
|
}
|
|
}
|
|
ApplicationManager.getApplication().runReadAction(() -> {
|
|
tool.runInspection(scope, inspectionManager, this, toolPresentation);
|
|
//skip phase when we are sure that scope already contains everything, unused declaration though needs to proceed with its suspicious code
|
|
if ((canBeExternalUsages || tool.getAdditionalJobs(this) != null) &&
|
|
tool.queryExternalUsagesRequests(inspectionManager, this, toolPresentation)) {
|
|
needRepeatSearchRequest.add(toolWrapper);
|
|
}
|
|
});
|
|
}
|
|
catch (ProcessCanceledException | IndexNotReadyException e) {
|
|
throw e;
|
|
}
|
|
catch (Throwable e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (GlobalInspectionContextExtension extension : myExtensions.values()) {
|
|
try {
|
|
extension.performPostRunActivities(needRepeatSearchRequest, this);
|
|
}
|
|
catch (ProcessCanceledException | IndexNotReadyException e) {
|
|
throw e;
|
|
}
|
|
catch (Throwable e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
|
|
addProblemsToView(globalTools);
|
|
}
|
|
|
|
public ActionCallback initializeViewIfNeed() {
|
|
if (myView != null) {
|
|
return ActionCallback.DONE;
|
|
}
|
|
final Application app = ApplicationManager.getApplication();
|
|
final Runnable createView = () -> {
|
|
InspectionResultsView view = getView();
|
|
if (view == null) {
|
|
view = new InspectionResultsView(this, createContentProvider());
|
|
addView(view);
|
|
}
|
|
};
|
|
if (app.isUnitTestMode()) {
|
|
createView.run();
|
|
return ActionCallback.DONE;
|
|
} else {
|
|
return app.getInvokator().invokeLater(createView);
|
|
}
|
|
}
|
|
|
|
private void appendPairedInspectionsForUnfairTools(@NotNull List<Tools> globalTools,
|
|
@NotNull List<Tools> globalSimpleTools,
|
|
@NotNull List<Tools> localTools) {
|
|
Tools[] larray = localTools.toArray(new Tools[localTools.size()]);
|
|
for (Tools tool : larray) {
|
|
LocalInspectionToolWrapper toolWrapper = (LocalInspectionToolWrapper)tool.getTool();
|
|
LocalInspectionTool localTool = toolWrapper.getTool();
|
|
if (localTool instanceof PairedUnfairLocalInspectionTool) {
|
|
String batchShortName = ((PairedUnfairLocalInspectionTool)localTool).getInspectionForBatchShortName();
|
|
InspectionProfile currentProfile = getCurrentProfile();
|
|
InspectionToolWrapper batchInspection;
|
|
if (currentProfile == null) {
|
|
batchInspection = null;
|
|
}
|
|
else {
|
|
final InspectionToolWrapper pairedWrapper = currentProfile.getInspectionTool(batchShortName, getProject());
|
|
batchInspection = pairedWrapper != null ? pairedWrapper.createCopy() : null;
|
|
}
|
|
if (batchInspection != null && !getTools().containsKey(batchShortName)) {
|
|
// add to existing inspections to run
|
|
InspectionProfileEntry batchTool = batchInspection.getTool();
|
|
final ScopeToolState defaultState = tool.getDefaultState();
|
|
ToolsImpl newTool = new ToolsImpl(batchInspection, defaultState.getLevel(), true, defaultState.isEnabled());
|
|
for (ScopeToolState state : tool.getTools()) {
|
|
final NamedScope scope = state.getScope(getProject());
|
|
if (scope != null) {
|
|
newTool.addTool(scope, batchInspection, state.isEnabled(), state.getLevel());
|
|
}
|
|
}
|
|
if (batchTool instanceof LocalInspectionTool) localTools.add(newTool);
|
|
else if (batchTool instanceof GlobalSimpleInspectionTool) globalSimpleTools.add(newTool);
|
|
else if (batchTool instanceof GlobalInspectionTool) globalTools.add(newTool);
|
|
else throw new AssertionError(batchTool);
|
|
myTools.put(batchShortName, newTool);
|
|
batchInspection.initialize(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@NotNull
|
|
private static <T extends InspectionToolWrapper> List<T> getWrappersFromTools(@NotNull List<Tools> localTools,
|
|
@NotNull PsiFile file,
|
|
boolean includeDoNotShow) {
|
|
final List<T> lTools = new ArrayList<>();
|
|
for (Tools tool : localTools) {
|
|
//noinspection unchecked
|
|
final T enabledTool = (T)tool.getEnabledTool(file, includeDoNotShow);
|
|
if (enabledTool != null) {
|
|
lTools.add(enabledTool);
|
|
}
|
|
}
|
|
return lTools;
|
|
}
|
|
|
|
@NotNull
|
|
private ProblemDescriptionsProcessor getProblemDescriptionProcessor(@NotNull final GlobalInspectionToolWrapper toolWrapper,
|
|
@NotNull final Map<String, InspectionToolWrapper> wrappersMap) {
|
|
return new ProblemDescriptionsProcessor() {
|
|
@Override
|
|
public void addProblemElement(@Nullable RefEntity refEntity, @NotNull CommonProblemDescriptor... commonProblemDescriptors) {
|
|
for (CommonProblemDescriptor problemDescriptor : commonProblemDescriptors) {
|
|
if (!(problemDescriptor instanceof ProblemDescriptor)) {
|
|
continue;
|
|
}
|
|
ProblemGroup problemGroup = ((ProblemDescriptor)problemDescriptor).getProblemGroup();
|
|
|
|
InspectionToolWrapper targetWrapper = problemGroup == null ? toolWrapper : wrappersMap.get(problemGroup.getProblemName());
|
|
if (targetWrapper != null) { // Else it's switched off
|
|
InspectionToolPresentation toolPresentation = getPresentation(targetWrapper);
|
|
toolPresentation.addProblemElement(refEntity, problemDescriptor);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
@NotNull
|
|
private static Map<String, InspectionToolWrapper> getInspectionWrappersMap(@NotNull List<Tools> tools) {
|
|
Map<String, InspectionToolWrapper> name2Inspection = new HashMap<>(tools.size());
|
|
for (Tools tool : tools) {
|
|
InspectionToolWrapper toolWrapper = tool.getTool();
|
|
name2Inspection.put(toolWrapper.getShortName(), toolWrapper);
|
|
}
|
|
|
|
return name2Inspection;
|
|
}
|
|
|
|
private static final TripleFunction<LocalInspectionTool,PsiElement,GlobalInspectionContext,RefElement> CONVERT =
|
|
(tool, elt, context) -> {
|
|
PsiNamedElement problemElement = PsiTreeUtil.getNonStrictParentOfType(elt, PsiFile.class);
|
|
|
|
RefElement refElement = context.getRefManager().getReference(problemElement);
|
|
if (refElement == null && problemElement != null) { // no need to lose collected results
|
|
refElement = GlobalInspectionContextUtil.retrieveRefElement(elt, context);
|
|
}
|
|
return refElement;
|
|
};
|
|
|
|
|
|
@Override
|
|
public void close(boolean noSuspiciousCodeFound) {
|
|
if (!noSuspiciousCodeFound) {
|
|
if (myView.isRerun()) {
|
|
myViewClosed = true;
|
|
myView = null;
|
|
}
|
|
if (myView == null) {
|
|
return;
|
|
}
|
|
}
|
|
AnalysisUIOptions.getInstance(getProject()).save(myUIOptions);
|
|
if (myContent != null) {
|
|
final ContentManager contentManager = getContentManager();
|
|
contentManager.removeContent(myContent, true);
|
|
}
|
|
myViewClosed = true;
|
|
myView = null;
|
|
((InspectionManagerEx)InspectionManager.getInstance(getProject())).closeRunningContext(this);
|
|
for (Tools tools : getTools().values()) {
|
|
for (ScopeToolState state : tools.getTools()) {
|
|
InspectionToolWrapper toolWrapper = state.getTool();
|
|
getPresentation(toolWrapper).finalCleanup();
|
|
}
|
|
}
|
|
super.close(noSuspiciousCodeFound);
|
|
}
|
|
|
|
@Override
|
|
public void cleanup() {
|
|
if (myView != null) {
|
|
myView.setUpdating(false);
|
|
} else {
|
|
myPresentationMap.clear();
|
|
super.cleanup();
|
|
}
|
|
}
|
|
|
|
public void refreshViews() {
|
|
if (myView != null) {
|
|
myView.getTree().queueUpdate();
|
|
}
|
|
}
|
|
|
|
private final ConcurrentMap<InspectionToolWrapper, InspectionToolPresentation> myPresentationMap = ContainerUtil.newConcurrentMap();
|
|
@NotNull
|
|
public InspectionToolPresentation getPresentation(@NotNull InspectionToolWrapper toolWrapper) {
|
|
InspectionToolPresentation presentation = myPresentationMap.get(toolWrapper);
|
|
if (presentation == null) {
|
|
String presentationClass = StringUtil.notNullize(toolWrapper.myEP == null ? null : toolWrapper.myEP.presentation, DefaultInspectionToolPresentation.class.getName());
|
|
|
|
try {
|
|
Constructor<?> constructor = Class.forName(presentationClass).getConstructor(InspectionToolWrapper.class, GlobalInspectionContextImpl.class);
|
|
presentation = (InspectionToolPresentation)constructor.newInstance(toolWrapper, this);
|
|
}
|
|
catch (Exception e) {
|
|
LOG.error(e);
|
|
throw new RuntimeException(e);
|
|
}
|
|
presentation = ConcurrencyUtil.cacheOrGet(myPresentationMap, toolWrapper, presentation);
|
|
}
|
|
return presentation;
|
|
}
|
|
|
|
@Override
|
|
public void codeCleanup(@NotNull final AnalysisScope scope,
|
|
@NotNull final InspectionProfile profile,
|
|
@Nullable final String commandName,
|
|
@Nullable final Runnable postRunnable,
|
|
final boolean modal) {
|
|
String title = "Inspect Code...";
|
|
Task task = modal ? new Task.Modal(getProject(), title, true) {
|
|
@Override
|
|
public void run(@NotNull ProgressIndicator indicator) {
|
|
cleanup(scope, profile, postRunnable, commandName);
|
|
}
|
|
} : new Task.Backgroundable(getProject(), title, true) {
|
|
@Override
|
|
public void run(@NotNull ProgressIndicator indicator) {
|
|
cleanup(scope, profile, postRunnable, commandName);
|
|
}
|
|
};
|
|
ProgressManager.getInstance().run(task);
|
|
}
|
|
|
|
private void cleanup(@NotNull final AnalysisScope scope,
|
|
@NotNull InspectionProfile profile,
|
|
@Nullable final Runnable postRunnable,
|
|
@Nullable final String commandName) {
|
|
setCurrentScope(scope);
|
|
final int fileCount = scope.getFileCount();
|
|
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
|
|
|
|
final SearchScope searchScope = ReadAction.compute(scope::toSearchScope);
|
|
final TextRange range;
|
|
if (searchScope instanceof LocalSearchScope) {
|
|
final PsiElement[] elements = ((LocalSearchScope)searchScope).getScope();
|
|
range = elements.length == 1 ? ReadAction.compute(elements[0]::getTextRange) : null;
|
|
}
|
|
else {
|
|
range = null;
|
|
}
|
|
final Iterable<Tools> inspectionTools = ContainerUtil.filter(profile.getAllEnabledInspectionTools(getProject()), tools -> {
|
|
assert tools != null;
|
|
return tools.getTool().isCleanupTool();
|
|
});
|
|
boolean includeDoNotShow = includeDoNotShow(profile);
|
|
final RefManagerImpl refManager = (RefManagerImpl)getRefManager();
|
|
refManager.inspectionReadActionStarted();
|
|
List<ProblemDescriptor> descriptors = new ArrayList<>();
|
|
Set<PsiFile> files = new HashSet<>();
|
|
try {
|
|
scope.accept(new PsiElementVisitor() {
|
|
private int myCount;
|
|
@Override
|
|
public void visitFile(PsiFile file) {
|
|
if (progressIndicator != null) {
|
|
progressIndicator.setFraction((double)++myCount / fileCount);
|
|
}
|
|
if (isBinary(file)) return;
|
|
final List<LocalInspectionToolWrapper> lTools = new ArrayList<>();
|
|
for (final Tools tools : inspectionTools) {
|
|
InspectionToolWrapper tool = tools.getEnabledTool(file, includeDoNotShow);
|
|
if (tool instanceof GlobalInspectionToolWrapper) {
|
|
tool = ((GlobalInspectionToolWrapper)tool).getSharedLocalInspectionToolWrapper();
|
|
}
|
|
if (tool != null) {
|
|
lTools.add((LocalInspectionToolWrapper)tool);
|
|
tool.initialize(GlobalInspectionContextImpl.this);
|
|
}
|
|
}
|
|
|
|
if (!lTools.isEmpty()) {
|
|
try {
|
|
final LocalInspectionsPass pass = new LocalInspectionsPass(file, PsiDocumentManager.getInstance(getProject()).getDocument(file), range != null ? range.getStartOffset() : 0,
|
|
range != null ? range.getEndOffset() : file.getTextLength(), LocalInspectionsPass.EMPTY_PRIORITY_RANGE, true,
|
|
HighlightInfoProcessor.getEmpty());
|
|
Runnable runnable = () -> pass.doInspectInBatch(GlobalInspectionContextImpl.this, InspectionManager.getInstance(getProject()), lTools);
|
|
ApplicationManager.getApplication().runReadAction(runnable);
|
|
|
|
final Set<ProblemDescriptor> localDescriptors = new TreeSet<>(CommonProblemDescriptor.DESCRIPTOR_COMPARATOR);
|
|
for (LocalInspectionToolWrapper tool : lTools) {
|
|
InspectionToolPresentation toolPresentation = getPresentation(tool);
|
|
for (CommonProblemDescriptor descriptor : toolPresentation.getProblemDescriptors()) {
|
|
if (descriptor instanceof ProblemDescriptor) {
|
|
localDescriptors.add((ProblemDescriptor)descriptor);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (searchScope instanceof LocalSearchScope) {
|
|
for (Iterator<ProblemDescriptor> iterator = localDescriptors.iterator(); iterator.hasNext(); ) {
|
|
final ProblemDescriptor descriptor = iterator.next();
|
|
final TextRange infoRange = descriptor instanceof ProblemDescriptorBase ? ((ProblemDescriptorBase)descriptor).getTextRange() : null;
|
|
if (infoRange != null && !((LocalSearchScope)searchScope).containsRange(file, infoRange)) {
|
|
iterator.remove();
|
|
}
|
|
}
|
|
}
|
|
if (!localDescriptors.isEmpty()) {
|
|
descriptors.addAll(localDescriptors);
|
|
files.add(file);
|
|
}
|
|
}
|
|
finally {
|
|
myPresentationMap.clear();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
finally {
|
|
refManager.inspectionReadActionFinished();
|
|
}
|
|
|
|
if (files.isEmpty()) {
|
|
GuiUtils.invokeLaterIfNeeded(() -> {
|
|
if (commandName != null) {
|
|
NOTIFICATION_GROUP.createNotification(InspectionsBundle.message("inspection.no.problems.message", scope.getFileCount(), scope.getDisplayName()), MessageType.INFO).notify(getProject());
|
|
}
|
|
if (postRunnable != null) {
|
|
postRunnable.run();
|
|
}
|
|
}, ModalityState.defaultModalityState());
|
|
return;
|
|
}
|
|
|
|
Runnable runnable = () -> {
|
|
if (!FileModificationService.getInstance().preparePsiElementsForWrite(files)) return;
|
|
CleanupInspectionIntention.applyFixesNoSort(getProject(), "Code Cleanup", descriptors, null);
|
|
if (postRunnable != null) {
|
|
postRunnable.run();
|
|
}
|
|
};
|
|
TransactionGuard.submitTransaction(getProject(), runnable);
|
|
}
|
|
|
|
private static boolean isBinary(@NotNull PsiFile file) {
|
|
return file instanceof PsiBinaryFile || file.getFileType().isBinary();
|
|
}
|
|
|
|
public boolean isViewClosed() {
|
|
return myViewClosed;
|
|
}
|
|
|
|
private InspectionRVContentProvider createContentProvider() {
|
|
return new InspectionRVContentProviderImpl(getProject());
|
|
}
|
|
|
|
private void addProblemsToView(List<Tools> tools) {
|
|
if (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) {
|
|
return;
|
|
}
|
|
if (myView == null && !ReadAction.compute(() -> InspectionResultsView.hasProblems(tools, this, createContentProvider())).booleanValue()) {
|
|
return;
|
|
}
|
|
initializeViewIfNeed().doWhenDone(() -> myView.addTools(tools));
|
|
}
|
|
}
|