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.psi.*;
61 import com.intellij.psi.util.PsiUtilCore;
62 import com.intellij.ui.*;
63 import com.intellij.util.ConcurrencyUtil;
64 import com.intellij.util.EditSourceOnDoubleClickHandler;
65 import com.intellij.util.OpenSourceUtil;
66 import com.intellij.util.containers.ContainerUtil;
67 import com.intellij.util.ui.UIUtil;
68 import com.intellij.util.ui.tree.TreeUtil;
69 import org.jetbrains.annotations.NonNls;
70 import org.jetbrains.annotations.NotNull;
71 import org.jetbrains.annotations.Nullable;
74 import javax.swing.event.TreeSelectionEvent;
75 import javax.swing.event.TreeSelectionListener;
76 import javax.swing.tree.DefaultMutableTreeNode;
77 import javax.swing.tree.DefaultTreeModel;
78 import javax.swing.tree.TreePath;
80 import java.awt.event.InputEvent;
81 import java.awt.event.KeyAdapter;
82 import java.awt.event.KeyEvent;
83 import java.awt.event.MouseEvent;
85 import java.util.List;
86 import java.util.concurrent.ConcurrentHashMap;
87 import java.util.concurrent.ConcurrentMap;
89 import static com.intellij.codeInspection.ex.InspectionRVContentProvider.insertByIndex;
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 = false; //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<InspectionToolWrapper, 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);
141 myTree = new InspectionTree(myProject, globalInspectionContext, this);
144 myOccurenceNavigator = initOccurenceNavigator();
146 mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION);
147 mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT));
148 mySplitter.setHonorComponentsMinimumSize(false);
150 mySplitter.addPropertyChangeListener(evt -> {
151 if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) {
152 myGlobalInspectionContext.setSplitterProportion(((Float)evt.getNewValue()).floatValue());
155 add(mySplitter, BorderLayout.CENTER);
156 myExclusionHandler = new ExclusionHandler<InspectionTreeNode>() {
158 public boolean isNodeExcluded(@NotNull InspectionTreeNode node) {
159 return node.isExcluded(myExcludedInspectionTreeNodesManager);
163 public void excludeNode(@NotNull InspectionTreeNode node) {
164 node.excludeElement(myExcludedInspectionTreeNodesManager);
165 if (myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS) {
166 InspectionTreeNode parent = (InspectionTreeNode)node.getParent();
167 synchronized (myTreeStructureUpdateLock) {
169 ((DefaultTreeModel)myTree.getModel()).reload(parent);
171 TreeUtil.selectInTree(parent, true, myTree);
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);
197 private void initTreeListeners() {
198 myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
200 public void valueChanged(TreeSelectionEvent e) {
201 if (myTree.isUnderQueueUpdate()) return;
203 if (isAutoScrollMode()) {
204 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
209 EditSourceOnDoubleClickHandler.install(myTree);
211 myTree.addKeyListener(new KeyAdapter() {
213 public void keyPressed(KeyEvent e) {
214 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
215 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
220 PopupHandler.installPopupHandler(myTree, IdeActions.INSPECTION_TOOL_WINDOW_TREE_POPUP, ActionPlaces.CODE_INSPECTION);
221 SmartExpander.installOn(myTree);
224 private OccurenceNavigatorSupport initOccurenceNavigator() {
225 return new OccurenceNavigatorSupport(myTree) {
228 protected Navigatable createDescriptorForNode(DefaultMutableTreeNode node) {
229 if (node instanceof InspectionTreeNode && ((InspectionTreeNode)node).isExcluded(myExcludedInspectionTreeNodesManager)) {
232 if (node instanceof RefElementNode) {
233 final RefElementNode refNode = (RefElementNode)node;
234 if (refNode.hasDescriptorsUnder()) return null;
235 final RefEntity element = refNode.getElement();
236 if (element == null || !element.isValid()) return null;
237 final CommonProblemDescriptor problem = refNode.getDescriptor();
238 if (problem != null) {
239 return navigate(problem);
241 if (element instanceof RefElement) {
242 return getOpenFileDescriptor((RefElement)element);
245 else if (node instanceof ProblemDescriptionNode) {
247 if (((ProblemDescriptionNode)node).isValid()) {
248 if (((ProblemDescriptionNode)node).isQuickFixAppliedFromView()) {
249 isValid = ((ProblemDescriptionNode)node).calculateIsValid();
257 ? navigate(((ProblemDescriptionNode)node).getDescriptor())
258 : InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)node);
264 private Navigatable navigate(final CommonProblemDescriptor descriptor) {
265 return getSelectedNavigatable(descriptor);
269 public String getNextOccurenceActionName() {
270 return InspectionsBundle.message("inspection.action.go.next");
274 public String getPreviousOccurenceActionName() {
275 return InspectionsBundle.message("inspection.actiongo.prev");
280 private void createActionsToolbar() {
281 final JComponent leftActionsToolbar = createLeftActionsToolbar();
282 final JComponent rightActionsToolbar = createRightActionsToolbar();
284 JPanel westPanel = new JPanel(new BorderLayout());
285 westPanel.add(leftActionsToolbar, BorderLayout.WEST);
286 westPanel.add(rightActionsToolbar, BorderLayout.EAST);
287 add(westPanel, BorderLayout.WEST);
290 @SuppressWarnings({"NonStaticInitializer"})
291 private JComponent createRightActionsToolbar() {
292 DefaultActionGroup specialGroup = new DefaultActionGroup();
293 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupBySeverityAction(this));
294 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupByDirectoryAction(this));
295 specialGroup.add(myGlobalInspectionContext.getUIOptions().createFilterResolvedItemsAction(this));
296 specialGroup.add(ActionManager.getInstance().getAction("EditInspectionSettings"));
297 specialGroup.add(new InvokeQuickFixAction(this));
298 specialGroup.add(new InspectionsOptionsToolbarAction(this));
299 return createToolbar(specialGroup);
302 private JComponent createLeftActionsToolbar() {
303 final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
304 DefaultActionGroup group = new DefaultActionGroup();
305 group.add(new RerunAction(this));
306 group.add(new CloseAction());
307 final TreeExpander treeExpander = new TreeExpander() {
309 public void expandAll() {
310 TreeUtil.expandAll(myTree);
314 public boolean canExpand() {
319 public void collapseAll() {
320 TreeUtil.collapseAll(myTree, 0);
324 public boolean canCollapse() {
328 group.add(actionsManager.createExpandAllAction(treeExpander, myTree));
329 group.add(actionsManager.createCollapseAllAction(treeExpander, myTree));
330 group.add(actionsManager.createPrevOccurenceAction(getOccurenceNavigator()));
331 group.add(actionsManager.createNextOccurenceAction(getOccurenceNavigator()));
332 group.add(myGlobalInspectionContext.createToggleAutoscrollAction());
333 group.add(new ExportHTMLAction(this));
334 group.add(new ContextHelpAction(HELP_ID));
336 return createToolbar(group);
339 private static JComponent createToolbar(final DefaultActionGroup specialGroup) {
340 return ActionManager.getInstance().createActionToolbar(ActionPlaces.CODE_INSPECTION, specialGroup, false).getComponent();
344 public void dispose() {
345 InspectionResultsViewUtil.releaseEditor(myPreviewEditor);
346 mySplitter.dispose();
347 myInspectionProfile = null;
349 if (myLoadingProgressPreview != null) {
350 Disposer.dispose(myLoadingProgressPreview);
351 myLoadingProgressPreview = null;
356 private boolean isAutoScrollMode() {
357 String activeToolWindowId = ToolWindowManager.getInstance(myProject).getActiveToolWindowId();
358 return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE &&
359 (activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION));
363 private static OpenFileDescriptor getOpenFileDescriptor(final RefElement refElement) {
364 final VirtualFile[] file = new VirtualFile[1];
365 final int[] offset = new int[1];
367 ApplicationManager.getApplication().runReadAction(new Runnable() {
370 PsiElement psiElement = refElement.getElement();
371 if (psiElement != null) {
372 final PsiFile containingFile = psiElement.getContainingFile();
373 if (containingFile != null) {
374 file[0] = containingFile.getVirtualFile();
375 offset[0] = psiElement.getTextOffset();
384 if (file[0] != null && file[0].isValid()) {
385 return new OpenFileDescriptor(refElement.getRefManager().getProject(), file[0], offset[0]);
390 public void setApplyingFix(boolean applyingFix) {
391 myApplyingFix = applyingFix;
395 public void openRightPanelIfNeed() {
396 if (mySplitter.getSecondComponent() == null) {
401 public void syncRightPanel() {
402 final Editor oldEditor = myPreviewEditor;
403 if (myLoadingProgressPreview != null) {
404 Disposer.dispose(myLoadingProgressPreview);
405 myLoadingProgressPreview = null;
408 final InspectionToolWrapper wrapper = myTree.getSelectedToolWrapper();
409 LOG.assertTrue(wrapper != null);
410 mySplitter.setSecondComponent(InspectionResultsViewUtil.getApplyingFixLabel(wrapper));
412 if (myTree.getSelectionModel().getSelectionCount() != 1) {
413 if (myTree.getSelectedToolWrapper() == null) {
414 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
417 showInRightPanel(myTree.getCommonSelectedElement());
421 TreePath pathSelected = myTree.getSelectionModel().getLeadSelectionPath();
422 if (pathSelected != null) {
423 final InspectionTreeNode node = (InspectionTreeNode)pathSelected.getLastPathComponent();
424 if (node instanceof ProblemDescriptionNode) {
425 final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
426 showInRightPanel(problemNode.getElement());
428 else if (node instanceof InspectionPackageNode ||
429 node instanceof InspectionModuleNode ||
430 node instanceof RefElementNode) {
431 showInRightPanel(node.getContainingFileLocalEntity());
433 else if (node instanceof InspectionNode) {
434 if (myGlobalInspectionContext.getPresentation(((InspectionNode)node).getToolWrapper()).isDummy()) {
435 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
438 showInRightPanel(null);
441 else if (node instanceof InspectionRootNode || node instanceof InspectionGroupNode || node instanceof InspectionSeverityGroupNode) {
442 final InspectionViewNavigationPanel panel = new InspectionViewNavigationPanel(node, myTree);
443 myLoadingProgressPreview = panel;
444 mySplitter.setSecondComponent(panel);
447 LOG.error("Unexpected node: " + node.getClass());
452 if (oldEditor != null) {
453 if (Boolean.TRUE.equals(oldEditor.getUserData(PREVIEW_EDITOR_IS_REUSED_KEY))) {
454 oldEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, null);
457 InspectionResultsViewUtil.releaseEditor(oldEditor);
458 if (oldEditor == myPreviewEditor) {
459 myPreviewEditor = null;
465 private void showInRightPanel(@Nullable final RefEntity refEntity) {
466 Cursor currentCursor = getCursor();
468 setCursor(new Cursor(Cursor.WAIT_CURSOR));
469 final JPanel editorPanel = new JPanel();
470 editorPanel.setLayout(new BorderLayout());
471 final int problemCount = myTree.getSelectedProblemCount();
472 JComponent previewPanel = null;
473 final InspectionToolWrapper tool = myTree.getSelectedToolWrapper();
474 if (tool != null && refEntity != null && problemCount == 1) {
475 final InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(tool);
476 previewPanel = presentation.getCustomPreviewPanel(refEntity);
478 EditorEx previewEditor = null;
479 if (previewPanel == null) {
480 final Pair<JComponent, EditorEx> panelAndEditor = createBaseRightComponentFor(problemCount, refEntity);
481 previewPanel = panelAndEditor.getFirst();
482 previewEditor = panelAndEditor.getSecond();
484 editorPanel.add(previewPanel, BorderLayout.CENTER);
485 if (problemCount > 0) {
486 final JComponent fixToolbar = QuickFixPreviewPanelFactory.create(previewEditor, this);
487 if (fixToolbar != null) {
488 if (fixToolbar instanceof InspectionTreeLoadingProgressAware) {
489 myLoadingProgressPreview = (InspectionTreeLoadingProgressAware)fixToolbar;
491 if (previewEditor != null) {
492 previewPanel.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP));
494 editorPanel.add(fixToolbar, BorderLayout.NORTH);
497 mySplitter.setSecondComponent(editorPanel);
500 setCursor(currentCursor);
504 private Pair<JComponent, EditorEx> createBaseRightComponentFor(int problemCount, RefEntity selectedEntity) {
505 if (selectedEntity instanceof RefElement &&
506 selectedEntity.isValid() &&
507 !(((RefElement)selectedEntity).getElement() instanceof PsiDirectory)) {
508 PsiElement selectedElement = ((RefElement)selectedEntity).getElement();
509 if (problemCount == 1) {
510 CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors();
511 if (descriptors.length != 0) {
512 final CommonProblemDescriptor descriptor = descriptors[0];
513 if (descriptor instanceof ProblemDescriptorBase) {
514 final PsiElement element = ((ProblemDescriptorBase)descriptor).getPsiElement();
515 if (element != null) {
516 selectedElement = element;
521 final PsiFile file = selectedElement.getContainingFile();
522 final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
523 if (document == null) {
524 return Pair.create(InspectionResultsViewUtil.createLabelForText("Can't open preview for \'" + file.getName() + "\'"), null);
527 if (reuseEditorFor(document)) {
528 myPreviewEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, true);
529 myPreviewEditor.getFoldingModel().runBatchFoldingOperation(() -> {
530 myPreviewEditor.getFoldingModel().clearFoldRegions();
531 myPreviewEditor.getMarkupModel().removeAllHighlighters();
535 myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true);
536 DiffUtil.setFoldingModelSupport(myPreviewEditor);
537 final EditorSettings settings = myPreviewEditor.getSettings();
538 settings.setLineNumbersShown(false);
539 settings.setLineMarkerAreaShown(false);
540 settings.setAdditionalColumnsCount(0);
541 settings.setAdditionalLinesCount(0);
542 settings.setLeadingWhitespaceShown(true);
543 myPreviewEditor.getColorsScheme().setColor(EditorColors.GUTTER_BACKGROUND, myPreviewEditor.getColorsScheme().getDefaultBackground());
544 myPreviewEditor.getScrollPane().setBorder(IdeBorderFactory.createEmptyBorder());
546 if (problemCount == 0) {
547 myPreviewEditor.getScrollingModel().scrollTo(myPreviewEditor.offsetToLogicalPosition(selectedElement.getTextOffset()), ScrollType.CENTER_UP);
549 myPreviewEditor.getSettings().setFoldingOutlineShown(problemCount != 1);
550 myPreviewEditor.getComponent().setBorder(IdeBorderFactory.createEmptyBorder());
551 return Pair.create(myPreviewEditor.getComponent(), myPreviewEditor);
553 else if (selectedEntity == null) {
554 return Pair.create(new InspectionNodeInfo(myTree, myProject), null);
556 return Pair.create(InspectionResultsViewUtil.getInvalidEntityLabel(selectedEntity), null);
559 private boolean reuseEditorFor(Document document) {
560 return myPreviewEditor != null && !myPreviewEditor.isDisposed() && myPreviewEditor.getDocument() == document;
564 public InspectionNode addTool(@NotNull final InspectionToolWrapper toolWrapper,
565 HighlightDisplayLevel errorLevel,
566 boolean groupedBySeverity,
567 boolean isSingleInspectionRun) {
569 toolWrapper.getGroupDisplayName().isEmpty() ? InspectionProfileEntry.GENERAL_GROUP_NAME : toolWrapper.getGroupDisplayName();
570 InspectionTreeNode parentNode = getToolParentNode(groupName, errorLevel, groupedBySeverity, isSingleInspectionRun);
571 InspectionNode toolNode = new InspectionNode(toolWrapper, myInspectionProfile);
572 boolean showStructure = myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE;
573 myProvider.appendToolNodeContent(myGlobalInspectionContext, toolNode, parentNode, showStructure);
574 InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(toolWrapper);
575 toolNode = presentation.createToolNode(myGlobalInspectionContext, toolNode, myProvider, parentNode, showStructure);
576 synchronized (getTreeStructureUpdateLock()) {
577 ((DefaultInspectionToolPresentation)presentation).setToolNode(toolNode);
579 registerActionShortcuts(presentation);
583 private void registerActionShortcuts(@NotNull InspectionToolPresentation presentation) {
584 ApplicationManager.getApplication().invokeLater(() -> {
585 final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY, null);
587 for (QuickFixAction fix : fixes) {
588 fix.registerCustomShortcutSet(fix.getShortcutSet(), this);
595 public Set<SuppressIntentionAction> getSuppressActions(InspectionToolWrapper wrapper) {
596 return mySuppressActions.computeIfAbsent(wrapper, (w) -> {
597 final SuppressIntentionAction[] actions = InspectionManagerEx.getSuppressActions(w);
598 return actions == null ? Collections.emptySet() : ContainerUtil.newHashSet(actions);
602 Set<Object> getSuppressedNodes() {
603 return mySuppressedNodes;
607 public ExcludedInspectionTreeNodesManager getExcludedManager() {
608 return myExcludedInspectionTreeNodesManager;
612 public String getCurrentProfileName() {
613 return myInspectionProfile == null ? null : myInspectionProfile.getDisplayName();
616 public InspectionProfile getCurrentProfile() {
617 return myInspectionProfile;
620 public void update() {
621 ApplicationManager.getApplication().assertIsDispatchThread();
622 myTree.removeAllNodes();
623 mySeverityGroupNodes.clear();
627 public void setUpdating(boolean isUpdating) {
628 final Runnable update = () -> {
630 myUpdatingRequestors++;
632 myUpdatingRequestors--;
634 boolean hasUpdatingRequestors = myUpdatingRequestors > 0;
635 myTree.setPaintBusy(hasUpdatingRequestors);
636 if (!hasUpdatingRequestors && myLoadingProgressPreview != null) {
637 myLoadingProgressPreview.treeLoaded();
639 //TODO Dmitrii Batkovich it's a hack (preview panel should be created during selection update)
640 if (!hasUpdatingRequestors && mySplitter.getSecondComponent() == null) {
644 final Application app = ApplicationManager.getApplication();
645 if (app.isDispatchThread()) {
649 app.invokeLater(update, ModalityState.any());
653 public Object getTreeStructureUpdateLock() {
654 return myTreeStructureUpdateLock;
657 public void addTools(Collection<Tools> tools) {
658 InspectionProfileImpl profile = (InspectionProfileImpl)myInspectionProfile;
659 boolean isGroupedBySeverity = myGlobalInspectionContext.getUIOptions().GROUP_BY_SEVERITY;
660 boolean singleInspectionRun = myGlobalInspectionContext.isSingleInspectionRun();
661 for (Tools currentTools : tools) {
662 InspectionToolWrapper defaultToolWrapper = currentTools.getDefaultState().getTool();
663 final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
664 for (ScopeToolState state : myProvider.getTools(currentTools)) {
665 InspectionToolWrapper toolWrapper = state.getTool();
666 if (myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper)) {
668 profile.getErrorLevel(key, state.getScope(myProject), myProject),
670 singleInspectionRun);
676 public void buildTree() {
677 final Application app = ApplicationManager.getApplication();
678 final Runnable buildAction = () -> {
681 synchronized (getTreeStructureUpdateLock()) {
683 addTools(myGlobalInspectionContext.getTools().values());
688 UIUtil.invokeLaterIfNeeded(() -> myTree.restoreExpansionAndSelection(null));
691 if (app.isUnitTestMode()) {
694 app.executeOnPooledThread(() -> app.runReadAction(buildAction));
700 private InspectionTreeNode getToolParentNode(@NotNull String groupName,
701 HighlightDisplayLevel errorLevel,
702 boolean groupedBySeverity,
703 boolean isSingleInspectionRun) {
704 if (!groupedBySeverity && isSingleInspectionRun) {
705 return getTree().getRoot();
707 if (groupName.isEmpty()) {
708 return getRelativeRootNode(groupedBySeverity, errorLevel);
710 ConcurrentMap<String, InspectionGroupNode> map = myGroups.get(errorLevel);
712 map = ConcurrencyUtil.cacheOrGet(myGroups, errorLevel, ContainerUtil.newConcurrentMap());
714 InspectionGroupNode group;
715 if (groupedBySeverity) {
716 group = map.get(groupName);
720 for (Map<String, InspectionGroupNode> groupMap : myGroups.values()) {
721 if ((group = groupMap.get(groupName)) != null) break;
725 if (isSingleInspectionRun) {
726 return getRelativeRootNode(true, errorLevel);
728 group = ConcurrencyUtil.cacheOrGet(map, groupName, new InspectionGroupNode(groupName));
730 insertByIndex(group, getRelativeRootNode(groupedBySeverity, errorLevel));
737 private InspectionTreeNode getRelativeRootNode(boolean isGroupedBySeverity, HighlightDisplayLevel level) {
738 if (isGroupedBySeverity) {
739 InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level);
740 if (severityGroupNode == null) {
741 InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myProject, level);
742 severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode);
743 if (severityGroupNode == newNode) {
744 InspectionTreeNode root = myTree.getRoot();
745 insertByIndex(severityGroupNode, root);
748 return severityGroupNode;
750 return myTree.getRoot();
753 private OccurenceNavigator getOccurenceNavigator() {
754 return myOccurenceNavigator;
758 public boolean hasNextOccurence() {
759 return myOccurenceNavigator != null && myOccurenceNavigator.hasNextOccurence();
763 public boolean hasPreviousOccurence() {
764 return myOccurenceNavigator != null && myOccurenceNavigator.hasPreviousOccurence();
768 public OccurenceInfo goNextOccurence() {
769 return myOccurenceNavigator != null ? myOccurenceNavigator.goNextOccurence() : null;
773 public OccurenceInfo goPreviousOccurence() {
774 return myOccurenceNavigator != null ? myOccurenceNavigator.goPreviousOccurence() : null;
778 public String getNextOccurenceActionName() {
779 return myOccurenceNavigator != null ? myOccurenceNavigator.getNextOccurenceActionName() : "";
783 public String getPreviousOccurenceActionName() {
784 return myOccurenceNavigator != null ? myOccurenceNavigator.getPreviousOccurenceActionName() : "";
788 public Project getProject() {
793 public Object getData(String dataId) {
794 if (PlatformDataKeys.HELP_ID.is(dataId)) return HELP_ID;
795 if (DATA_KEY.is(dataId)) return this;
796 if (ExclusionHandler.EXCLUSION_HANDLER.is(dataId)) return myExclusionHandler;
797 if (myTree == null) return null;
798 TreePath[] paths = myTree.getSelectionPaths();
800 if (paths == null || paths.length == 0) return null;
802 if (paths.length > 1) {
803 if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
804 return collectPsiElements();
809 TreePath path = paths[0];
811 InspectionTreeNode selectedNode = (InspectionTreeNode)path.getLastPathComponent();
813 if (!CommonDataKeys.NAVIGATABLE.is(dataId) && !CommonDataKeys.PSI_ELEMENT.is(dataId)) {
817 if (selectedNode instanceof RefElementNode) {
818 final RefElementNode refElementNode = (RefElementNode)selectedNode;
819 RefEntity refElement = refElementNode.getElement();
820 if (refElement == null) return null;
821 final RefEntity item = refElement.getRefManager().getRefinedElement(refElement);
823 if (!item.isValid()) return null;
825 PsiElement psiElement = item instanceof RefElement ? ((RefElement)item).getElement() : null;
826 if (psiElement == null) return null;
828 final CommonProblemDescriptor problem = refElementNode.getDescriptor();
829 if (problem != null) {
830 if (problem instanceof ProblemDescriptor) {
831 PsiElement elementFromDescriptor = ((ProblemDescriptor)problem).getPsiElement();
832 if (elementFromDescriptor == null) {
833 final InspectionTreeNode node = (InspectionTreeNode)refElementNode.getChildAt(0);
834 if (node.isValid()) {
838 psiElement = elementFromDescriptor;
846 if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
847 return getSelectedNavigatable(problem, psiElement);
849 else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
850 return psiElement.isValid() ? psiElement : null;
853 else if (selectedNode instanceof ProblemDescriptionNode && CommonDataKeys.NAVIGATABLE.is(dataId)) {
854 Navigatable navigatable = getSelectedNavigatable(((ProblemDescriptionNode)selectedNode).getDescriptor());
855 return navigatable == null ? InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)selectedNode) : navigatable;
862 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor) {
863 return getSelectedNavigatable(descriptor,
864 descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getPsiElement() : null);
868 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor, final PsiElement psiElement) {
869 if (descriptor instanceof ProblemDescriptorBase) {
870 Navigatable navigatable = ((ProblemDescriptorBase)descriptor).getNavigatable();
871 if (navigatable != null) {
875 if (psiElement == null || !psiElement.isValid()) return null;
876 PsiFile containingFile = psiElement.getContainingFile();
877 VirtualFile virtualFile = containingFile == null ? null : containingFile.getVirtualFile();
879 if (virtualFile != null) {
880 int startOffset = psiElement.getTextOffset();
881 if (descriptor instanceof ProblemDescriptorBase) {
882 final TextRange textRange = ((ProblemDescriptorBase)descriptor).getTextRangeForNavigation();
883 if (textRange != null) {
884 if (virtualFile instanceof VirtualFileWindow) {
885 virtualFile = ((VirtualFileWindow)virtualFile).getDelegate();
887 startOffset = textRange.getStartOffset();
890 return new OpenFileDescriptor(myProject, virtualFile, startOffset);
895 private PsiElement[] collectPsiElements() {
896 RefEntity[] refElements = myTree.getSelectedElements();
897 List<PsiElement> psiElements = new ArrayList<PsiElement>();
898 for (RefEntity refElement : refElements) {
899 PsiElement psiElement = refElement instanceof RefElement ? ((RefElement)refElement).getElement() : null;
900 if (psiElement != null && psiElement.isValid()) {
901 psiElements.add(psiElement);
905 return PsiUtilCore.toPsiElementArray(psiElements);
909 public InspectionTree getTree() {
914 public GlobalInspectionContextImpl getGlobalInspectionContext() {
915 return myGlobalInspectionContext;
919 public InspectionRVContentProvider getProvider() {
923 public boolean isSingleToolInSelection() {
924 return myTree != null && myTree.getSelectedToolWrapper() != null;
927 public boolean isRerun() {
928 boolean rerun = myRerun;
933 public boolean isProfileDefined() {
934 return myInspectionProfile != null && myInspectionProfile.isEditable();
937 public static void showPopup(AnActionEvent e, JBPopup popup) {
938 final InputEvent event = e.getInputEvent();
939 if (event instanceof MouseEvent) {
940 popup.showUnderneathOf(event.getComponent());
943 popup.showInBestPositionFor(e.getDataContext());
948 public AnalysisScope getScope() {
952 public boolean isUpdating() {
953 return myUpdatingRequestors > 0;
956 void updateRightPanelLoading() {
957 if (!myDisposed && isUpdating() && myLoadingProgressPreview != null) {
958 myLoadingProgressPreview.updateLoadingProgress();
962 public boolean hasProblems() {
963 return hasProblems(myGlobalInspectionContext.getTools().values(), myGlobalInspectionContext, myProvider);
966 public static boolean hasProblems(@NotNull Collection<Tools> tools,
967 @NotNull GlobalInspectionContextImpl context,
968 @NotNull InspectionRVContentProvider contentProvider) {
969 for (Tools currentTools : tools) {
970 for (ScopeToolState state : contentProvider.getTools(currentTools)) {
971 InspectionToolWrapper toolWrapper = state.getTool();
972 if (contentProvider.checkReportedProblems(context, toolWrapper)) {
980 public boolean isDisposed() {
984 private class CloseAction extends AnAction implements DumbAware {
985 private CloseAction() {
986 super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel);
990 public void actionPerformed(AnActionEvent e) {
991 myGlobalInspectionContext.close(true);
995 public void updateCurrentProfile() {
996 final String name = myInspectionProfile.getName();
997 myInspectionProfile = (InspectionProfile)myInspectionProfile.getProfileManager().getProfile(name);
1000 private class RerunAction extends AnAction {
1001 public RerunAction(JComponent comp) {
1002 super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"),
1003 AllIcons.Actions.Rerun);
1004 registerCustomShortcutSet(CommonShortcuts.getRerun(), comp);
1008 public void update(AnActionEvent e) {
1009 e.getPresentation().setEnabled(myScope.isValid());
1013 public void actionPerformed(AnActionEvent e) {
1017 private void rerun() {
1019 if (myScope.isValid()) {
1020 AnalysisUIOptions.getInstance(myProject).save(myGlobalInspectionContext.getUIOptions());
1021 myGlobalInspectionContext.setTreeState(getTree().getTreeState());
1022 myGlobalInspectionContext.doInspections(myScope);