moved addThreadDump to utils
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / ui / DebuggerSessionTab.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.debugger.ui;
17
18 import com.intellij.debugger.DebugUIEnvironment;
19 import com.intellij.debugger.DebuggerBundle;
20 import com.intellij.debugger.actions.DebuggerActions;
21 import com.intellij.debugger.engine.DebugProcessImpl;
22 import com.intellij.debugger.engine.evaluation.EvaluateException;
23 import com.intellij.debugger.engine.evaluation.TextWithImports;
24 import com.intellij.debugger.impl.DebuggerContextImpl;
25 import com.intellij.debugger.impl.DebuggerContextListener;
26 import com.intellij.debugger.impl.DebuggerSession;
27 import com.intellij.debugger.impl.DebuggerStateManager;
28 import com.intellij.debugger.settings.DebuggerSettings;
29 import com.intellij.debugger.ui.impl.MainWatchPanel;
30 import com.intellij.debugger.ui.impl.ThreadsPanel;
31 import com.intellij.debugger.ui.impl.VariablesPanel;
32 import com.intellij.debugger.ui.impl.WatchDebuggerTree;
33 import com.intellij.debugger.ui.impl.watch.*;
34 import com.intellij.execution.DefaultExecutionResult;
35 import com.intellij.execution.ExecutionManager;
36 import com.intellij.execution.ExecutionResult;
37 import com.intellij.execution.configurations.RunProfile;
38 import com.intellij.execution.executors.DefaultDebugExecutor;
39 import com.intellij.execution.ui.ConsoleView;
40 import com.intellij.execution.ui.ExecutionConsoleEx;
41 import com.intellij.execution.ui.layout.PlaceInGrid;
42 import com.intellij.icons.AllIcons;
43 import com.intellij.idea.ActionsBundle;
44 import com.intellij.openapi.Disposable;
45 import com.intellij.openapi.actionSystem.*;
46 import com.intellij.openapi.application.ApplicationManager;
47 import com.intellij.openapi.diagnostic.Logger;
48 import com.intellij.openapi.project.Project;
49 import com.intellij.openapi.util.Disposer;
50 import com.intellij.ui.content.AlertIcon;
51 import com.intellij.ui.content.Content;
52 import com.intellij.ui.content.ContentManagerAdapter;
53 import com.intellij.ui.content.ContentManagerEvent;
54 import com.intellij.ui.content.tabs.PinToolwindowTabAction;
55 import com.intellij.xdebugger.XDebuggerBundle;
56 import com.intellij.xdebugger.impl.actions.XDebuggerActions;
57 import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager;
58 import com.intellij.xdebugger.impl.ui.DebuggerSessionTabBase;
59 import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
60 import org.jetbrains.annotations.NotNull;
61 import org.jetbrains.annotations.Nullable;
62
63 import javax.swing.*;
64 import javax.swing.tree.TreePath;
65
66 public class DebuggerSessionTab extends DebuggerSessionTabBase implements Disposable {
67   private static final Logger LOG = Logger.getInstance(DebuggerSessionTab.class);
68
69   private final VariablesPanel myVariablesPanel;
70   private final MainWatchPanel myWatchPanel;
71
72   private volatile DebuggerSession myDebuggerSession;
73
74   private final MyDebuggerStateManager myStateManager = new MyDebuggerStateManager();
75
76   private final FramesPanel myFramesPanel;
77   private final DebugUIEnvironment myDebugUIEnvironment;
78
79   private final ThreadsPanel myThreadsPanel;
80
81   public DebuggerSessionTab(final Project project, final String sessionName, @NotNull final DebugUIEnvironment environment,
82                             @NotNull DebuggerSession debuggerSession) {
83     super(project, "JavaDebugger", sessionName, debuggerSession.getSearchScope());
84
85     myDebuggerSession = debuggerSession;
86     myDebugUIEnvironment = environment;
87
88     final DefaultActionGroup focus = new DefaultActionGroup();
89     focus.add(ActionManager.getInstance().getAction("Debugger.FocusOnBreakpoint"));
90     myUi.getOptions().setAdditionalFocusActions(focus);
91
92     final DebuggerSettings debuggerSettings = DebuggerSettings.getInstance();
93     if (!ApplicationManager.getApplication().isUnitTestMode()) {
94       getContextManager().addListener(new DebuggerContextListener() {
95         @Override
96         public void changeEvent(DebuggerContextImpl newContext, int event) {
97           switch (event) {
98             case DebuggerSession.EVENT_DETACHED:
99               myUi.updateActionsNow();
100
101               if (XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings().isHideDebuggerOnProcessTermination()) {
102                 try {
103                   ExecutionManager.getInstance(project).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor);
104                 }
105                 catch (NullPointerException e) {
106                   //if we can get closeProcess after the project have been closed
107                   LOG.debug(e);
108                 }
109               }
110               break;
111           }
112         }
113       });
114     }
115
116     DefaultActionGroup topToolbar = new DefaultActionGroup();
117     ActionManager actionManager = ActionManager.getInstance();
118     topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP));
119     topToolbar.add(actionManager.getAction(DebuggerActions.POP_FRAME), new Constraints(Anchor.AFTER, XDebuggerActions.STEP_OUT));
120     topToolbar.add(Separator.getInstance(), new Constraints(Anchor.BEFORE, DebuggerActions.POP_FRAME));
121     topToolbar.add(Separator.getInstance(), new Constraints(Anchor.AFTER, DebuggerActions.POP_FRAME));
122     myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
123
124     myWatchPanel = new MainWatchPanel(project, getContextManager());
125     myFramesPanel = new FramesPanel(project, getContextManager());
126
127     final AlertIcon breakpointAlert = new AlertIcon(AllIcons.Debugger.BreakpointAlert);
128
129     // watches
130     Content watches = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchPanel, XDebuggerBundle.message("debugger.session.tab.watches.title"),
131                                          AllIcons.Debugger.Watches, null);
132     watches.setCloseable(false);
133     watches.setAlertIcon(breakpointAlert);
134     myUi.addContent(watches, 0, PlaceInGrid.right, false);
135
136     // frames
137     Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, myFramesPanel, XDebuggerBundle.message("debugger.session.tab.frames.title"),
138                                                AllIcons.Debugger.Frame, myFramesPanel.getFramesList());
139     framesContent.setCloseable(false);
140     framesContent.setAlertIcon(breakpointAlert);
141
142     myUi.addContent(framesContent, 0, PlaceInGrid.left, false);
143
144     // variables
145     myVariablesPanel = new VariablesPanel(project, myStateManager, this);
146     myVariablesPanel.getFrameTree().setAutoVariablesMode(debuggerSettings.AUTO_VARIABLES_MODE);
147     Content vars = myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, myVariablesPanel, XDebuggerBundle.message("debugger.session.tab.variables.title"),
148                                       AllIcons.Debugger.Value, null);
149     vars.setCloseable(false);
150     vars.setAlertIcon(breakpointAlert);
151     myUi.addContent(vars, 0, PlaceInGrid.center, false);
152
153     // threads
154     myThreadsPanel = new ThreadsPanel(project, getContextManager());
155     Content threadsContent = myUi.createContent(DebuggerContentInfo.THREADS_CONTENT, myThreadsPanel, XDebuggerBundle.message("debugger.session.tab.threads.title"),
156                                                 AllIcons.Debugger.Threads, null);
157     threadsContent.setCloseable(false);
158     //threadsContent.setAlertIcon(breakpointAlert);
159
160     //final DefaultActionGroup threadsGroup = new DefaultActionGroup();
161     //threadsContent.setActions(threadsGroup, ActionPlaces.DEBUGGER_TOOLBAR, threadsPanel.getThreadsTree());
162
163     myUi.addContent(threadsContent, 0, PlaceInGrid.left, true);
164
165     for (Content each : myUi.getContents()) {
166       updateStatus(each);
167     }
168
169     myUi.addListener(new ContentManagerAdapter() {
170       @Override
171       public void selectionChanged(ContentManagerEvent event) {
172         updateStatus(event.getContent());
173       }
174     }, this);
175
176     debuggerSession.getContextManager().addListener(new DebuggerContextListener() {
177       @Override
178       public void changeEvent(DebuggerContextImpl newContext, int event) {
179         if (!myUi.isDisposed()) {
180           attractFramesOnPause(event);
181           myStateManager.fireStateChanged(newContext, event);
182           SwingUtilities.invokeLater(new Runnable() {
183             @Override
184             public void run() {
185               if (!myUi.isDisposed()) {
186                 myUi.updateActionsNow();
187               }
188             }
189           });
190         }
191       }
192     });
193
194 //    ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult();
195 //    myConsole = executionResult.getExecutionConsole();
196 //    myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName(),
197 //                                                      environment.getIcon());
198 //    initUI(executionResult);
199   }
200
201   private static void updateStatus(final Content content) {
202     if (content.getComponent() instanceof DebuggerView) {
203       final DebuggerView view = (DebuggerView)content.getComponent();
204       if (content.isSelected()) {
205         view.setUpdateEnabled(true);
206         if (view.isRefreshNeeded()) {
207           view.rebuildIfVisible(DebuggerSession.EVENT_CONTEXT);
208         }
209       }
210       else {
211         view.setUpdateEnabled(false);
212       }
213     }
214   }
215
216   public MainWatchPanel getWatchPanel() {
217     return myWatchPanel;
218   }
219
220   private void initUI(ExecutionResult executionResult) {
221     if (ApplicationManager.getApplication().isUnitTestMode()) {
222       return;
223     }
224
225
226     myUi.removeContent(myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT), true);
227
228     Content console = null;
229     if (myConsole instanceof ExecutionConsoleEx) {
230       ((ExecutionConsoleEx)myConsole).buildUi(myUi);
231       console = myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT);
232       if (console == null) {
233         LOG.debug("Reuse console created with non-debug runner");
234       }
235     }
236     if (console == null) {
237       console = myUi.createContent(DebuggerContentInfo.CONSOLE_CONTENT, myConsole.getComponent(),
238                                            XDebuggerBundle.message("debugger.session.tab.console.content.name"),
239                                            AllIcons.Debugger.Console, myConsole.getPreferredFocusableComponent());
240
241       console.setCloseable(false);
242       myUi.addContent(console, 1, PlaceInGrid.bottom, false);
243     }
244     attachNotificationTo(console);
245
246     if (myConsole != null) {
247       Disposer.register(this, myConsole);
248     }
249
250     final DefaultActionGroup consoleActions = new DefaultActionGroup();
251     if (myConsole instanceof ConsoleView) {
252       AnAction[] actions = ((ConsoleView)myConsole).createConsoleActions();
253       for (AnAction goAction : actions) {
254         consoleActions.add(goAction);
255       }
256     }
257     console.setActions(consoleActions, ActionPlaces.DEBUGGER_TOOLBAR, myConsole.getPreferredFocusableComponent());
258
259 //    myDebugUIEnvironment.initLogs(myRunContentDescriptor, myManager);
260
261     DefaultActionGroup leftToolbar = new DefaultActionGroup();
262
263     if (executionResult instanceof DefaultExecutionResult) {
264       final AnAction[] actions = ((DefaultExecutionResult)executionResult).getRestartActions();
265         leftToolbar.addAll(actions);
266         if (actions.length > 0) {
267           leftToolbar.addSeparator();
268         }
269     }
270     final AnAction[] profileActions = executionResult.getActions();
271     leftToolbar.addAll(profileActions);
272
273     leftToolbar.add(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_LEFT_TOOLBAR_GROUP));
274     if (executionResult instanceof DefaultExecutionResult) {
275       AnAction[] actions = ((DefaultExecutionResult)executionResult).getAdditionalStopActions();
276       for (AnAction action : actions) {
277         leftToolbar.add(action, new Constraints(Anchor.AFTER, IdeActions.ACTION_STOP_PROGRAM));
278       }
279     }
280
281     leftToolbar.addSeparator();
282     addAction(leftToolbar, DebuggerActions.DUMP_THREADS);
283     leftToolbar.addSeparator();
284
285     leftToolbar.add(myUi.getOptions().getLayoutActions());
286
287     final AnAction[] commonSettings = myUi.getOptions().getSettingsActionsList();
288     final AnAction commonSettingsList = myUi.getOptions().getSettingsActions();
289
290     final DefaultActionGroup settings = new DefaultActionGroup("DebuggerSettings", true) {
291       @Override
292       public void update(AnActionEvent e) {
293         e.getPresentation().setText(ActionsBundle.message("group.XDebugger.settings.text"));
294         e.getPresentation().setIcon(commonSettingsList.getTemplatePresentation().getIcon());
295       }
296
297       @Override
298       public boolean isDumbAware() {
299         return true;
300       }
301     };
302     for (AnAction each : commonSettings) {
303       settings.add(each);
304     }
305     if (commonSettings.length > 0) {
306       settings.addSeparator();
307     }
308     settings.add(new WatchLastMethodReturnValueAction());
309     settings.add(new AutoVarsSwitchAction());
310     settings.addSeparator();
311     addActionToGroup(settings, XDebuggerActions.INLINE_DEBUGGER);
312     addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP);
313     addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP_ON_SELECTION);
314
315     leftToolbar.add(settings);
316
317     leftToolbar.addSeparator();
318
319     addActionToGroup(leftToolbar, PinToolwindowTabAction.ACTION_NAME);
320
321     myDebugUIEnvironment.initActions(myRunContentDescriptor, leftToolbar);
322
323     myUi.getOptions().setLeftToolbar(leftToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
324   }
325
326   private static void addAction(DefaultActionGroup group, String actionId) {
327     group.add(ActionManager.getInstance().getAction(actionId));
328   }
329
330   private static void addActionToGroup(final DefaultActionGroup group, final String actionId) {
331     AnAction action = ActionManager.getInstance().getAction(actionId);
332     if (action != null) group.add(action);
333   }
334
335   @Override
336   public void dispose() {
337     disposeSession();
338     myFramesPanel.dispose();
339     myVariablesPanel.dispose();
340     myWatchPanel.dispose();
341     myThreadsPanel.dispose();
342     myConsole = null;
343     super.dispose();
344   }
345
346   private void disposeSession() {
347     final DebuggerSession session = myDebuggerSession;
348     myDebuggerSession = null;
349     if (session != null) {
350       session.dispose();
351     }
352   }
353
354   @Nullable
355   private DebugProcessImpl getDebugProcess() {
356     final DebuggerSession session = myDebuggerSession;
357     return session != null ? session.getProcess() : null;
358   }
359
360   public void reuse(DebuggerSessionTab reuseSession) {
361     DebuggerTreeNodeImpl[] watches = reuseSession.getWatchPanel().getWatchTree().getWatches();
362
363     final WatchDebuggerTree watchTree = getWatchPanel().getWatchTree();
364     for (DebuggerTreeNodeImpl watch : watches) {
365       watchTree.addWatch((WatchItemDescriptor)watch.getDescriptor());
366     }
367
368     DebugProcessImpl process = getDebugProcess();
369     DebugProcessImpl reuseProcess = reuseSession.getDebugProcess();
370     if (process != null && reuseProcess != null) {
371       //process.setBreakpointsMuted(reuseProcess.areBreakpointsMuted());
372     }
373   }
374
375   public String getSessionName() {
376     return myDebugUIEnvironment.getEnvironment().getSessionName();
377   }
378
379   public DebuggerStateManager getContextManager() {
380     return myStateManager;
381   }
382
383   @Nullable
384   public TextWithImports getSelectedExpression() {
385     final DebuggerSession session = myDebuggerSession;
386     if (session == null || session.getState() != DebuggerSession.STATE_PAUSED) {
387       return null;
388     }
389     JTree tree = myVariablesPanel.getFrameTree();
390     if (tree == null || !tree.hasFocus()) {
391       tree = myWatchPanel.getWatchTree();
392       if (tree == null || !tree.hasFocus()) {
393         return null;
394       }
395     }
396     TreePath path = tree.getSelectionPath();
397     if (path == null) {
398       return null;
399     }
400     DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)path.getLastPathComponent();
401     if (node == null) {
402       return null;
403     }
404     NodeDescriptorImpl descriptor = node.getDescriptor();
405     if (!(descriptor instanceof ValueDescriptorImpl)) {
406       return null;
407     }
408     if (descriptor instanceof WatchItemDescriptor) {
409       return ((WatchItemDescriptor)descriptor).getEvaluationText();
410     }
411     try {
412       return DebuggerTreeNodeExpression.createEvaluationText(node, getContextManager().getContext());
413     }
414     catch (EvaluateException e) {
415       return null;
416     }
417   }
418
419   @Nullable
420   @Override
421   protected RunProfile getRunProfile() {
422     return myDebugUIEnvironment.getRunProfile();
423   }
424
425   private void attractFramesOnPause(final int event) {
426     if (DebuggerSession.EVENT_PAUSE == event) {
427       myUi.attractBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
428     }
429     else if (DebuggerSession.EVENT_RESUME == event) {
430       myUi.clearAttractionBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
431     }
432   }
433
434   public DebuggerSession getSession() {
435     return myDebuggerSession;
436   }
437
438   public void showFramePanel() {
439     myUi.selectAndFocus(myUi.findContent(DebuggerContentInfo.FRAME_CONTENT), true, false);
440   }
441
442   private class MyDebuggerStateManager extends DebuggerStateManager {
443     @Override
444     public void fireStateChanged(DebuggerContextImpl newContext, int event) {
445       super.fireStateChanged(newContext, event);
446     }
447
448     @Override
449     public DebuggerContextImpl getContext() {
450       final DebuggerSession session = myDebuggerSession;
451       return session != null ? session.getContextManager().getContext() : DebuggerContextImpl.EMPTY_CONTEXT;
452     }
453
454     @Override
455     public void setState(DebuggerContextImpl context, int state, int event, String description) {
456       final DebuggerSession session = myDebuggerSession;
457       if (session != null) {
458         session.getContextManager().setState(context, state, event, description);
459       }
460     }
461   }
462
463   private class AutoVarsSwitchAction extends ToggleAction {
464     private volatile boolean myAutoModeEnabled;
465
466     public AutoVarsSwitchAction() {
467       super("", "", AllIcons.Debugger.AutoVariablesMode);
468       myAutoModeEnabled = DebuggerSettings.getInstance().AUTO_VARIABLES_MODE;
469     }
470
471     @Override
472     public void update(@NotNull final AnActionEvent e) {
473       super.update(e);
474       final Presentation presentation = e.getPresentation();
475       final boolean autoModeEnabled = Boolean.TRUE.equals(presentation.getClientProperty(SELECTED_PROPERTY));
476       presentation.setText(autoModeEnabled ? "All-Variables Mode" : "Auto-Variables Mode");
477     }
478
479     @Override
480     public boolean isSelected(AnActionEvent e) {
481       return myAutoModeEnabled;
482     }
483
484     @Override
485     public void setSelected(AnActionEvent e, boolean enabled) {
486       myAutoModeEnabled = enabled;
487       DebuggerSettings.getInstance().AUTO_VARIABLES_MODE = enabled;
488       myVariablesPanel.getFrameTree().setAutoVariablesMode(enabled);
489     }
490   }
491
492   private class WatchLastMethodReturnValueAction extends ToggleAction {
493     private volatile boolean myWatchesReturnValues;
494     private final String myTextEnable;
495     private final String myTextUnavailable;
496     private final String myMyTextDisable;
497
498     public WatchLastMethodReturnValueAction() {
499       super("", DebuggerBundle.message("action.watch.method.return.value.description"), null);
500       myWatchesReturnValues = DebuggerSettings.getInstance().WATCH_RETURN_VALUES;
501       myTextEnable = DebuggerBundle.message("action.watches.method.return.value.enable");
502       myMyTextDisable = DebuggerBundle.message("action.watches.method.return.value.disable");
503       myTextUnavailable = DebuggerBundle.message("action.watches.method.return.value.unavailable.reason");
504     }
505
506     @Override
507     public void update(@NotNull final AnActionEvent e) {
508       super.update(e);
509       final Presentation presentation = e.getPresentation();
510       final boolean watchValues = Boolean.TRUE.equals(presentation.getClientProperty(SELECTED_PROPERTY));
511       final DebugProcessImpl process = getDebugProcess();
512       final String actionText = watchValues ? myMyTextDisable : myTextEnable;
513       if (process != null && process.canGetMethodReturnValue()) {
514         presentation.setEnabled(true);
515         presentation.setText(actionText);
516       }
517       else {
518         presentation.setEnabled(false);
519         presentation.setText(process == null ? actionText : myTextUnavailable);
520       }
521     }
522
523     @Override
524     public boolean isSelected(AnActionEvent e) {
525       return myWatchesReturnValues;
526     }
527
528     @Override
529     public void setSelected(AnActionEvent e, boolean watch) {
530       myWatchesReturnValues = watch;
531       DebuggerSettings.getInstance().WATCH_RETURN_VALUES = watch;
532       final DebugProcessImpl process = getDebugProcess();
533       if (process != null) {
534         process.setWatchMethodReturnValuesEnabled(watch);
535       }
536     }
537   }
538 }