/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.slicer; import com.intellij.analysis.AnalysisUIOptions; import com.intellij.ide.impl.ContentManagerWatcher; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.impl.content.BaseLabel; import com.intellij.psi.*; import com.intellij.refactoring.util.RefactoringDescriptionLocation; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import java.util.regex.Pattern; @State( name = "SliceManager", storages = {@Storage( file = StoragePathMacros.WORKSPACE_FILE)} ) public class SliceManager implements PersistentStateComponent { private final Project myProject; private ContentManager myBackContentManager; private ContentManager myForthContentManager; private final StoredSettingsBean myStoredSettings = new StoredSettingsBean(); private static final String BACK_TOOLWINDOW_ID = "Analyze Dataflow to"; private static final String FORTH_TOOLWINDOW_ID = "Analyze Dataflow from"; public static class StoredSettingsBean { public boolean showDereferences = true; // to show in dataflow/from dialog public AnalysisUIOptions analysisUIOptions = new AnalysisUIOptions(); } public static SliceManager getInstance(@NotNull Project project) { return ServiceManager.getService(project, SliceManager.class); } public SliceManager(@NotNull Project project, PsiManager psiManager) { myProject = project; } @NotNull private Disposable addPsiListener(@NotNull final ProgressIndicator indicator) { Disposable disposable = Disposer.newDisposable(); PsiManager.getInstance(myProject).addPsiTreeChangeListener(new PsiTreeChangeAdapter() { @Override public void beforeChildAddition(@NotNull PsiTreeChangeEvent event) { indicator.cancel(); } @Override public void beforeChildRemoval(@NotNull PsiTreeChangeEvent event) { indicator.cancel(); } @Override public void beforeChildReplacement(@NotNull PsiTreeChangeEvent event) { indicator.cancel(); } @Override public void beforeChildMovement(@NotNull PsiTreeChangeEvent event) { indicator.cancel(); } @Override public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) { indicator.cancel(); } @Override public void beforePropertyChange(@NotNull PsiTreeChangeEvent event) { indicator.cancel(); } }, disposable); return disposable; } private ContentManager getContentManager(boolean dataFlowToThis) { if (dataFlowToThis) { if (myBackContentManager == null) { ToolWindow backToolWindow = ToolWindowManager.getInstance(myProject).registerToolWindow(BACK_TOOLWINDOW_ID, true, ToolWindowAnchor.BOTTOM, myProject); myBackContentManager = backToolWindow.getContentManager(); new ContentManagerWatcher(backToolWindow, myBackContentManager); } return myBackContentManager; } if (myForthContentManager == null) { ToolWindow forthToolWindow = ToolWindowManager.getInstance(myProject).registerToolWindow(FORTH_TOOLWINDOW_ID, true, ToolWindowAnchor.BOTTOM, myProject); myForthContentManager = forthToolWindow.getContentManager(); new ContentManagerWatcher(forthToolWindow, myForthContentManager); } return myForthContentManager; } public void slice(@NotNull PsiElement element, boolean dataFlowToThis, @NotNull SliceHandler handler) { String dialogTitle = getElementDescription((dataFlowToThis ? BACK_TOOLWINDOW_ID : FORTH_TOOLWINDOW_ID) + " ", element, null); dialogTitle = Pattern.compile("()|<[^<>]*>").matcher(dialogTitle).replaceAll(""); SliceAnalysisParams params = handler.askForParams(element, dataFlowToThis, myStoredSettings, dialogTitle); if (params == null) return; SliceRootNode rootNode = new SliceRootNode(myProject, new DuplicateMap(), SliceUsage.createRootUsage(element, params)); createToolWindow(dataFlowToThis, rootNode, false, getElementDescription(null, element, null)); } public void createToolWindow(boolean dataFlowToThis, @NotNull SliceRootNode rootNode, boolean splitByLeafExpressions, @NotNull String displayName) { final SliceToolwindowSettings sliceToolwindowSettings = SliceToolwindowSettings.getInstance(myProject); final ContentManager contentManager = getContentManager(dataFlowToThis); final Content[] myContent = new Content[1]; ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(dataFlowToThis ? BACK_TOOLWINDOW_ID : FORTH_TOOLWINDOW_ID); final SlicePanel slicePanel = new SlicePanel(myProject, dataFlowToThis, rootNode, splitByLeafExpressions, toolWindow) { @Override protected void close() { super.close(); contentManager.removeContent(myContent[0], true); } @Override public boolean isAutoScroll() { return sliceToolwindowSettings.isAutoScroll(); } @Override public void setAutoScroll(boolean autoScroll) { sliceToolwindowSettings.setAutoScroll(autoScroll); } @Override public boolean isPreview() { return sliceToolwindowSettings.isPreview(); } @Override public void setPreview(boolean preview) { sliceToolwindowSettings.setPreview(preview); } }; myContent[0] = contentManager.getFactory().createContent(slicePanel, displayName, true); contentManager.addContent(myContent[0]); contentManager.setSelectedContent(myContent[0]); toolWindow.activate(null); } public static String getElementDescription(String prefix, PsiElement element, String suffix) { PsiElement elementToSlice = element; if (element instanceof PsiReferenceExpression) elementToSlice = ((PsiReferenceExpression)element).resolve(); if (elementToSlice == null) elementToSlice = element; String desc = ElementDescriptionUtil.getElementDescription(elementToSlice, RefactoringDescriptionLocation.WITHOUT_PARENT); return "" + UIUtil.getCssFontDeclaration(BaseLabel.getLabelFont()) + "" + (prefix == null ? "" : prefix) + StringUtil.first(desc, 100, true)+(suffix == null ? "" : suffix) + ""; } public void runInterruptibly(@NotNull ProgressIndicator progress, @NotNull Runnable onCancel, @NotNull Runnable runnable) throws ProcessCanceledException { Disposable disposable = addPsiListener(progress); try { progress.checkCanceled(); ProgressManager.getInstance().executeProcessUnderProgress(runnable, progress); } catch (ProcessCanceledException e) { progress.cancel(); //reschedule for later onCancel.run(); throw e; } finally { Disposer.dispose(disposable); } } @Override public StoredSettingsBean getState() { return myStoredSettings; } @Override public void loadState(StoredSettingsBean state) { myStoredSettings.analysisUIOptions.save(state.analysisUIOptions); } }