2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.codeInspection.ui;
19 import com.intellij.CommonBundle;
20 import com.intellij.analysis.AnalysisScope;
21 import com.intellij.analysis.AnalysisUIOptions;
22 import com.intellij.codeHighlighting.HighlightDisplayLevel;
23 import com.intellij.codeInsight.daemon.HighlightDisplayKey;
24 import com.intellij.codeInspection.*;
25 import com.intellij.codeInspection.ex.*;
26 import com.intellij.codeInspection.offlineViewer.OfflineInspectionRVContentProvider;
27 import com.intellij.codeInspection.reference.RefElement;
28 import com.intellij.codeInspection.reference.RefEntity;
29 import com.intellij.codeInspection.ui.actions.ExportHTMLAction;
30 import com.intellij.codeInspection.ui.actions.InspectionsOptionsToolbarAction;
31 import com.intellij.codeInspection.ui.actions.InvokeQuickFixAction;
32 import com.intellij.diff.util.DiffUtil;
33 import com.intellij.icons.AllIcons;
34 import com.intellij.ide.*;
35 import com.intellij.ide.actions.ContextHelpAction;
36 import com.intellij.ide.actions.exclusion.ExclusionHandler;
37 import com.intellij.injected.editor.VirtualFileWindow;
38 import com.intellij.openapi.Disposable;
39 import com.intellij.openapi.actionSystem.*;
40 import com.intellij.openapi.application.Application;
41 import com.intellij.openapi.application.ApplicationManager;
42 import com.intellij.openapi.application.ModalityState;
43 import com.intellij.openapi.diagnostic.Logger;
44 import com.intellij.openapi.editor.*;
45 import com.intellij.openapi.editor.colors.EditorColors;
46 import com.intellij.openapi.editor.ex.EditorEx;
47 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
48 import com.intellij.openapi.project.DumbAware;
49 import com.intellij.openapi.project.Project;
50 import com.intellij.openapi.ui.Splitter;
51 import com.intellij.openapi.ui.popup.JBPopup;
52 import com.intellij.openapi.util.Disposer;
53 import com.intellij.openapi.util.Key;
54 import com.intellij.openapi.util.Pair;
55 import com.intellij.openapi.util.TextRange;
56 import com.intellij.openapi.vfs.VirtualFile;
57 import com.intellij.openapi.wm.ToolWindowId;
58 import com.intellij.openapi.wm.ToolWindowManager;
59 import com.intellij.pom.Navigatable;
60 import com.intellij.profile.Profile;
61 import com.intellij.profile.ProfileChangeAdapter;
62 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
63 import com.intellij.psi.*;
64 import com.intellij.psi.util.PsiUtilCore;
65 import com.intellij.ui.*;
66 import com.intellij.util.ConcurrencyUtil;
67 import com.intellij.util.EditSourceOnDoubleClickHandler;
68 import com.intellij.util.OpenSourceUtil;
69 import com.intellij.util.containers.ContainerUtil;
70 import com.intellij.util.ui.UIUtil;
71 import com.intellij.util.ui.tree.TreeUtil;
72 import org.jetbrains.annotations.NonNls;
73 import org.jetbrains.annotations.NotNull;
74 import org.jetbrains.annotations.Nullable;
77 import javax.swing.event.TreeSelectionEvent;
78 import javax.swing.event.TreeSelectionListener;
79 import javax.swing.tree.DefaultMutableTreeNode;
80 import javax.swing.tree.DefaultTreeModel;
81 import javax.swing.tree.TreePath;
83 import java.awt.event.InputEvent;
84 import java.awt.event.KeyAdapter;
85 import java.awt.event.KeyEvent;
86 import java.awt.event.MouseEvent;
88 import java.util.List;
89 import java.util.concurrent.ConcurrentHashMap;
90 import java.util.concurrent.ConcurrentMap;
92 import static com.intellij.codeInspection.ex.InspectionRVContentProvider.insertByIndex;
97 public class InspectionResultsView extends JPanel implements Disposable, OccurenceNavigator, DataProvider {
98 private static final Logger LOG = Logger.getInstance(InspectionResultsView.class);
100 public static final DataKey<InspectionResultsView> DATA_KEY = DataKey.create("inspectionView");
101 private static final Key<Boolean> PREVIEW_EDITOR_IS_REUSED_KEY = Key.create("inspection.tool.window.preview.editor.is.reused");
103 private final Project myProject;
104 private final InspectionTree myTree;
105 private final ConcurrentMap<HighlightDisplayLevel, ConcurrentMap<String, InspectionGroupNode>> myGroups =
106 ContainerUtil.newConcurrentMap();
107 private final OccurenceNavigator myOccurenceNavigator;
108 private volatile InspectionProfile myInspectionProfile;
110 private final AnalysisScope myScope;
112 private static final String HELP_ID = "reference.toolWindows.inspections";
113 private final ConcurrentMap<HighlightDisplayLevel, InspectionSeverityGroupNode> mySeverityGroupNodes = ContainerUtil.newConcurrentMap();
115 private final Splitter mySplitter;
117 private final GlobalInspectionContextImpl myGlobalInspectionContext;
118 private boolean myRerun;
119 private volatile boolean myDisposed;
120 private int myUpdatingRequestors; //accessed only in edt
121 private boolean myApplyingFix; //accessed only in edt
124 private final InspectionRVContentProvider myProvider;
125 private final ExclusionHandler<InspectionTreeNode> myExclusionHandler;
126 private EditorEx myPreviewEditor;
127 private InspectionTreeLoadingProgressAware myLoadingProgressPreview;
128 private final ExcludedInspectionTreeNodesManager myExcludedInspectionTreeNodesManager;
129 private final Set<Object> mySuppressedNodes = new HashSet<>();
130 private final ConcurrentMap<String, Set<SuppressIntentionAction>> mySuppressActions = new ConcurrentHashMap<>();
132 private final Object myTreeStructureUpdateLock = new Object();
134 public InspectionResultsView(@NotNull GlobalInspectionContextImpl globalInspectionContext,
135 @NotNull InspectionRVContentProvider provider) {
136 setLayout(new BorderLayout());
137 myProject = globalInspectionContext.getProject();
138 myInspectionProfile = globalInspectionContext.getCurrentProfile();
139 myScope = globalInspectionContext.getCurrentScope();
140 myGlobalInspectionContext = globalInspectionContext;
141 myProvider = provider;
142 myExcludedInspectionTreeNodesManager = new ExcludedInspectionTreeNodesManager(provider instanceof OfflineInspectionRVContentProvider,
143 globalInspectionContext.isSingleInspectionRun());
145 myTree = new InspectionTree(myProject, globalInspectionContext, this);
148 myOccurenceNavigator = initOccurenceNavigator();
150 mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION);
151 mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT));
152 mySplitter.setHonorComponentsMinimumSize(false);
154 mySplitter.addPropertyChangeListener(evt -> {
155 if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) {
156 myGlobalInspectionContext.setSplitterProportion(((Float)evt.getNewValue()).floatValue());
159 add(mySplitter, BorderLayout.CENTER);
160 myExclusionHandler = new ExclusionHandler<InspectionTreeNode>() {
162 public boolean isNodeExcluded(@NotNull InspectionTreeNode node) {
163 return node.isExcluded(myExcludedInspectionTreeNodesManager);
167 public void excludeNode(@NotNull InspectionTreeNode node) {
168 node.excludeElement(myExcludedInspectionTreeNodesManager);
169 if (myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS) {
170 final TreePath[] paths = myTree.getSelectionPaths();
171 LOG.assertTrue(paths != null);
172 InspectionTreeNode parent = (InspectionTreeNode)node.getParent();
173 InspectionTreeNode toSelect = null;
174 synchronized (myTreeStructureUpdateLock) {
175 if (paths.length == 1) {
176 toSelect = (InspectionTreeNode)node.getNextNode();
179 ((DefaultTreeModel)myTree.getModel()).reload(parent);
181 TreeUtil.selectInTree(toSelect == null ? parent : toSelect, true, myTree);
186 public void includeNode(@NotNull InspectionTreeNode node) {
187 node.amnestyElement(myExcludedInspectionTreeNodesManager);
191 public boolean isActionEnabled(boolean isExcludeAction) {
192 return isExcludeAction || !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS;
196 public void onDone(boolean isExcludeAction) {
197 if (!isExcludeAction || !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS) {
198 myTree.queueUpdate();
203 createActionsToolbar();
204 PsiManager.getInstance(myProject).addPsiTreeChangeListener(new InspectionViewPsiTreeChangeAdapter(this), this);
206 final InspectionProjectProfileManager profileManager = InspectionProjectProfileManager.getInstance(myProject);
207 profileManager.addProfilesListener(new ProfileChangeAdapter() {
209 public void profileChanged(Profile profile) {
210 if (profile == profileManager.getProjectProfileImpl()) {
219 private void initTreeListeners() {
220 myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
222 public void valueChanged(TreeSelectionEvent e) {
223 if (myTree.isUnderQueueUpdate()) return;
225 if (isAutoScrollMode()) {
226 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
231 EditSourceOnDoubleClickHandler.install(myTree);
233 myTree.addKeyListener(new KeyAdapter() {
235 public void keyPressed(KeyEvent e) {
236 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
237 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
242 PopupHandler.installPopupHandler(myTree, IdeActions.INSPECTION_TOOL_WINDOW_TREE_POPUP, ActionPlaces.CODE_INSPECTION);
243 SmartExpander.installOn(myTree);
246 private OccurenceNavigatorSupport initOccurenceNavigator() {
247 return new OccurenceNavigatorSupport(myTree) {
250 protected Navigatable createDescriptorForNode(DefaultMutableTreeNode node) {
251 if (node instanceof InspectionTreeNode && ((InspectionTreeNode)node).isExcluded(myExcludedInspectionTreeNodesManager)) {
254 if (node instanceof RefElementNode) {
255 final RefElementNode refNode = (RefElementNode)node;
256 if (refNode.hasDescriptorsUnder()) return null;
257 final RefEntity element = refNode.getElement();
258 if (element == null || !element.isValid()) return null;
259 final CommonProblemDescriptor problem = refNode.getDescriptor();
260 if (problem != null) {
261 return navigate(problem);
263 if (element instanceof RefElement) {
264 return getOpenFileDescriptor((RefElement)element);
267 else if (node instanceof ProblemDescriptionNode) {
269 if (((ProblemDescriptionNode)node).isValid()) {
270 if (((ProblemDescriptionNode)node).isQuickFixAppliedFromView()) {
271 isValid = ((ProblemDescriptionNode)node).calculateIsValid();
279 ? navigate(((ProblemDescriptionNode)node).getDescriptor())
280 : InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)node);
286 private Navigatable navigate(final CommonProblemDescriptor descriptor) {
287 return getSelectedNavigatable(descriptor);
291 public String getNextOccurenceActionName() {
292 return InspectionsBundle.message("inspection.action.go.next");
296 public String getPreviousOccurenceActionName() {
297 return InspectionsBundle.message("inspection.actiongo.prev");
302 private void createActionsToolbar() {
303 final JComponent leftActionsToolbar = createLeftActionsToolbar();
304 final JComponent rightActionsToolbar = createRightActionsToolbar();
306 JPanel westPanel = new JPanel(new BorderLayout());
307 westPanel.add(leftActionsToolbar, BorderLayout.WEST);
308 westPanel.add(rightActionsToolbar, BorderLayout.EAST);
309 add(westPanel, BorderLayout.WEST);
312 @SuppressWarnings({"NonStaticInitializer"})
313 private JComponent createRightActionsToolbar() {
314 DefaultActionGroup specialGroup = new DefaultActionGroup();
315 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupBySeverityAction(this));
316 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupByDirectoryAction(this));
317 specialGroup.add(myGlobalInspectionContext.getUIOptions().createFilterResolvedItemsAction(this));
318 specialGroup.add(ActionManager.getInstance().getAction("EditInspectionSettings"));
319 specialGroup.add(new InvokeQuickFixAction(this));
320 specialGroup.add(new InspectionsOptionsToolbarAction(this));
321 return createToolbar(specialGroup);
324 private JComponent createLeftActionsToolbar() {
325 final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
326 DefaultActionGroup group = new DefaultActionGroup();
327 group.add(new RerunAction(this));
328 group.add(new CloseAction());
329 final TreeExpander treeExpander = new TreeExpander() {
331 public void expandAll() {
332 TreeUtil.expandAll(myTree);
336 public boolean canExpand() {
341 public void collapseAll() {
342 TreeUtil.collapseAll(myTree, 0);
346 public boolean canCollapse() {
350 group.add(actionsManager.createExpandAllAction(treeExpander, myTree));
351 group.add(actionsManager.createCollapseAllAction(treeExpander, myTree));
352 group.add(actionsManager.createPrevOccurenceAction(getOccurenceNavigator()));
353 group.add(actionsManager.createNextOccurenceAction(getOccurenceNavigator()));
354 group.add(myGlobalInspectionContext.createToggleAutoscrollAction());
355 group.add(new ExportHTMLAction(this));
356 group.add(new ContextHelpAction(HELP_ID));
358 return createToolbar(group);
361 private static JComponent createToolbar(final DefaultActionGroup specialGroup) {
362 return ActionManager.getInstance().createActionToolbar(ActionPlaces.CODE_INSPECTION, specialGroup, false).getComponent();
366 public void dispose() {
367 InspectionResultsViewUtil.releaseEditor(myPreviewEditor);
368 mySplitter.dispose();
369 myInspectionProfile = null;
371 if (myLoadingProgressPreview != null) {
372 Disposer.dispose(myLoadingProgressPreview);
373 myLoadingProgressPreview = null;
378 private boolean isAutoScrollMode() {
379 String activeToolWindowId = ToolWindowManager.getInstance(myProject).getActiveToolWindowId();
380 return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE &&
381 (activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION));
385 private static OpenFileDescriptor getOpenFileDescriptor(final RefElement refElement) {
386 final VirtualFile[] file = new VirtualFile[1];
387 final int[] offset = new int[1];
389 ApplicationManager.getApplication().runReadAction(() -> {
390 PsiElement psiElement = refElement.getElement();
391 if (psiElement != null) {
392 final PsiFile containingFile = psiElement.getContainingFile();
393 if (containingFile != null) {
394 file[0] = containingFile.getVirtualFile();
395 offset[0] = psiElement.getTextOffset();
403 if (file[0] != null && file[0].isValid()) {
404 return new OpenFileDescriptor(refElement.getRefManager().getProject(), file[0], offset[0]);
409 public void setApplyingFix(boolean applyingFix) {
410 myApplyingFix = applyingFix;
414 public void openRightPanelIfNeed() {
415 if (mySplitter.getSecondComponent() == null) {
420 public void syncRightPanel() {
421 final Editor oldEditor = myPreviewEditor;
422 if (myLoadingProgressPreview != null) {
423 Disposer.dispose(myLoadingProgressPreview);
424 myLoadingProgressPreview = null;
427 final InspectionToolWrapper wrapper = myTree.getSelectedToolWrapper();
428 LOG.assertTrue(wrapper != null);
429 mySplitter.setSecondComponent(InspectionResultsViewUtil.getApplyingFixLabel(wrapper));
431 if (myTree.getSelectionModel().getSelectionCount() != 1) {
432 if (myTree.getSelectedToolWrapper() == null) {
433 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
436 showInRightPanel(myTree.getCommonSelectedElement());
440 TreePath pathSelected = myTree.getSelectionModel().getLeadSelectionPath();
441 if (pathSelected != null) {
442 final InspectionTreeNode node = (InspectionTreeNode)pathSelected.getLastPathComponent();
443 if (node instanceof ProblemDescriptionNode) {
444 final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
445 showInRightPanel(problemNode.getElement());
447 else if (node instanceof InspectionPackageNode ||
448 node instanceof InspectionModuleNode ||
449 node instanceof RefElementNode) {
450 showInRightPanel(node.getContainingFileLocalEntity());
452 else if (node instanceof InspectionNode) {
453 if (myGlobalInspectionContext.getPresentation(((InspectionNode)node).getToolWrapper()).isDummy()) {
454 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
457 showInRightPanel(null);
460 else if (node instanceof InspectionGroupNode || node instanceof InspectionSeverityGroupNode) {
461 final InspectionViewNavigationPanel panel = new InspectionViewNavigationPanel(node, myTree);
462 myLoadingProgressPreview = panel;
463 mySplitter.setSecondComponent(panel);
466 LOG.error("Unexpected node: " + node.getClass());
471 if (oldEditor != null) {
472 if (Boolean.TRUE.equals(oldEditor.getUserData(PREVIEW_EDITOR_IS_REUSED_KEY))) {
473 oldEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, null);
476 InspectionResultsViewUtil.releaseEditor(oldEditor);
477 if (oldEditor == myPreviewEditor) {
478 myPreviewEditor = null;
484 private void showInRightPanel(@Nullable final RefEntity refEntity) {
485 Cursor currentCursor = getCursor();
487 setCursor(new Cursor(Cursor.WAIT_CURSOR));
488 final JPanel editorPanel = new JPanel();
489 editorPanel.setLayout(new BorderLayout());
490 final int problemCount = myTree.getSelectedProblemCount();
491 JComponent previewPanel = null;
492 final InspectionToolWrapper tool = myTree.getSelectedToolWrapper();
493 if (tool != null && refEntity != null && refEntity.isValid()) {
494 final InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(tool);
495 previewPanel = presentation.getCustomPreviewPanel(refEntity);
497 EditorEx previewEditor = null;
498 if (previewPanel == null) {
499 final Pair<JComponent, EditorEx> panelAndEditor = createBaseRightComponentFor(problemCount, refEntity);
500 previewPanel = panelAndEditor.getFirst();
501 previewEditor = panelAndEditor.getSecond();
503 editorPanel.add(previewPanel, BorderLayout.CENTER);
504 if (problemCount > 0) {
505 final JComponent fixToolbar = QuickFixPreviewPanelFactory.create(previewEditor, this);
506 if (fixToolbar != null) {
507 if (fixToolbar instanceof InspectionTreeLoadingProgressAware) {
508 myLoadingProgressPreview = (InspectionTreeLoadingProgressAware)fixToolbar;
510 if (previewEditor != null) {
511 previewPanel.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP));
513 editorPanel.add(fixToolbar, BorderLayout.NORTH);
516 mySplitter.setSecondComponent(editorPanel);
519 setCursor(currentCursor);
523 private Pair<JComponent, EditorEx> createBaseRightComponentFor(int problemCount, RefEntity selectedEntity) {
524 if (selectedEntity instanceof RefElement &&
525 selectedEntity.isValid() &&
526 !(((RefElement)selectedEntity).getElement() instanceof PsiDirectory)) {
527 PsiElement selectedElement = ((RefElement)selectedEntity).getElement();
528 if (problemCount == 1) {
529 CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors();
530 if (descriptors.length != 0) {
531 final CommonProblemDescriptor descriptor = descriptors[0];
532 if (descriptor instanceof ProblemDescriptorBase) {
533 final PsiElement element = ((ProblemDescriptorBase)descriptor).getPsiElement();
534 if (element != null) {
535 selectedElement = element;
540 final PsiFile file = selectedElement.getContainingFile();
541 final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
542 if (document == null) {
543 return Pair.create(InspectionResultsViewUtil.createLabelForText("Can't open preview for \'" + file.getName() + "\'"), null);
546 if (reuseEditorFor(document)) {
547 myPreviewEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, true);
548 myPreviewEditor.getFoldingModel().runBatchFoldingOperation(() -> {
549 myPreviewEditor.getFoldingModel().clearFoldRegions();
550 myPreviewEditor.getMarkupModel().removeAllHighlighters();
554 myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true);
555 DiffUtil.setFoldingModelSupport(myPreviewEditor);
556 final EditorSettings settings = myPreviewEditor.getSettings();
557 settings.setLineNumbersShown(false);
558 settings.setLineMarkerAreaShown(false);
559 settings.setAdditionalColumnsCount(0);
560 settings.setAdditionalLinesCount(0);
561 settings.setLeadingWhitespaceShown(true);
562 myPreviewEditor.getColorsScheme().setColor(EditorColors.GUTTER_BACKGROUND, myPreviewEditor.getColorsScheme().getDefaultBackground());
563 myPreviewEditor.getScrollPane().setBorder(IdeBorderFactory.createEmptyBorder());
565 if (problemCount == 0) {
566 myPreviewEditor.getScrollingModel().scrollTo(myPreviewEditor.offsetToLogicalPosition(selectedElement.getTextOffset()), ScrollType.CENTER_UP);
568 myPreviewEditor.getSettings().setFoldingOutlineShown(problemCount > 1);
569 myPreviewEditor.getComponent().setBorder(IdeBorderFactory.createEmptyBorder());
570 return Pair.create(myPreviewEditor.getComponent(), myPreviewEditor);
572 else if (selectedEntity == null) {
573 return Pair.create(new InspectionNodeInfo(myTree, myProject), null);
575 return Pair.create(InspectionResultsViewUtil.getInvalidEntityLabel(selectedEntity), null);
578 private boolean reuseEditorFor(Document document) {
579 return myPreviewEditor != null && !myPreviewEditor.isDisposed() && myPreviewEditor.getDocument() == document;
583 public InspectionNode addTool(@NotNull final InspectionToolWrapper toolWrapper,
584 HighlightDisplayLevel errorLevel,
585 boolean groupedBySeverity,
586 boolean isSingleInspectionRun) {
588 toolWrapper.getGroupDisplayName().isEmpty() ? InspectionProfileEntry.GENERAL_GROUP_NAME : toolWrapper.getGroupDisplayName();
589 InspectionTreeNode parentNode = getToolParentNode(groupName, toolWrapper.getGroupPath(), errorLevel, groupedBySeverity, isSingleInspectionRun);
590 InspectionNode toolNode = new InspectionNode(toolWrapper, myInspectionProfile);
591 boolean showStructure = myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE;
592 myProvider.appendToolNodeContent(myGlobalInspectionContext, toolNode, parentNode, showStructure);
593 InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(toolWrapper);
594 toolNode = presentation.createToolNode(myGlobalInspectionContext, toolNode, myProvider, parentNode, showStructure);
595 synchronized (getTreeStructureUpdateLock()) {
596 ((DefaultInspectionToolPresentation)presentation).setToolNode(toolNode);
598 registerActionShortcuts(presentation);
602 private void registerActionShortcuts(@NotNull InspectionToolPresentation presentation) {
603 ApplicationManager.getApplication().invokeLater(() -> {
604 final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY, null);
606 for (QuickFixAction fix : fixes) {
607 fix.registerCustomShortcutSet(fix.getShortcutSet(), this);
614 public Set<SuppressIntentionAction> getSuppressActions(InspectionToolWrapper wrapper) {
615 return mySuppressActions.computeIfAbsent(wrapper.getShortName(), (w) -> {
616 final SuppressIntentionAction[] actions = InspectionManagerEx.getSuppressActions(wrapper);
617 return actions == null ? Collections.emptySet() : ContainerUtil.newLinkedHashSet(actions);
621 Set<Object> getSuppressedNodes() {
622 return mySuppressedNodes;
626 public ExcludedInspectionTreeNodesManager getExcludedManager() {
627 return myExcludedInspectionTreeNodesManager;
631 public String getCurrentProfileName() {
632 return myInspectionProfile == null ? null : myInspectionProfile.getDisplayName();
635 public InspectionProfile getCurrentProfile() {
636 return myInspectionProfile;
639 public void update() {
640 ApplicationManager.getApplication().assertIsDispatchThread();
641 myTree.removeAllNodes();
642 mySeverityGroupNodes.clear();
646 public void setUpdating(boolean isUpdating) {
647 final Runnable update = () -> {
649 myUpdatingRequestors++;
651 myUpdatingRequestors--;
653 boolean hasUpdatingRequestors = myUpdatingRequestors > 0;
654 myTree.setPaintBusy(hasUpdatingRequestors);
655 if (!hasUpdatingRequestors && myLoadingProgressPreview != null) {
656 myLoadingProgressPreview.treeLoaded();
658 //TODO Dmitrii Batkovich it's a hack (preview panel should be created during selection update)
659 if (!hasUpdatingRequestors && mySplitter.getSecondComponent() == null) {
660 if (myTree.getSelectionModel().getSelectionPath() == null) {
661 TreeUtil.selectFirstNode(myTree);
666 final Application app = ApplicationManager.getApplication();
667 if (app.isDispatchThread()) {
671 app.invokeLater(update, ModalityState.any());
675 public Object getTreeStructureUpdateLock() {
676 return myTreeStructureUpdateLock;
679 public void addTools(Collection<Tools> tools) {
680 InspectionProfileImpl profile = (InspectionProfileImpl)myInspectionProfile;
681 boolean isGroupedBySeverity = myGlobalInspectionContext.getUIOptions().GROUP_BY_SEVERITY;
682 boolean singleInspectionRun = myGlobalInspectionContext.isSingleInspectionRun();
683 for (Tools currentTools : tools) {
684 InspectionToolWrapper defaultToolWrapper = currentTools.getDefaultState().getTool();
685 if (myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS && myExcludedInspectionTreeNodesManager.containsInspectionNode(defaultToolWrapper)) {
688 final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
689 for (ScopeToolState state : myProvider.getTools(currentTools)) {
690 InspectionToolWrapper toolWrapper = state.getTool();
691 if (myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper)) {
693 profile.getErrorLevel(key, state.getScope(myProject), myProject),
695 singleInspectionRun);
701 public void buildTree() {
702 final Application app = ApplicationManager.getApplication();
703 final Runnable buildAction = () -> {
706 synchronized (getTreeStructureUpdateLock()) {
708 addTools(myGlobalInspectionContext.getTools().values());
713 UIUtil.invokeLaterIfNeeded(() -> myTree.restoreExpansionAndSelection(null));
716 if (app.isUnitTestMode()) {
719 app.executeOnPooledThread(() -> app.runReadAction(buildAction));
725 private InspectionTreeNode getToolParentNode(@NotNull String groupName,
726 @NotNull String[] groupPath,
727 HighlightDisplayLevel errorLevel,
728 boolean groupedBySeverity,
729 boolean isSingleInspectionRun) {
730 if (!groupedBySeverity && isSingleInspectionRun) {
731 return getTree().getRoot();
733 if (groupName.isEmpty()) {
734 return getRelativeRootNode(groupedBySeverity, errorLevel);
736 ConcurrentMap<String, InspectionGroupNode> map = myGroups.get(errorLevel);
738 map = ConcurrencyUtil.cacheOrGet(myGroups, errorLevel, ContainerUtil.newConcurrentMap());
740 InspectionGroupNode group;
741 if (groupedBySeverity) {
742 group = map.get(groupName);
746 for (Map<String, InspectionGroupNode> groupMap : myGroups.values()) {
747 if ((group = groupMap.get(groupName)) != null) break;
751 if (isSingleInspectionRun) {
752 return getRelativeRootNode(true, errorLevel);
754 group = ConcurrencyUtil.cacheOrGet(map, groupName, new InspectionGroupNode(groupName, groupPath));
756 insertByIndex(group, getRelativeRootNode(groupedBySeverity, errorLevel));
763 private InspectionTreeNode getRelativeRootNode(boolean isGroupedBySeverity, HighlightDisplayLevel level) {
764 if (isGroupedBySeverity) {
765 InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level);
766 if (severityGroupNode == null) {
767 InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myProject, level);
768 severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode);
769 if (severityGroupNode == newNode) {
770 InspectionTreeNode root = myTree.getRoot();
771 insertByIndex(severityGroupNode, root);
774 return severityGroupNode;
776 return myTree.getRoot();
779 private OccurenceNavigator getOccurenceNavigator() {
780 return myOccurenceNavigator;
784 public boolean hasNextOccurence() {
785 return myOccurenceNavigator != null && myOccurenceNavigator.hasNextOccurence();
789 public boolean hasPreviousOccurence() {
790 return myOccurenceNavigator != null && myOccurenceNavigator.hasPreviousOccurence();
794 public OccurenceInfo goNextOccurence() {
795 return myOccurenceNavigator != null ? myOccurenceNavigator.goNextOccurence() : null;
799 public OccurenceInfo goPreviousOccurence() {
800 return myOccurenceNavigator != null ? myOccurenceNavigator.goPreviousOccurence() : null;
804 public String getNextOccurenceActionName() {
805 return myOccurenceNavigator != null ? myOccurenceNavigator.getNextOccurenceActionName() : "";
809 public String getPreviousOccurenceActionName() {
810 return myOccurenceNavigator != null ? myOccurenceNavigator.getPreviousOccurenceActionName() : "";
814 public Project getProject() {
819 public Object getData(String dataId) {
820 if (PlatformDataKeys.HELP_ID.is(dataId)) return HELP_ID;
821 if (DATA_KEY.is(dataId)) return this;
822 if (ExclusionHandler.EXCLUSION_HANDLER.is(dataId)) return myExclusionHandler;
823 if (myTree == null) return null;
824 TreePath[] paths = myTree.getSelectionPaths();
826 if (paths == null || paths.length == 0) return null;
828 if (paths.length > 1) {
829 if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
830 return collectPsiElements();
835 TreePath path = paths[0];
837 InspectionTreeNode selectedNode = (InspectionTreeNode)path.getLastPathComponent();
839 if (!CommonDataKeys.NAVIGATABLE.is(dataId) && !CommonDataKeys.PSI_ELEMENT.is(dataId)) {
843 if (selectedNode instanceof RefElementNode) {
844 final RefElementNode refElementNode = (RefElementNode)selectedNode;
845 RefEntity refElement = refElementNode.getElement();
846 if (refElement == null) return null;
847 final RefEntity item = refElement.getRefManager().getRefinedElement(refElement);
849 if (!item.isValid()) return null;
851 PsiElement psiElement = item instanceof RefElement ? ((RefElement)item).getElement() : null;
852 if (psiElement == null) return null;
854 final CommonProblemDescriptor problem = refElementNode.getDescriptor();
855 if (problem != null) {
856 if (problem instanceof ProblemDescriptor) {
857 PsiElement elementFromDescriptor = ((ProblemDescriptor)problem).getPsiElement();
858 if (elementFromDescriptor == null) {
859 final InspectionTreeNode node = (InspectionTreeNode)refElementNode.getChildAt(0);
860 if (node.isValid()) {
864 psiElement = elementFromDescriptor;
872 if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
873 return getSelectedNavigatable(problem, psiElement);
875 else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
876 return psiElement.isValid() ? psiElement : null;
879 else if (selectedNode instanceof ProblemDescriptionNode && CommonDataKeys.NAVIGATABLE.is(dataId)) {
880 Navigatable navigatable = getSelectedNavigatable(((ProblemDescriptionNode)selectedNode).getDescriptor());
881 return navigatable == null ? InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)selectedNode) : navigatable;
888 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor) {
889 return getSelectedNavigatable(descriptor,
890 descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getPsiElement() : null);
894 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor, final PsiElement psiElement) {
895 if (descriptor instanceof ProblemDescriptorBase) {
896 Navigatable navigatable = ((ProblemDescriptorBase)descriptor).getNavigatable();
897 if (navigatable != null) {
901 if (psiElement == null || !psiElement.isValid()) return null;
902 PsiFile containingFile = psiElement.getContainingFile();
903 VirtualFile virtualFile = containingFile == null ? null : containingFile.getVirtualFile();
905 if (virtualFile != null) {
906 int startOffset = psiElement.getTextOffset();
907 if (descriptor instanceof ProblemDescriptorBase) {
908 final TextRange textRange = ((ProblemDescriptorBase)descriptor).getTextRangeForNavigation();
909 if (textRange != null) {
910 if (virtualFile instanceof VirtualFileWindow) {
911 virtualFile = ((VirtualFileWindow)virtualFile).getDelegate();
913 startOffset = textRange.getStartOffset();
916 return new OpenFileDescriptor(myProject, virtualFile, startOffset);
921 private PsiElement[] collectPsiElements() {
922 RefEntity[] refElements = myTree.getSelectedElements();
923 List<PsiElement> psiElements = new ArrayList<PsiElement>();
924 for (RefEntity refElement : refElements) {
925 PsiElement psiElement = refElement instanceof RefElement ? ((RefElement)refElement).getElement() : null;
926 if (psiElement != null && psiElement.isValid()) {
927 psiElements.add(psiElement);
931 return PsiUtilCore.toPsiElementArray(psiElements);
935 public InspectionTree getTree() {
940 public GlobalInspectionContextImpl getGlobalInspectionContext() {
941 return myGlobalInspectionContext;
945 public InspectionRVContentProvider getProvider() {
949 public boolean isSingleToolInSelection() {
950 return myTree != null && myTree.getSelectedToolWrapper() != null;
953 public boolean isRerun() {
954 boolean rerun = myRerun;
959 public boolean isProfileDefined() {
960 return myInspectionProfile != null && myInspectionProfile.isEditable();
963 public static void showPopup(AnActionEvent e, JBPopup popup) {
964 final InputEvent event = e.getInputEvent();
965 if (event instanceof MouseEvent) {
966 popup.showUnderneathOf(event.getComponent());
969 popup.showInBestPositionFor(e.getDataContext());
974 public AnalysisScope getScope() {
978 public boolean isUpdating() {
979 return myUpdatingRequestors > 0;
982 void updateRightPanelLoading() {
983 if (!myDisposed && isUpdating() && myLoadingProgressPreview != null) {
984 myLoadingProgressPreview.updateLoadingProgress();
988 public boolean hasProblems() {
989 return hasProblems(myGlobalInspectionContext.getTools().values(), myGlobalInspectionContext, myProvider);
992 public static boolean hasProblems(@NotNull Collection<Tools> tools,
993 @NotNull GlobalInspectionContextImpl context,
994 @NotNull InspectionRVContentProvider contentProvider) {
995 for (Tools currentTools : tools) {
996 for (ScopeToolState state : contentProvider.getTools(currentTools)) {
997 InspectionToolWrapper toolWrapper = state.getTool();
998 if (contentProvider.checkReportedProblems(context, toolWrapper)) {
1006 public boolean isDisposed() {
1010 private class CloseAction extends AnAction implements DumbAware {
1011 private CloseAction() {
1012 super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel);
1016 public void actionPerformed(AnActionEvent e) {
1017 myGlobalInspectionContext.close(true);
1021 public void updateCurrentProfile() {
1022 final String name = myInspectionProfile.getName();
1023 myInspectionProfile = (InspectionProfile)myInspectionProfile.getProfileManager().getProfile(name);
1026 private class RerunAction extends AnAction {
1027 public RerunAction(JComponent comp) {
1028 super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"),
1029 AllIcons.Actions.Rerun);
1030 registerCustomShortcutSet(CommonShortcuts.getRerun(), comp);
1034 public void update(AnActionEvent e) {
1035 e.getPresentation().setEnabled(myScope.isValid());
1039 public void actionPerformed(AnActionEvent e) {
1043 private void rerun() {
1045 if (myScope.isValid()) {
1046 AnalysisUIOptions.getInstance(myProject).save(myGlobalInspectionContext.getUIOptions());
1047 myGlobalInspectionContext.setTreeState(getTree().getTreeState());
1048 myGlobalInspectionContext.doInspections(myScope);