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 final TreePath[] paths = myTree.getSelectionPaths();
168 LOG.assertTrue(paths != null);
169 InspectionTreeNode parent = (InspectionTreeNode)node.getParent();
170 InspectionTreeNode toSelect = null;
171 synchronized (myTreeStructureUpdateLock) {
172 if (paths.length == 1) {
173 toSelect = (InspectionTreeNode)node.getNextNode();
176 ((DefaultTreeModel)myTree.getModel()).reload(parent);
178 TreeUtil.selectInTree(toSelect == null ? parent : toSelect, true, myTree);
183 public void includeNode(@NotNull InspectionTreeNode node) {
184 node.amnestyElement(myExcludedInspectionTreeNodesManager);
188 public boolean isActionEnabled(boolean isExcludeAction) {
189 return isExcludeAction || !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS;
193 public void onDone(boolean isExcludeAction) {
194 if (!isExcludeAction || !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS) {
195 myTree.queueUpdate();
200 createActionsToolbar();
201 PsiManager.getInstance(myProject).addPsiTreeChangeListener(new InspectionViewPsiTreeChangeAdapter(this), this);
203 final InspectionProjectProfileManager profileManager = InspectionProjectProfileManager.getInstance(myProject);
204 profileManager.addProfilesListener(new ProfileChangeAdapter() {
206 public void profileChanged(Profile profile) {
207 if (profile == profileManager.getProjectProfileImpl()) {
216 private void initTreeListeners() {
217 myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
219 public void valueChanged(TreeSelectionEvent e) {
220 if (myTree.isUnderQueueUpdate()) return;
222 if (isAutoScrollMode()) {
223 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
228 EditSourceOnDoubleClickHandler.install(myTree);
230 myTree.addKeyListener(new KeyAdapter() {
232 public void keyPressed(KeyEvent e) {
233 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
234 OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
239 PopupHandler.installPopupHandler(myTree, IdeActions.INSPECTION_TOOL_WINDOW_TREE_POPUP, ActionPlaces.CODE_INSPECTION);
240 SmartExpander.installOn(myTree);
243 private OccurenceNavigatorSupport initOccurenceNavigator() {
244 return new OccurenceNavigatorSupport(myTree) {
247 protected Navigatable createDescriptorForNode(DefaultMutableTreeNode node) {
248 if (node instanceof InspectionTreeNode && ((InspectionTreeNode)node).isExcluded(myExcludedInspectionTreeNodesManager)) {
251 if (node instanceof RefElementNode) {
252 final RefElementNode refNode = (RefElementNode)node;
253 if (refNode.hasDescriptorsUnder()) return null;
254 final RefEntity element = refNode.getElement();
255 if (element == null || !element.isValid()) return null;
256 final CommonProblemDescriptor problem = refNode.getDescriptor();
257 if (problem != null) {
258 return navigate(problem);
260 if (element instanceof RefElement) {
261 return getOpenFileDescriptor((RefElement)element);
264 else if (node instanceof ProblemDescriptionNode) {
266 if (((ProblemDescriptionNode)node).isValid()) {
267 if (((ProblemDescriptionNode)node).isQuickFixAppliedFromView()) {
268 isValid = ((ProblemDescriptionNode)node).calculateIsValid();
276 ? navigate(((ProblemDescriptionNode)node).getDescriptor())
277 : InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)node);
283 private Navigatable navigate(final CommonProblemDescriptor descriptor) {
284 return getSelectedNavigatable(descriptor);
288 public String getNextOccurenceActionName() {
289 return InspectionsBundle.message("inspection.action.go.next");
293 public String getPreviousOccurenceActionName() {
294 return InspectionsBundle.message("inspection.actiongo.prev");
299 private void createActionsToolbar() {
300 final JComponent leftActionsToolbar = createLeftActionsToolbar();
301 final JComponent rightActionsToolbar = createRightActionsToolbar();
303 JPanel westPanel = new JPanel(new BorderLayout());
304 westPanel.add(leftActionsToolbar, BorderLayout.WEST);
305 westPanel.add(rightActionsToolbar, BorderLayout.EAST);
306 add(westPanel, BorderLayout.WEST);
309 @SuppressWarnings({"NonStaticInitializer"})
310 private JComponent createRightActionsToolbar() {
311 DefaultActionGroup specialGroup = new DefaultActionGroup();
312 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupBySeverityAction(this));
313 specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupByDirectoryAction(this));
314 specialGroup.add(myGlobalInspectionContext.getUIOptions().createFilterResolvedItemsAction(this));
315 specialGroup.add(myGlobalInspectionContext.createToggleAutoscrollAction());
316 specialGroup.add(new ExportHTMLAction(this));
317 specialGroup.add(ActionManager.getInstance().getAction("EditInspectionSettings"));
318 specialGroup.add(new InvokeQuickFixAction(this));
319 return createToolbar(specialGroup);
322 private JComponent createLeftActionsToolbar() {
323 final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
324 DefaultActionGroup group = new DefaultActionGroup();
325 group.add(new RerunAction(this));
326 group.add(new CloseAction());
327 final TreeExpander treeExpander = new TreeExpander() {
329 public void expandAll() {
330 TreeUtil.expandAll(myTree);
334 public boolean canExpand() {
339 public void collapseAll() {
340 TreeUtil.collapseAll(myTree, 0);
344 public boolean canCollapse() {
348 group.add(actionsManager.createExpandAllAction(treeExpander, myTree));
349 group.add(actionsManager.createCollapseAllAction(treeExpander, myTree));
350 group.add(actionsManager.createPrevOccurenceAction(getOccurenceNavigator()));
351 group.add(actionsManager.createNextOccurenceAction(getOccurenceNavigator()));
352 group.add(new ContextHelpAction(HELP_ID));
354 return createToolbar(group);
357 private static JComponent createToolbar(final DefaultActionGroup specialGroup) {
358 return ActionManager.getInstance().createActionToolbar(ActionPlaces.CODE_INSPECTION, specialGroup, false).getComponent();
362 public void dispose() {
363 InspectionResultsViewUtil.releaseEditor(myPreviewEditor);
364 mySplitter.dispose();
365 myInspectionProfile = null;
367 if (myLoadingProgressPreview != null) {
368 Disposer.dispose(myLoadingProgressPreview);
369 myLoadingProgressPreview = null;
374 private boolean isAutoScrollMode() {
375 String activeToolWindowId = ToolWindowManager.getInstance(myProject).getActiveToolWindowId();
376 return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE &&
377 (activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION));
381 private static OpenFileDescriptor getOpenFileDescriptor(final RefElement refElement) {
382 final VirtualFile[] file = new VirtualFile[1];
383 final int[] offset = new int[1];
385 ApplicationManager.getApplication().runReadAction(() -> {
386 PsiElement psiElement = refElement.getElement();
387 if (psiElement != null) {
388 final PsiFile containingFile = psiElement.getContainingFile();
389 if (containingFile != null) {
390 file[0] = containingFile.getVirtualFile();
391 offset[0] = psiElement.getTextOffset();
399 if (file[0] != null && file[0].isValid()) {
400 return new OpenFileDescriptor(refElement.getRefManager().getProject(), file[0], offset[0]);
405 public void setApplyingFix(boolean applyingFix) {
406 myApplyingFix = applyingFix;
410 public void openRightPanelIfNeed() {
411 if (mySplitter.getSecondComponent() == null) {
416 public void syncRightPanel() {
417 final Editor oldEditor = myPreviewEditor;
418 if (myLoadingProgressPreview != null) {
419 Disposer.dispose(myLoadingProgressPreview);
420 myLoadingProgressPreview = null;
423 final InspectionToolWrapper wrapper = myTree.getSelectedToolWrapper();
424 LOG.assertTrue(wrapper != null);
425 mySplitter.setSecondComponent(InspectionResultsViewUtil.getApplyingFixLabel(wrapper));
427 if (myTree.getSelectionModel().getSelectionCount() != 1) {
428 if (myTree.getSelectedToolWrapper() == null) {
429 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
432 showInRightPanel(myTree.getCommonSelectedElement());
436 TreePath pathSelected = myTree.getSelectionModel().getLeadSelectionPath();
437 if (pathSelected != null) {
438 final InspectionTreeNode node = (InspectionTreeNode)pathSelected.getLastPathComponent();
439 if (node instanceof ProblemDescriptionNode) {
440 final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
441 showInRightPanel(problemNode.getElement());
443 else if (node instanceof InspectionPackageNode ||
444 node instanceof InspectionModuleNode ||
445 node instanceof RefElementNode) {
446 showInRightPanel(node.getContainingFileLocalEntity());
448 else if (node instanceof InspectionNode) {
449 if (myGlobalInspectionContext.getPresentation(((InspectionNode)node).getToolWrapper()).isDummy()) {
450 mySplitter.setSecondComponent(InspectionResultsViewUtil.getNothingToShowTextLabel());
453 showInRightPanel(null);
456 else if (node instanceof InspectionGroupNode || node instanceof InspectionSeverityGroupNode) {
457 final InspectionViewNavigationPanel panel = new InspectionViewNavigationPanel(node, myTree);
458 myLoadingProgressPreview = panel;
459 mySplitter.setSecondComponent(panel);
462 LOG.error("Unexpected node: " + node.getClass());
467 if (oldEditor != null) {
468 if (Boolean.TRUE.equals(oldEditor.getUserData(PREVIEW_EDITOR_IS_REUSED_KEY))) {
469 oldEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, null);
472 InspectionResultsViewUtil.releaseEditor(oldEditor);
473 if (oldEditor == myPreviewEditor) {
474 myPreviewEditor = null;
480 private void showInRightPanel(@Nullable final RefEntity refEntity) {
481 Cursor currentCursor = getCursor();
483 setCursor(new Cursor(Cursor.WAIT_CURSOR));
484 final JPanel editorPanel = new JPanel();
485 editorPanel.setLayout(new BorderLayout());
486 final int problemCount = myTree.getSelectedProblemCount();
487 JComponent previewPanel = null;
488 final InspectionToolWrapper tool = myTree.getSelectedToolWrapper();
489 if (tool != null && refEntity != null && refEntity.isValid()) {
490 final InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(tool);
491 previewPanel = presentation.getCustomPreviewPanel(refEntity);
493 EditorEx previewEditor = null;
494 if (previewPanel == null) {
495 final Pair<JComponent, EditorEx> panelAndEditor = createBaseRightComponentFor(problemCount, refEntity);
496 previewPanel = panelAndEditor.getFirst();
497 previewEditor = panelAndEditor.getSecond();
499 editorPanel.add(previewPanel, BorderLayout.CENTER);
500 if (problemCount > 0) {
501 final JComponent fixToolbar = QuickFixPreviewPanelFactory.create(previewEditor, this);
502 if (fixToolbar != null) {
503 if (fixToolbar instanceof InspectionTreeLoadingProgressAware) {
504 myLoadingProgressPreview = (InspectionTreeLoadingProgressAware)fixToolbar;
506 if (previewEditor != null) {
507 previewPanel.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP));
509 editorPanel.add(fixToolbar, BorderLayout.NORTH);
512 mySplitter.setSecondComponent(editorPanel);
515 setCursor(currentCursor);
519 private Pair<JComponent, EditorEx> createBaseRightComponentFor(int problemCount, RefEntity selectedEntity) {
520 if (selectedEntity instanceof RefElement &&
521 selectedEntity.isValid() &&
522 !(((RefElement)selectedEntity).getElement() instanceof PsiDirectory)) {
523 PsiElement selectedElement = ((RefElement)selectedEntity).getElement();
524 if (problemCount == 1) {
525 CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors();
526 if (descriptors.length != 0) {
527 final CommonProblemDescriptor descriptor = descriptors[0];
528 if (descriptor instanceof ProblemDescriptorBase) {
529 final PsiElement element = ((ProblemDescriptorBase)descriptor).getPsiElement();
530 if (element != null) {
531 selectedElement = element;
536 final PsiFile file = selectedElement.getContainingFile();
537 final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
538 if (document == null) {
539 return Pair.create(InspectionResultsViewUtil.createLabelForText("Can't open preview for \'" + file.getName() + "\'"), null);
542 if (reuseEditorFor(document)) {
543 myPreviewEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, true);
544 myPreviewEditor.getFoldingModel().runBatchFoldingOperation(() -> {
545 myPreviewEditor.getFoldingModel().clearFoldRegions();
546 myPreviewEditor.getMarkupModel().removeAllHighlighters();
550 myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true);
551 DiffUtil.setFoldingModelSupport(myPreviewEditor);
552 final EditorSettings settings = myPreviewEditor.getSettings();
553 settings.setLineNumbersShown(false);
554 settings.setLineMarkerAreaShown(false);
555 settings.setAdditionalColumnsCount(0);
556 settings.setAdditionalLinesCount(0);
557 settings.setLeadingWhitespaceShown(true);
558 myPreviewEditor.getColorsScheme().setColor(EditorColors.GUTTER_BACKGROUND, myPreviewEditor.getColorsScheme().getDefaultBackground());
559 myPreviewEditor.getScrollPane().setBorder(IdeBorderFactory.createEmptyBorder());
561 if (problemCount == 0) {
562 myPreviewEditor.getScrollingModel().scrollTo(myPreviewEditor.offsetToLogicalPosition(selectedElement.getTextOffset()), ScrollType.CENTER_UP);
564 myPreviewEditor.getSettings().setFoldingOutlineShown(problemCount > 1);
565 myPreviewEditor.getComponent().setBorder(IdeBorderFactory.createEmptyBorder());
566 return Pair.create(myPreviewEditor.getComponent(), myPreviewEditor);
568 else if (selectedEntity == null) {
569 return Pair.create(new InspectionNodeInfo(myTree, myProject), null);
571 else if (selectedEntity.isValid()) {
572 return Pair.create(InspectionResultsViewUtil.getPreviewIsNotAvailable(selectedEntity), null);
574 return Pair.create(InspectionResultsViewUtil.getInvalidEntityLabel(selectedEntity), null);
577 private boolean reuseEditorFor(Document document) {
578 return myPreviewEditor != null && !myPreviewEditor.isDisposed() && myPreviewEditor.getDocument() == document;
582 public InspectionNode addTool(@NotNull final InspectionToolWrapper toolWrapper,
583 HighlightDisplayLevel errorLevel,
584 boolean groupedBySeverity,
585 boolean isSingleInspectionRun) {
587 toolWrapper.getGroupDisplayName().isEmpty() ? InspectionProfileEntry.GENERAL_GROUP_NAME : toolWrapper.getGroupDisplayName();
588 InspectionTreeNode parentNode = getToolParentNode(groupName, toolWrapper.getGroupPath(), errorLevel, groupedBySeverity, isSingleInspectionRun);
589 InspectionNode toolNode = new InspectionNode(toolWrapper, myInspectionProfile);
590 boolean showStructure = myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE;
591 myProvider.appendToolNodeContent(myGlobalInspectionContext, toolNode, parentNode, showStructure);
592 InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(toolWrapper);
593 toolNode = presentation.createToolNode(myGlobalInspectionContext, toolNode, myProvider, parentNode, showStructure);
594 synchronized (getTreeStructureUpdateLock()) {
595 ((DefaultInspectionToolPresentation)presentation).setToolNode(toolNode);
597 registerActionShortcuts(presentation);
601 private void registerActionShortcuts(@NotNull InspectionToolPresentation presentation) {
602 ApplicationManager.getApplication().invokeLater(() -> {
603 final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY, null);
605 for (QuickFixAction fix : fixes) {
606 fix.registerCustomShortcutSet(fix.getShortcutSet(), this);
613 public Set<SuppressIntentionAction> getSuppressActions(InspectionToolWrapper wrapper) {
614 return mySuppressActions.computeIfAbsent(wrapper.getShortName(), (w) -> {
615 final SuppressIntentionAction[] actions = InspectionManagerEx.getSuppressActions(wrapper);
616 return actions == null ? Collections.emptySet() : ContainerUtil.newLinkedHashSet(actions);
620 public Set<Object> getSuppressedNodes() {
621 return mySuppressedNodes;
625 public ExcludedInspectionTreeNodesManager getExcludedManager() {
626 return myExcludedInspectionTreeNodesManager;
630 public String getCurrentProfileName() {
631 return myInspectionProfile == null ? null : myInspectionProfile.getDisplayName();
634 public InspectionProfile getCurrentProfile() {
635 return myInspectionProfile;
638 public void update() {
639 ApplicationManager.getApplication().assertIsDispatchThread();
640 myTree.removeAllNodes();
641 mySeverityGroupNodes.clear();
645 public void setUpdating(boolean isUpdating) {
646 final Runnable update = () -> {
648 myUpdatingRequestors++;
650 myUpdatingRequestors--;
652 boolean hasUpdatingRequestors = myUpdatingRequestors > 0;
653 myTree.setPaintBusy(hasUpdatingRequestors);
654 if (!hasUpdatingRequestors && myLoadingProgressPreview != null) {
655 myLoadingProgressPreview.treeLoaded();
657 //TODO Dmitrii Batkovich it's a hack (preview panel should be created during selection update)
658 if (!hasUpdatingRequestors && mySplitter.getSecondComponent() == null) {
659 if (myTree.getSelectionModel().getSelectionPath() == null) {
660 TreeUtil.selectFirstNode(myTree);
665 final Application app = ApplicationManager.getApplication();
666 if (app.isDispatchThread()) {
670 app.invokeLater(update, ModalityState.any());
674 public Object getTreeStructureUpdateLock() {
675 return myTreeStructureUpdateLock;
678 public void addTools(Collection<Tools> tools) {
679 InspectionProfileImpl profile = (InspectionProfileImpl)myInspectionProfile;
680 boolean isGroupedBySeverity = myGlobalInspectionContext.getUIOptions().GROUP_BY_SEVERITY;
681 boolean singleInspectionRun = myGlobalInspectionContext.isSingleInspectionRun();
682 for (Tools currentTools : tools) {
683 InspectionToolWrapper defaultToolWrapper = currentTools.getDefaultState().getTool();
684 if (myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS && myExcludedInspectionTreeNodesManager.containsInspectionNode(defaultToolWrapper)) {
687 final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
688 for (ScopeToolState state : myProvider.getTools(currentTools)) {
689 InspectionToolWrapper toolWrapper = state.getTool();
690 if (myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper)) {
692 profile.getErrorLevel(key, state.getScope(myProject), myProject),
694 singleInspectionRun);
700 public void buildTree() {
701 final Application app = ApplicationManager.getApplication();
702 final Runnable buildAction = () -> {
705 synchronized (getTreeStructureUpdateLock()) {
707 addTools(myGlobalInspectionContext.getTools().values());
712 UIUtil.invokeLaterIfNeeded(() -> myTree.restoreExpansionAndSelection(null));
715 if (app.isUnitTestMode()) {
718 app.executeOnPooledThread(() -> app.runReadAction(buildAction));
724 private InspectionTreeNode getToolParentNode(@NotNull String groupName,
725 @NotNull String[] groupPath,
726 HighlightDisplayLevel errorLevel,
727 boolean groupedBySeverity,
728 boolean isSingleInspectionRun) {
729 if (!groupedBySeverity && isSingleInspectionRun) {
730 return getTree().getRoot();
732 if (groupName.isEmpty()) {
733 return getRelativeRootNode(groupedBySeverity, errorLevel);
735 ConcurrentMap<String, InspectionGroupNode> map = myGroups.get(errorLevel);
737 map = ConcurrencyUtil.cacheOrGet(myGroups, errorLevel, ContainerUtil.newConcurrentMap());
739 InspectionGroupNode group;
740 if (groupedBySeverity) {
741 group = map.get(groupName);
745 for (Map<String, InspectionGroupNode> groupMap : myGroups.values()) {
746 if ((group = groupMap.get(groupName)) != null) break;
750 if (isSingleInspectionRun) {
751 return getRelativeRootNode(true, errorLevel);
753 group = ConcurrencyUtil.cacheOrGet(map, groupName, new InspectionGroupNode(groupName, groupPath));
755 getRelativeRootNode(groupedBySeverity, errorLevel).insertByOrder(group);
762 private InspectionTreeNode getRelativeRootNode(boolean isGroupedBySeverity, HighlightDisplayLevel level) {
763 if (isGroupedBySeverity) {
764 InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level);
765 if (severityGroupNode == null) {
766 InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myProject, level);
767 severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode);
768 if (severityGroupNode == newNode) {
769 InspectionTreeNode root = myTree.getRoot();
770 root.insertByOrder(severityGroupNode);
773 return severityGroupNode;
775 return myTree.getRoot();
778 private OccurenceNavigator getOccurenceNavigator() {
779 return myOccurenceNavigator;
783 public boolean hasNextOccurence() {
784 return myOccurenceNavigator != null && myOccurenceNavigator.hasNextOccurence();
788 public boolean hasPreviousOccurence() {
789 return myOccurenceNavigator != null && myOccurenceNavigator.hasPreviousOccurence();
793 public OccurenceInfo goNextOccurence() {
794 return myOccurenceNavigator != null ? myOccurenceNavigator.goNextOccurence() : null;
798 public OccurenceInfo goPreviousOccurence() {
799 return myOccurenceNavigator != null ? myOccurenceNavigator.goPreviousOccurence() : null;
803 public String getNextOccurenceActionName() {
804 return myOccurenceNavigator != null ? myOccurenceNavigator.getNextOccurenceActionName() : "";
808 public String getPreviousOccurenceActionName() {
809 return myOccurenceNavigator != null ? myOccurenceNavigator.getPreviousOccurenceActionName() : "";
813 public Project getProject() {
818 public Object getData(String dataId) {
819 if (PlatformDataKeys.HELP_ID.is(dataId)) return HELP_ID;
820 if (DATA_KEY.is(dataId)) return this;
821 if (ExclusionHandler.EXCLUSION_HANDLER.is(dataId)) return myExclusionHandler;
822 if (myTree == null) return null;
823 TreePath[] paths = myTree.getSelectionPaths();
825 if (paths == null || paths.length == 0) return null;
827 if (paths.length > 1) {
828 if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
829 return collectPsiElements();
834 TreePath path = paths[0];
836 InspectionTreeNode selectedNode = (InspectionTreeNode)path.getLastPathComponent();
838 if (!CommonDataKeys.NAVIGATABLE.is(dataId) && !CommonDataKeys.PSI_ELEMENT.is(dataId)) {
842 if (selectedNode instanceof RefElementNode) {
843 final RefElementNode refElementNode = (RefElementNode)selectedNode;
844 RefEntity refElement = refElementNode.getElement();
845 if (refElement == null) return null;
846 final RefEntity item = refElement.getRefManager().getRefinedElement(refElement);
848 if (!item.isValid()) return null;
850 PsiElement psiElement = item instanceof RefElement ? ((RefElement)item).getElement() : null;
851 if (psiElement == null) return null;
853 final CommonProblemDescriptor problem = refElementNode.getDescriptor();
854 if (problem != null) {
855 if (problem instanceof ProblemDescriptor) {
856 PsiElement elementFromDescriptor = ((ProblemDescriptor)problem).getPsiElement();
857 if (elementFromDescriptor == null) {
858 final InspectionTreeNode node = (InspectionTreeNode)refElementNode.getChildAt(0);
859 if (node.isValid()) {
863 psiElement = elementFromDescriptor;
871 if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
872 return getSelectedNavigatable(problem, psiElement);
874 else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
875 return psiElement.isValid() ? psiElement : null;
878 else if (selectedNode instanceof ProblemDescriptionNode && CommonDataKeys.NAVIGATABLE.is(dataId)) {
879 Navigatable navigatable = getSelectedNavigatable(((ProblemDescriptionNode)selectedNode).getDescriptor());
880 return navigatable == null ? InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)selectedNode) : navigatable;
887 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor) {
888 return getSelectedNavigatable(descriptor,
889 descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getPsiElement() : null);
893 private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor, final PsiElement psiElement) {
894 if (descriptor instanceof ProblemDescriptorBase) {
895 Navigatable navigatable = ((ProblemDescriptorBase)descriptor).getNavigatable();
896 if (navigatable != null) {
900 if (psiElement == null || !psiElement.isValid()) return null;
901 PsiFile containingFile = psiElement.getContainingFile();
902 VirtualFile virtualFile = containingFile == null ? null : containingFile.getVirtualFile();
904 if (virtualFile != null) {
905 int startOffset = psiElement.getTextOffset();
906 if (descriptor instanceof ProblemDescriptorBase) {
907 final TextRange textRange = ((ProblemDescriptorBase)descriptor).getTextRangeForNavigation();
908 if (textRange != null) {
909 if (virtualFile instanceof VirtualFileWindow) {
910 virtualFile = ((VirtualFileWindow)virtualFile).getDelegate();
912 startOffset = textRange.getStartOffset();
915 return new OpenFileDescriptor(myProject, virtualFile, startOffset);
920 private PsiElement[] collectPsiElements() {
921 RefEntity[] refElements = myTree.getSelectedElements();
922 List<PsiElement> psiElements = new ArrayList<PsiElement>();
923 for (RefEntity refElement : refElements) {
924 PsiElement psiElement = refElement instanceof RefElement ? ((RefElement)refElement).getElement() : null;
925 if (psiElement != null && psiElement.isValid()) {
926 psiElements.add(psiElement);
930 return PsiUtilCore.toPsiElementArray(psiElements);
934 public InspectionTree getTree() {
939 public GlobalInspectionContextImpl getGlobalInspectionContext() {
940 return myGlobalInspectionContext;
944 public InspectionRVContentProvider getProvider() {
948 public boolean isSingleToolInSelection() {
949 return myTree != null && myTree.getSelectedToolWrapper() != null;
952 public boolean isRerun() {
953 boolean rerun = myRerun;
958 public boolean isProfileDefined() {
959 return myInspectionProfile != null && myInspectionProfile.isEditable();
962 public static void showPopup(AnActionEvent e, JBPopup popup) {
963 final InputEvent event = e.getInputEvent();
964 if (event instanceof MouseEvent) {
965 popup.showUnderneathOf(event.getComponent());
968 popup.showInBestPositionFor(e.getDataContext());
973 public AnalysisScope getScope() {
977 public boolean isUpdating() {
978 return myUpdatingRequestors > 0;
981 void updateRightPanelLoading() {
982 if (!myDisposed && isUpdating() && myLoadingProgressPreview != null) {
983 myLoadingProgressPreview.updateLoadingProgress();
987 public boolean hasProblems() {
988 return hasProblems(myGlobalInspectionContext.getTools().values(), myGlobalInspectionContext, myProvider);
991 public static boolean hasProblems(@NotNull Collection<Tools> tools,
992 @NotNull GlobalInspectionContextImpl context,
993 @NotNull InspectionRVContentProvider contentProvider) {
994 for (Tools currentTools : tools) {
995 for (ScopeToolState state : contentProvider.getTools(currentTools)) {
996 InspectionToolWrapper toolWrapper = state.getTool();
997 if (contentProvider.checkReportedProblems(context, toolWrapper)) {
1005 public boolean isDisposed() {
1009 private class CloseAction extends AnAction implements DumbAware {
1010 private CloseAction() {
1011 super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel);
1015 public void actionPerformed(AnActionEvent e) {
1016 myGlobalInspectionContext.close(true);
1020 public void updateCurrentProfile() {
1021 final String name = myInspectionProfile.getName();
1022 myInspectionProfile = (InspectionProfile)myInspectionProfile.getProfileManager().getProfile(name);
1025 private class RerunAction extends AnAction {
1026 public RerunAction(JComponent comp) {
1027 super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"),
1028 AllIcons.Actions.Rerun);
1029 registerCustomShortcutSet(CommonShortcuts.getRerun(), comp);
1033 public void update(AnActionEvent e) {
1034 e.getPresentation().setEnabled(myScope.isValid());
1038 public void actionPerformed(AnActionEvent e) {
1042 private void rerun() {
1044 if (myScope.isValid()) {
1045 AnalysisUIOptions.getInstance(myProject).save(myGlobalInspectionContext.getUIOptions());
1046 myGlobalInspectionContext.setTreeState(getTree().getTreeState());
1047 myGlobalInspectionContext.doInspections(myScope);