package com.jetbrains.actionscript.profiler.ui; import com.intellij.icons.AllIcons; import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; import com.intellij.ide.util.scopeChooser.ScopeDescriptor; import com.intellij.lang.javascript.psi.ecmal4.JSClass; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbServiceImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.Navigatable; import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.ProjectAndLibrariesScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.scope.ProjectFilesScope; import com.intellij.ui.PopupHandler; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeTableSpeedSearch; import com.intellij.util.Function; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import com.jetbrains.actionscript.profiler.ProfilerBundle; import com.jetbrains.actionscript.profiler.livetable.LiveModelController; import com.jetbrains.actionscript.profiler.livetable.LiveObjectsTreeTable; import com.jetbrains.actionscript.profiler.livetable.SizeInfoNode; import com.jetbrains.actionscript.profiler.model.ProfileData; import com.jetbrains.actionscript.profiler.model.ProfilingManager; import com.jetbrains.actionscript.profiler.render.SizeInfoCellRenderer; import com.jetbrains.actionscript.profiler.sampler.FrameInfo; import com.jetbrains.actionscript.profiler.sampler.SampleLocationResolver; import com.jetbrains.actionscript.profiler.util.AllSearchScope; import com.jetbrains.actionscript.profiler.util.ResolveUtil; import com.jetbrains.profiler.ProfileView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.tree.TreePath; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.List; /** * @author: Fedor.Korotkov */ public class LiveObjectsView extends ProfileView implements Disposable { private static final Logger LOG = Logger.getInstance(LiveObjectsView.class.getName()); private static final int MS_COLUMN_WIDTH = 140; private JCheckBox liveUpdatesCheckBox; private LiveObjectsTreeTable liveObjectsTreeTable; private JPanel mainPanel; private ScopeChooserCombo filterScope; private JLabel scopeLabel; private JLabel myAllocatedMemory; private final LiveModelController controller; private final ProfilingManager profilingManager; private Timer updateTimer = null; public LiveObjectsView(VirtualFile file, Project project) { super(file, project); setupUI(); controller = file.getUserData(ProfileData.CONTROLLER); profilingManager = file.getUserData(ProfileData.PROFILING_MANAGER); if (controller == null || profilingManager == null) { return; } updateTimer = UIUtil.createNamedTimer("LiveObjects", 2000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (!liveUpdatesCheckBox.isSelected() || DumbServiceImpl.getInstance(getProject()).isDumb()) { return; } final List<TreePath> paths = TreeUtil.collectExpandedPaths(liveObjectsTreeTable.getTree()); final TreePath selectionPath = liveObjectsTreeTable.getTree().getSelectionPath(); controller.updateScope(getCurrentScope()); controller.apply(liveObjectsTreeTable.getSortableTreeTableModel()); liveObjectsTreeTable.reload(); TreeUtil.restoreExpandedPaths(liveObjectsTreeTable.getTree(), paths); liveObjectsTreeTable.getTree().setSelectionPath(selectionPath); myAllocatedMemory.setText(ProfilerBundle.message("allocated.memory.size", controller.getAllocatedMemorySize())); } }); updateTimer.setInitialDelay(1000); updateTimer.setRepeats(true); updateTimer.start(); liveUpdatesCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (liveUpdatesCheckBox.isSelected()) { updateTimer.stop(); updateTimer.setInitialDelay(0); updateTimer.start(); } } }); liveObjectsTreeTable.setFrameLocationResolveFunction( frameInfo -> new SampleLocationResolver(frameInfo, new ProjectAndLibrariesScope(getProject()))); liveObjectsTreeTable.setClassNameLocationResolveFunction(s -> { PsiElement element = ResolveUtil.findClassByQName(s, getCurrentScope()); if (element instanceof JSClass) { return element.getNavigationElement().getContainingFile(); } return null; }); liveObjectsTreeTable.getTree().setShowsRootHandles(true); profilingManager.startCollectingLiveObjects(new ProfilingManager.Callback() { @Override public void finished(@Nullable String data, @Nullable IOException ex) { if (ex != null) { LOG.warn(ex); } } }); } @Override protected void uiSettingsChange() { super.uiSettingsChange(); liveObjectsTreeTable.clearColorCaches(); } private void createUIComponents() { liveObjectsTreeTable = new LiveObjectsTreeTable(getProject()); filterScope = new ScopeChooserCombo(getProject(), true, false, ProjectFilesScope.NAME); } private void setupUI() { liveObjectsTreeTable.getTree().setRootVisible(false); setColumnWidth(liveObjectsTreeTable.getColumnModel().getColumn(1), MS_COLUMN_WIDTH); setColumnWidth(liveObjectsTreeTable.getColumnModel().getColumn(2), MS_COLUMN_WIDTH); new TreeTableSpeedSearch(liveObjectsTreeTable).setComparator(new SpeedSearchComparator(false)); PopupHandler.installPopupHandler(liveObjectsTreeTable, PROFILER_VIEW_GROUP_ID, ActionPlaces.UNKNOWN); liveObjectsTreeTable.getEmptyText().setText(ProfilerBundle.message("live.objects.loading")); liveObjectsTreeTable.getTree().setCellRenderer(new SizeInfoCellRenderer() { @Override public void customizeCellRenderer(Object value, boolean selected) { Icon icon = AllIcons.General.Information; if (value instanceof SizeInfoNode) { icon = ((SizeInfoNode)value).isMethod() ? AllIcons.Nodes.Method : AllIcons.Nodes.Class; } setPaintFocusBorder(false); setIcon(icon); super.customizeCellRenderer(value, selected); } }); final ComboBoxModel model = filterScope.getComboBox().getModel(); if (model instanceof DefaultComboBoxModel) { ((DefaultComboBoxModel)model).insertElementAt(new ScopeDescriptor(new AllSearchScope(getProject())), 0); } } @Nullable private GlobalSearchScope getCurrentScope() { final SearchScope _selectedScope = filterScope.getSelectedScope(); return _selectedScope instanceof GlobalSearchScope ? (GlobalSearchScope)_selectedScope : GlobalSearchScope.allScope(getProject()); } @NotNull @Override public JComponent getComponent() { return mainPanel; } @Override public JComponent getPreferredFocusedComponent() { return null; } @Override public void dispose() { super.dispose(); if (updateTimer != null) { updateTimer.stop(); } if (profilingManager != null) { profilingManager.stopCollectingLiveObjects(new ProfilingManager.Callback() { @Override public void finished(@Nullable String data, @Nullable IOException ex) { if (ex != null) { LOG.warn(ex); } } }); } } private static void setColumnWidth(TableColumn column, int newSize) { column.setMinWidth(newSize); column.setWidth(newSize); column.setMaxWidth(newSize); } }