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.InvokeQuickFixAction;
31 import com.intellij.diff.util.DiffUtil;
32 import com.intellij.icons.AllIcons;
33 import com.intellij.ide.*;
34 import com.intellij.ide.actions.ContextHelpAction;
35 import com.intellij.ide.actions.exclusion.ExclusionHandler;
36 import com.intellij.injected.editor.VirtualFileWindow;
37 import com.intellij.openapi.Disposable;
38 import com.intellij.openapi.actionSystem.*;
39 import com.intellij.openapi.application.Application;
40 import com.intellij.openapi.application.ApplicationManager;
41 import com.intellij.openapi.application.ModalityState;
42 import com.intellij.openapi.diagnostic.Logger;
43 import com.intellij.openapi.editor.*;
44 import com.intellij.openapi.editor.colors.EditorColors;
45 import com.intellij.openapi.editor.ex.EditorEx;
46 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
47 import com.intellij.openapi.project.DumbAware;
48 import com.intellij.openapi.project.Project;
49 import com.intellij.openapi.ui.Splitter;
50 import com.intellij.openapi.ui.popup.JBPopup;
51 import com.intellij.openapi.util.Disposer;
52 import com.intellij.openapi.util.Key;
53 import com.intellij.openapi.util.Pair;
54 import com.intellij.openapi.util.TextRange;
55 import com.intellij.openapi.vfs.VirtualFile;
56 import com.intellij.openapi.wm.ToolWindowId;
57 import com.intellij.openapi.wm.ToolWindowManager;
58 import com.intellij.pom.Navigatable;
59 import com.intellij.profile.Profile;
60 import com.intellij.profile.ProfileChangeAdapter;
61 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
62 import com.intellij.psi.*;
63 import com.intellij.psi.util.PsiUtilCore;
64 import com.intellij.ui.*;
65 import com.intellij.util.ConcurrencyUtil;
66 import com.intellij.util.EditSourceOnDoubleClickHandler;
67 import com.intellij.util.OpenSourceUtil;
68 import com.intellij.util.containers.ContainerUtil;
69 import com.intellij.util.ui.UIUtil;
70 import com.intellij.util.ui.tree.TreeUtil;
71 import org.jetbrains.annotations.NonNls;
72 import org.jetbrains.annotations.NotNull;
73 import org.jetbrains.annotations.Nullable;
76 import javax.swing.event.TreeSelectionEvent;
77 import javax.swing.event.TreeSelectionListener;
78 import javax.swing.tree.DefaultMutableTreeNode;
79 import javax.swing.tree.DefaultTreeModel;
80 import javax.swing.tree.TreePath;
82 import java.awt.event.InputEvent;
83 import java.awt.event.KeyAdapter;
84 import java.awt.event.KeyEvent;
85 import java.awt.event.MouseEvent;
87 import java.util.List;
88 import java.util.concurrent.ConcurrentHashMap;
89 import java.util.concurrent.ConcurrentMap;
94 public class InspectionResultsView extends JPanel implements Disposable, OccurenceNavigator, DataProvider {
95 private static final Logger LOG = Logger.getInstance(InspectionResultsView.class);
97 public static final DataKey<InspectionResultsView> DATA_KEY = DataKey.create("inspectionView");
98 private static final Key<Boolean> PREVIEW_EDITOR_IS_REUSED_KEY = Key.create("inspection.tool.window.preview.editor.is.reused");
100 private final Project myProject;
101 private final InspectionTree myTree;
102 private final ConcurrentMap<HighlightDisplayLevel, ConcurrentMap<String, InspectionGroupNode>> myGroups =
103 ContainerUtil.newConcurrentMap();
104 private final OccurenceNavigator myOccurenceNavigator;
105 private volatile InspectionProfile myInspectionProfile;
107 private final AnalysisScope myScope;
109 private static final String HELP_ID = "reference.toolWindows.inspections";
110 private final ConcurrentMap<HighlightDisplayLevel, InspectionSeverityGroupNode> mySeverityGroupNodes = ContainerUtil.newConcurrentMap();
112 private final Splitter mySplitter;
114 private final GlobalInspectionContextImpl myGlobalInspectionContext;
115 private boolean myRerun;
116 private volatile boolean myDisposed;
117 private int myUpdatingRequestors; //accessed only in edt
118 private boolean myApplyingFix; //accessed only in edt
121 private final InspectionRVContentProvider myProvider;
122 private final ExclusionHandler<InspectionTreeNode> myExclusionHandler;
123 private EditorEx myPreviewEditor;
124 private InspectionTreeLoadingProgressAware myLoadingProgressPreview;
125 private final ExcludedInspectionTreeNodesManager myExcludedInspectionTreeNodesManager;
126 private final Set<Object> mySuppressedNodes = new HashSet<>();
127 private final ConcurrentMap<String, Set<SuppressIntentionAction>> mySuppressActions = new ConcurrentHashMap<>();
129 private final Object myTreeStructureUpdateLock = new Object();
131 public InspectionResultsView(@NotNull GlobalInspectionContextImpl globalInspectionContext,
132 @NotNull InspectionRVContentProvider provider) {
133 setLayout(new BorderLayout());
134 myProject = globalInspectionContext.getProject();
135 myInspectionProfile = globalInspectionContext.getCurrentProfile();
136 myScope = globalInspectionContext.getCurrentScope();
137 myGlobalInspectionContext = globalInspectionContext;
138 myProvider = provider;
139 myExcludedInspectionTreeNodesManager = new ExcludedInspectionTreeNodesManager(provider instanceof OfflineInspectionRVContentProvider,
140 globalInspectionContext.isSingleInspectionRun());
142 myTree = new InspectionTree(myProject, globalInspectionContext, this);
145 myOccurenceNavigator = initOccurenceNavigator();
147 mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION);
148 mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT));
149 mySplitter.setHonorComponentsMinimumSize(false);
151 mySplitter.addPropertyChangeListener(evt -> {
152 if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) {
153 myGlobalInspectionContext.setSplitterProportion(((Float)evt.getNewValue()).floatValue());
156 add(mySplitter, BorderLayout.CENTER);
157 myExclusionHandler = new ExclusionHandler<InspectionTreeNode>() {
159 public boolean isNodeExcluded(@NotNull InspectionTreeNode node) {
160 return node.isExcluded(myExcludedInspectionTreeNodesManager);
164 public void excludeNode(@NotNull InspectionTreeNode node) {
165 node.excludeElement(myExcludedInspectionTreeNodesManager);
166 if (myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS) {
167 InspectionTreeNode parent = (InspectionTreeNode)node.getParent();
168 synchronized (myTreeStructureUpdateLock) {
170 ((DefaultTreeModel)myTree.getModel()).reload(parent);
176 public void includeNode(@NotNull InspectionTreeNode node) {
177 node.amnestyElement(myExcludedInspectionTreeNodesManager);
181 public boolean isActionEnabled(boolean isExcludeAction) {
182 return isExcludeAction || !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS;
186 public void onDone(boolean isExcludeAction) {
187 if (!isExcludeAction || !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS) {
188 myTree.queueUpdate();
193 createActionsToolbar();
194 PsiManager.getInstance(myProject).addPsiTreeChangeListener(new InspectionViewPsiTreeChangeAdapter(this), this);
196 final InspectionProjectProfileManager profileManager = InspectionProjectProfileManager.getInstance(myProject);
197 profileManager.addProfilesListener(new ProfileChangeAdapter() {
199 public void profileChanged(Profile profile) {
200 if (profile == profileManager.getProjectProfileImpl()) {
209 private void initTreeListeners() {
210 myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
212 public void valueChanged(TreeSelectionEvent e) {
213 if (myTree.isUnderQueueUpdate()) return;
215 if (isAutoScrollMode()) {
216 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
221 EditSourceOnDoubleClickHandler.install(myTree);
223 myTree.addKeyListener(new KeyAdapter() {
225 public void keyPressed(KeyEvent e) {
226 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
227 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
232 PopupHandler.installPopupHandler(myTree, IdeActions.INSPECTION_TOOL_WINDOW_TREE_POPUP, ActionPlaces.CODE_INSPECTION);
233 SmartExpander.installOn(myTree);
236 private OccurenceNavigatorSupport initOccurenceNavigator() {
237 return new OccurenceNavigatorSupport(myTree) {
240 protected Navigatable createDescriptorForNode(DefaultMutableTreeNode node) {
241 if (node instanceof InspectionTreeNode && ((InspectionTreeNode)node).isExcluded(myExcludedInspectionTreeNodesManager)) {
244 if (node instanceof RefElementNode) {
245 final RefElementNode refNode = (RefElementNode)node;
246 if (refNode.hasDescriptorsUnder()) return null;
247 final RefEntity element = refNode.getElement();
248 if (element == null || !element.isValid()) return null;
249 final CommonProblemDescriptor problem = refNode.getDescriptor();
250 if (problem != null) {
251 return navigate(problem);
253 if (element instanceof RefElement) {
254 return getOpenFileDescriptor((RefElement)element);
257 else if (node instanceof ProblemDescriptionNode) {
259 if (((ProblemDescriptionNode)node).isValid()) {
260 if (((ProblemDescriptionNode)node).isQuickFixAppliedFromView()) {
261 isValid = ((ProblemDescriptionNode)node).calculateIsValid();
269 ? navigate(((ProblemDescriptionNode)node).getDescriptor())
270 : InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)node);
276 private Navigatable navigate(final CommonProblemDescriptor descriptor) {
277 return getSelectedNavigatable(descriptor);
281 public String getNextOccurenceActionName() {
282 return InspectionsBundle.message("inspection.action.go.next");
286 public String getPreviousOccurenceActionName() {
287 return InspectionsBundle.message("inspection.actiongo.prev");
292 private void createActionsToolbar() {
293 final JComponent leftActionsToolbar = createLeftActionsToolbar();
294 final JComponent rightActionsToolbar = createRightActionsToolbar();
296 JPanel westPanel = new JPanel(new BorderLayout());
297 westPanel.add(leftActionsToolbar, BorderLayout.WEST);
298 westPanel.add(rightActionsToolbar, BorderLayout.EAST);
299 add(westPanel, BorderLayout.WEST);
302 @SuppressWarnings({"NonStaticInitializer"})
303 private JComponent createRightActionsToolbar() {
304 DefaultActionGroup specialGroup = new DefaultActionGroup();
305 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupBySeverityAction(this));
306 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupByDirectoryAction(this));
307 specialGroup.add(myGlobalInspectionContext.getUIOptions().createFilterResolvedItemsAction(this));
308 specialGroup.add(myGlobalInspectionContext.createToggleAutoscrollAction());
309 specialGroup.add(new ExportHTMLAction(this));
310 specialGroup.add(ActionManager.getInstance().getAction("EditInspectionSettings"));
311 specialGroup.add(new InvokeQuickFixAction(this));
312 return createToolbar(specialGroup);
315 private JComponent createLeftActionsToolbar() {
316 final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
317 DefaultActionGroup group = new DefaultActionGroup();
318 group.add(new RerunAction(this));
319 group.add(new CloseAction());
320 final TreeExpander treeExpander = new TreeExpander() {
322 public void expandAll() {
323 TreeUtil.expandAll(myTree);
327 public boolean canExpand() {
332 public void collapseAll() {
333 TreeUtil.collapseAll(myTree, 0);
337 public boolean canCollapse() {
341 group.add(actionsManager.createExpandAllAction(treeExpander, myTree));
342 group.add(actionsManager.createCollapseAllAction(treeExpander, myTree));
343 group.add(actionsManager.createPrevOccurenceAction(getOccurenceNavigator()));
344 group.add(actionsManager.createNextOccurenceAction(getOccurenceNavigator()));
345 group.add(new ContextHelpAction(HELP_ID));
347 return createToolbar(group);
350 private static JComponent createToolbar(final DefaultActionGroup specialGroup) {
351 return ActionManager.getInstance().createActionToolbar(ActionPlaces.CODE_INSPECTION, specialGroup, false).getComponent();
355 public void dispose() {
356 InspectionResultsViewUtil.releaseEditor(myPreviewEditor);
357 mySplitter.dispose();
358 myInspectionProfile = null;
360 if (myLoadingProgressPreview != null) {
361 Disposer.dispose(myLoadingProgressPreview);
362 myLoadingProgressPreview = null;
367 private boolean isAutoScrollMode() {
368 String activeToolWindowId = ToolWindowManager.getInstance(myProject).getActiveToolWindowId();
369 return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE &&
370 (activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION));
374 private static OpenFileDescriptor getOpenFileDescriptor(final RefElement refElement) {
375 final VirtualFile[] file = new VirtualFile[1];
376 final int[] offset = new int[1];
378 ApplicationManager.getApplication().runReadAction(() -> {
379 PsiElement psiElement = refElement.getElement();
380 if (psiElement != null) {
381 final PsiFile containingFile = psiElement.getContainingFile();
382 if (containingFile != null) {
383 file[0] = containingFile.getVirtualFile();
384 offset[0] = psiElement.getTextOffset();
392 if (file[0] != null && file[0].isValid()) {
393 return new OpenFileDescriptor(refElement.getRefManager().getProject(), file[0], offset[0]);
398 public void setApplyingFix(boolean applyingFix) {
399 myApplyingFix = applyingFix;
403 public void openRightPanelIfNeed() {
404 if (mySplitter.getSecondComponent() == null) {
409 public void syncRightPanel() {
410 final Editor oldEditor = myPreviewEditor;
411 if (myLoadingProgressPreview != null) {
412 Disposer.dispose(myLoadingProgressPreview);
413 myLoadingProgressPreview = null;
416 final InspectionToolWrapper wrapper = myTree.getSelectedToolWrapper();
417 LOG.assertTrue(wrapper != null);
418 mySplitter.setSecondComponent(InspectionResultsViewUtil.getApplyingFixLabel(wrapper));
420 if (myTree.getSelectionModel().getSelectionCount() != 1) {
421 if (myTree.getSelectedToolWrapper() == null) {
422 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
425 showInRightPanel(myTree.getCommonSelectedElement());
429 TreePath pathSelected = myTree.getSelectionModel().getLeadSelectionPath();
430 if (pathSelected != null) {
431 final InspectionTreeNode node = (InspectionTreeNode)pathSelected.getLastPathComponent();
432 if (node instanceof ProblemDescriptionNode) {
433 final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
434 showInRightPanel(problemNode.getElement());
436 else if (node instanceof InspectionPackageNode ||
437 node instanceof InspectionModuleNode ||
438 node instanceof RefElementNode) {
439 showInRightPanel(node.getContainingFileLocalEntity());
441 else if (node instanceof InspectionNode) {
442 if (myGlobalInspectionContext.getPresentation(((InspectionNode)node).getToolWrapper()).isDummy()) {
443 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
446 showInRightPanel(null);
449 else if (node instanceof InspectionGroupNode || node instanceof InspectionSeverityGroupNode) {
450 final InspectionViewNavigationPanel panel = new InspectionViewNavigationPanel(node, myTree);
451 myLoadingProgressPreview = panel;
452 mySplitter.setSecondComponent(panel);
455 LOG.error("Unexpected node: " + node.getClass());
460 if (oldEditor != null) {
461 if (Boolean.TRUE.equals(oldEditor.getUserData(PREVIEW_EDITOR_IS_REUSED_KEY))) {
462 oldEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, null);
465 InspectionResultsViewUtil.releaseEditor(oldEditor);
466 if (oldEditor == myPreviewEditor) {
467 myPreviewEditor = null;
473 private void showInRightPanel(@Nullable final RefEntity refEntity) {
474 Cursor currentCursor = getCursor();
476 setCursor(new Cursor(Cursor.WAIT_CURSOR));
477 final JPanel editorPanel = new JPanel();
478 editorPanel.setLayout(new BorderLayout());
479 final int problemCount = myTree.getSelectedProblemCount();
480 JComponent previewPanel = null;
481 final InspectionToolWrapper tool = myTree.getSelectedToolWrapper();
482 if (tool != null && refEntity != null && refEntity.isValid()) {
483 final InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(tool);
484 previewPanel = presentation.getCustomPreviewPanel(refEntity);
486 EditorEx previewEditor = null;
487 if (previewPanel == null) {
488 final Pair<JComponent, EditorEx> panelAndEditor = createBaseRightComponentFor(problemCount, refEntity);
489 previewPanel = panelAndEditor.getFirst();
490 previewEditor = panelAndEditor.getSecond();
492 editorPanel.add(previewPanel, BorderLayout.CENTER);
493 if (problemCount > 0) {
494 final JComponent fixToolbar = QuickFixPreviewPanelFactory.create(previewEditor, this);
495 if (fixToolbar != null) {
496 if (fixToolbar instanceof InspectionTreeLoadingProgressAware) {
497 myLoadingProgressPreview = (InspectionTreeLoadingProgressAware)fixToolbar;
499 if (previewEditor != null) {
500 previewPanel.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP));
502 editorPanel.add(fixToolbar, BorderLayout.NORTH);
505 mySplitter.setSecondComponent(editorPanel);
508 setCursor(currentCursor);
512 private Pair<JComponent, EditorEx> createBaseRightComponentFor(int problemCount, RefEntity selectedEntity) {
513 if (selectedEntity instanceof RefElement &&
514 selectedEntity.isValid() &&
515 !(((RefElement)selectedEntity).getElement() instanceof PsiDirectory)) {
516 PsiElement selectedElement = ((RefElement)selectedEntity).getElement();
517 if (problemCount == 1) {
518 CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors();
519 if (descriptors.length != 0) {
520 final CommonProblemDescriptor descriptor = descriptors[0];
521 if (descriptor instanceof ProblemDescriptorBase) {
522 final PsiElement element = ((ProblemDescriptorBase)descriptor).getPsiElement();
523 if (element != null) {
524 selectedElement = element;
529 final PsiFile file = selectedElement.getContainingFile();
530 final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
531 if (document == null) {
532 return Pair.create(InspectionResultsViewUtil.createLabelForText("Can't open preview for \'" + file.getName() + "\'"), null);
535 if (reuseEditorFor(document)) {
536 myPreviewEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, true);
537 myPreviewEditor.getFoldingModel().runBatchFoldingOperation(() -> {
538 myPreviewEditor.getFoldingModel().clearFoldRegions();
539 myPreviewEditor.getMarkupModel().removeAllHighlighters();
543 myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true);
544 DiffUtil.setFoldingModelSupport(myPreviewEditor);
545 final EditorSettings settings = myPreviewEditor.getSettings();
546 settings.setLineNumbersShown(false);
547 settings.setLineMarkerAreaShown(false);
548 settings.setAdditionalColumnsCount(0);
549 settings.setAdditionalLinesCount(0);
550 settings.setLeadingWhitespaceShown(true);
551 myPreviewEditor.getColorsScheme().setColor(EditorColors.GUTTER_BACKGROUND, myPreviewEditor.getColorsScheme().getDefaultBackground());
552 myPreviewEditor.getScrollPane().setBorder(IdeBorderFactory.createEmptyBorder());
554 if (problemCount == 0) {
555 myPreviewEditor.getScrollingModel().scrollTo(myPreviewEditor.offsetToLogicalPosition(selectedElement.getTextOffset()), ScrollType.CENTER_UP);
557 myPreviewEditor.getSettings().setFoldingOutlineShown(problemCount > 1);
558 myPreviewEditor.getComponent().setBorder(IdeBorderFactory.createEmptyBorder());
559 return Pair.create(myPreviewEditor.getComponent(), myPreviewEditor);
561 else if (selectedEntity == null) {
562 return Pair.create(new InspectionNodeInfo(myTree, myProject), null);
564 else if (selectedEntity.isValid()) {
565 return Pair.create(InspectionResultsViewUtil.getPreviewIsNotAvailable(selectedEntity), null);
567 return Pair.create(InspectionResultsViewUtil.getInvalidEntityLabel(selectedEntity), null);
570 private boolean reuseEditorFor(Document document) {
571 return myPreviewEditor != null && !myPreviewEditor.isDisposed() && myPreviewEditor.getDocument() == document;
575 public InspectionNode addTool(@NotNull final InspectionToolWrapper toolWrapper,
576 HighlightDisplayLevel errorLevel,
577 boolean groupedBySeverity,
578 boolean isSingleInspectionRun) {
580 toolWrapper.getGroupDisplayName().isEmpty() ? InspectionProfileEntry.GENERAL_GROUP_NAME : toolWrapper.getGroupDisplayName();
581 InspectionTreeNode parentNode = getToolParentNode(groupName, toolWrapper.getGroupPath(), errorLevel, groupedBySeverity, isSingleInspectionRun);
582 InspectionNode toolNode = new InspectionNode(toolWrapper, myInspectionProfile);
583 boolean showStructure = myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE;
584 myProvider.appendToolNodeContent(myGlobalInspectionContext, toolNode, parentNode, showStructure);
585 InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(toolWrapper);
586 toolNode = presentation.createToolNode(myGlobalInspectionContext, toolNode, myProvider, parentNode, showStructure);
587 synchronized (getTreeStructureUpdateLock()) {
588 ((DefaultInspectionToolPresentation)presentation).setToolNode(toolNode);
590 registerActionShortcuts(presentation);
594 private void registerActionShortcuts(@NotNull InspectionToolPresentation presentation) {
595 ApplicationManager.getApplication().invokeLater(() -> {
596 final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY, null);
598 for (QuickFixAction fix : fixes) {
599 fix.registerCustomShortcutSet(fix.getShortcutSet(), this);
606 public Set<SuppressIntentionAction> getSuppressActions(InspectionToolWrapper wrapper) {
607 return mySuppressActions.computeIfAbsent(wrapper.getShortName(), (w) -> {
608 final SuppressIntentionAction[] actions = InspectionManagerEx.getSuppressActions(wrapper);
609 return actions == null ? Collections.emptySet() : ContainerUtil.newLinkedHashSet(actions);
613 public Set<Object> getSuppressedNodes() {
614 return mySuppressedNodes;
618 public ExcludedInspectionTreeNodesManager getExcludedManager() {
619 return myExcludedInspectionTreeNodesManager;
623 public String getCurrentProfileName() {
624 return myInspectionProfile == null ? null : myInspectionProfile.getDisplayName();
627 public InspectionProfile getCurrentProfile() {
628 return myInspectionProfile;
631 public void update() {
632 ApplicationManager.getApplication().assertIsDispatchThread();
633 myTree.removeAllNodes();
634 mySeverityGroupNodes.clear();
638 public void setUpdating(boolean isUpdating) {
639 final Runnable update = () -> {
641 myUpdatingRequestors++;
643 myUpdatingRequestors--;
645 boolean hasUpdatingRequestors = myUpdatingRequestors > 0;
646 myTree.setPaintBusy(hasUpdatingRequestors);
647 if (!hasUpdatingRequestors && myLoadingProgressPreview != null) {
648 myLoadingProgressPreview.treeLoaded();
650 //TODO Dmitrii Batkovich it's a hack (preview panel should be created during selection update)
651 if (!hasUpdatingRequestors && mySplitter.getSecondComponent() == null) {
652 if (myTree.getSelectionModel().getSelectionPath() == null) {
653 TreeUtil.selectFirstNode(myTree);
658 final Application app = ApplicationManager.getApplication();
659 if (app.isDispatchThread()) {
663 app.invokeLater(update, ModalityState.any());
667 public Object getTreeStructureUpdateLock() {
668 return myTreeStructureUpdateLock;
671 public void addTools(Collection<Tools> tools) {
672 InspectionProfileImpl profile = (InspectionProfileImpl)myInspectionProfile;
673 boolean isGroupedBySeverity = myGlobalInspectionContext.getUIOptions().GROUP_BY_SEVERITY;
674 boolean singleInspectionRun = myGlobalInspectionContext.isSingleInspectionRun();
675 for (Tools currentTools : tools) {
676 InspectionToolWrapper defaultToolWrapper = currentTools.getDefaultState().getTool();
677 if (myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS && myExcludedInspectionTreeNodesManager.containsInspectionNode(defaultToolWrapper)) {
680 final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
681 for (ScopeToolState state : myProvider.getTools(currentTools)) {
682 InspectionToolWrapper toolWrapper = state.getTool();
683 if (myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper)) {
685 profile.getErrorLevel(key, state.getScope(myProject), myProject),
687 singleInspectionRun);
693 public void buildTree() {
694 final Application app = ApplicationManager.getApplication();
695 final Runnable buildAction = () -> {
698 synchronized (getTreeStructureUpdateLock()) {
700 addTools(myGlobalInspectionContext.getTools().values());
705 UIUtil.invokeLaterIfNeeded(() -> myTree.restoreExpansionAndSelection(null));
708 if (app.isUnitTestMode()) {
711 app.executeOnPooledThread(() -> app.runReadAction(buildAction));
717 private InspectionTreeNode getToolParentNode(@NotNull String groupName,
718 @NotNull String[] groupPath,
719 HighlightDisplayLevel errorLevel,
720 boolean groupedBySeverity,
721 boolean isSingleInspectionRun) {
722 if (!groupedBySeverity && isSingleInspectionRun) {
723 return getTree().getRoot();
725 if (groupName.isEmpty()) {
726 return getRelativeRootNode(groupedBySeverity, errorLevel);
728 ConcurrentMap<String, InspectionGroupNode> map = myGroups.get(errorLevel);
730 map = ConcurrencyUtil.cacheOrGet(myGroups, errorLevel, ContainerUtil.newConcurrentMap());
732 InspectionGroupNode group;
733 if (groupedBySeverity) {
734 group = map.get(groupName);
738 for (Map<String, InspectionGroupNode> groupMap : myGroups.values()) {
739 if ((group = groupMap.get(groupName)) != null) break;
743 if (isSingleInspectionRun) {
744 return getRelativeRootNode(true, errorLevel);
746 group = ConcurrencyUtil.cacheOrGet(map, groupName, new InspectionGroupNode(groupName, groupPath));
748 getRelativeRootNode(groupedBySeverity, errorLevel).insertByOrder(group);
755 private InspectionTreeNode getRelativeRootNode(boolean isGroupedBySeverity, HighlightDisplayLevel level) {
756 if (isGroupedBySeverity) {
757 InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level);
758 if (severityGroupNode == null) {
759 InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myProject, level);
760 severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode);
761 if (severityGroupNode == newNode) {
762 InspectionTreeNode root = myTree.getRoot();
763 root.insertByOrder(severityGroupNode);
766 return severityGroupNode;
768 return myTree.getRoot();
771 private OccurenceNavigator getOccurenceNavigator() {
772 return myOccurenceNavigator;
776 public boolean hasNextOccurence() {
777 return myOccurenceNavigator != null && myOccurenceNavigator.hasNextOccurence();
781 public boolean hasPreviousOccurence() {
782 return myOccurenceNavigator != null && myOccurenceNavigator.hasPreviousOccurence();
786 public OccurenceInfo goNextOccurence() {
787 return myOccurenceNavigator != null ? myOccurenceNavigator.goNextOccurence() : null;
791 public OccurenceInfo goPreviousOccurence() {
792 return myOccurenceNavigator != null ? myOccurenceNavigator.goPreviousOccurence() : null;
796 public String getNextOccurenceActionName() {
797 return myOccurenceNavigator != null ? myOccurenceNavigator.getNextOccurenceActionName() : "";
801 public String getPreviousOccurenceActionName() {
802 return myOccurenceNavigator != null ? myOccurenceNavigator.getPreviousOccurenceActionName() : "";
806 public Project getProject() {
811 public Object getData(String dataId) {
812 if (PlatformDataKeys.HELP_ID.is(dataId)) return HELP_ID;
813 if (DATA_KEY.is(dataId)) return this;
814 if (ExclusionHandler.EXCLUSION_HANDLER.is(dataId)) return myExclusionHandler;
815 if (myTree == null) return null;
816 TreePath[] paths = myTree.getSelectionPaths();
818 if (paths == null || paths.length == 0) return null;
820 if (paths.length > 1) {
821 if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
822 return collectPsiElements();
827 TreePath path = paths[0];
829 InspectionTreeNode selectedNode = (InspectionTreeNode)path.getLastPathComponent();
831 if (!CommonDataKeys.NAVIGATABLE.is(dataId) && !CommonDataKeys.PSI_ELEMENT.is(dataId)) {
835 if (selectedNode instanceof RefElementNode) {
836 final RefElementNode refElementNode = (RefElementNode)selectedNode;
837 RefEntity refElement = refElementNode.getElement();
838 if (refElement == null) return null;
839 final RefEntity item = refElement.getRefManager().getRefinedElement(refElement);
841 if (!item.isValid()) return null;
843 PsiElement psiElement = item instanceof RefElement ? ((RefElement)item).getElement() : null;
844 if (psiElement == null) return null;
846 final CommonProblemDescriptor problem = refElementNode.getDescriptor();
847 if (problem != null) {
848 if (problem instanceof ProblemDescriptor) {
849 PsiElement elementFromDescriptor = ((ProblemDescriptor)problem).getPsiElement();
850 if (elementFromDescriptor == null) {
851 final InspectionTreeNode node = (InspectionTreeNode)refElementNode.getChildAt(0);
852 if (node.isValid()) {
856 psiElement = elementFromDescriptor;
864 if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
865 return getSelectedNavigatable(problem, psiElement);
867 else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
868 return psiElement.isValid() ? psiElement : null;
871 else if (selectedNode instanceof ProblemDescriptionNode && CommonDataKeys.NAVIGATABLE.is(dataId)) {
872 Navigatable navigatable = getSelectedNavigatable(((ProblemDescriptionNode)selectedNode).getDescriptor());
873 return navigatable == null ? InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)selectedNode) : navigatable;
880 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor) {
881 return getSelectedNavigatable(descriptor,
882 descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getPsiElement() : null);
886 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor, final PsiElement psiElement) {
887 if (descriptor instanceof ProblemDescriptorBase) {
888 Navigatable navigatable = ((ProblemDescriptorBase)descriptor).getNavigatable();
889 if (navigatable != null) {
893 if (psiElement == null || !psiElement.isValid()) return null;
894 PsiFile containingFile = psiElement.getContainingFile();
895 VirtualFile virtualFile = containingFile == null ? null : containingFile.getVirtualFile();
897 if (virtualFile != null) {
898 int startOffset = psiElement.getTextOffset();
899 if (descriptor instanceof ProblemDescriptorBase) {
900 final TextRange textRange = ((ProblemDescriptorBase)descriptor).getTextRangeForNavigation();
901 if (textRange != null) {
902 if (virtualFile instanceof VirtualFileWindow) {
903 virtualFile = ((VirtualFileWindow)virtualFile).getDelegate();
905 startOffset = textRange.getStartOffset();
908 return new OpenFileDescriptor(myProject, virtualFile, startOffset);
913 private PsiElement[] collectPsiElements() {
914 RefEntity[] refElements = myTree.getSelectedElements();
915 List<PsiElement> psiElements = new ArrayList<PsiElement>();
916 for (RefEntity refElement : refElements) {
917 PsiElement psiElement = refElement instanceof RefElement ? ((RefElement)refElement).getElement() : null;
918 if (psiElement != null && psiElement.isValid()) {
919 psiElements.add(psiElement);
923 return PsiUtilCore.toPsiElementArray(psiElements);
927 public InspectionTree getTree() {
932 public GlobalInspectionContextImpl getGlobalInspectionContext() {
933 return myGlobalInspectionContext;
937 public InspectionRVContentProvider getProvider() {
941 public boolean isSingleToolInSelection() {
942 return myTree != null && myTree.getSelectedToolWrapper() != null;
945 public boolean isRerun() {
946 boolean rerun = myRerun;
951 public boolean isProfileDefined() {
952 return myInspectionProfile != null && myInspectionProfile.isEditable();
955 public static void showPopup(AnActionEvent e, JBPopup popup) {
956 final InputEvent event = e.getInputEvent();
957 if (event instanceof MouseEvent) {
958 popup.showUnderneathOf(event.getComponent());
961 popup.showInBestPositionFor(e.getDataContext());
966 public AnalysisScope getScope() {
970 public boolean isUpdating() {
971 return myUpdatingRequestors > 0;
974 void updateRightPanelLoading() {
975 if (!myDisposed && isUpdating() && myLoadingProgressPreview != null) {
976 myLoadingProgressPreview.updateLoadingProgress();
980 public boolean hasProblems() {
981 return hasProblems(myGlobalInspectionContext.getTools().values(), myGlobalInspectionContext, myProvider);
984 public static boolean hasProblems(@NotNull Collection<Tools> tools,
985 @NotNull GlobalInspectionContextImpl context,
986 @NotNull InspectionRVContentProvider contentProvider) {
987 for (Tools currentTools : tools) {
988 for (ScopeToolState state : contentProvider.getTools(currentTools)) {
989 InspectionToolWrapper toolWrapper = state.getTool();
990 if (contentProvider.checkReportedProblems(context, toolWrapper)) {
998 public boolean isDisposed() {
1002 private class CloseAction extends AnAction implements DumbAware {
1003 private CloseAction() {
1004 super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel);
1008 public void actionPerformed(AnActionEvent e) {
1009 myGlobalInspectionContext.close(true);
1013 public void updateCurrentProfile() {
1014 final String name = myInspectionProfile.getName();
1015 myInspectionProfile = (InspectionProfile)myInspectionProfile.getProfileManager().getProfile(name);
1018 private class RerunAction extends AnAction {
1019 public RerunAction(JComponent comp) {
1020 super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"),
1021 AllIcons.Actions.Rerun);
1022 registerCustomShortcutSet(CommonShortcuts.getRerun(), comp);
1026 public void update(AnActionEvent e) {
1027 e.getPresentation().setEnabled(myScope.isValid());
1031 public void actionPerformed(AnActionEvent e) {
1035 private void rerun() {
1037 if (myScope.isValid()) {
1038 AnalysisUIOptions.getInstance(myProject).save(myGlobalInspectionContext.getUIOptions());
1039 myGlobalInspectionContext.setTreeState(getTree().getTreeState());
1040 myGlobalInspectionContext.doInspections(myScope);