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.
16 package com.intellij.debugger.ui;
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.filters.ExceptionFilters;
40 import com.intellij.execution.filters.TextConsoleBuilder;
41 import com.intellij.execution.filters.TextConsoleBuilderFactory;
42 import com.intellij.execution.ui.ConsoleView;
43 import com.intellij.execution.ui.ExecutionConsoleEx;
44 import com.intellij.execution.ui.RunnerLayoutUi;
45 import com.intellij.execution.ui.layout.PlaceInGrid;
46 import com.intellij.icons.AllIcons;
47 import com.intellij.idea.ActionsBundle;
48 import com.intellij.openapi.Disposable;
49 import com.intellij.openapi.actionSystem.*;
50 import com.intellij.openapi.application.ApplicationManager;
51 import com.intellij.openapi.diagnostic.Logger;
52 import com.intellij.openapi.project.Project;
53 import com.intellij.openapi.util.Disposer;
54 import com.intellij.ui.content.AlertIcon;
55 import com.intellij.ui.content.Content;
56 import com.intellij.ui.content.ContentManagerAdapter;
57 import com.intellij.ui.content.ContentManagerEvent;
58 import com.intellij.ui.content.tabs.PinToolwindowTabAction;
59 import com.intellij.unscramble.ThreadDumpPanel;
60 import com.intellij.unscramble.ThreadState;
61 import com.intellij.xdebugger.XDebuggerBundle;
62 import com.intellij.xdebugger.impl.actions.XDebuggerActions;
63 import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager;
64 import com.intellij.xdebugger.impl.ui.DebuggerSessionTabBase;
65 import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
66 import org.jetbrains.annotations.NotNull;
67 import org.jetbrains.annotations.Nullable;
70 import javax.swing.tree.TreePath;
71 import java.util.List;
73 public class DebuggerSessionTab extends DebuggerSessionTabBase implements Disposable {
74 private static final Logger LOG = Logger.getInstance(DebuggerSessionTab.class);
76 private final VariablesPanel myVariablesPanel;
77 private final MainWatchPanel myWatchPanel;
79 private volatile DebuggerSession myDebuggerSession;
81 private final MyDebuggerStateManager myStateManager = new MyDebuggerStateManager();
83 private final FramesPanel myFramesPanel;
84 private final DebugUIEnvironment myDebugUIEnvironment;
86 private final ThreadsPanel myThreadsPanel;
87 private static final String THREAD_DUMP_CONTENT_PREFIX = "Dump";
89 public DebuggerSessionTab(final Project project, final String sessionName, @NotNull final DebugUIEnvironment environment,
90 @NotNull DebuggerSession debuggerSession) {
91 super(project, "JavaDebugger", sessionName, debuggerSession.getSearchScope());
93 myDebuggerSession = debuggerSession;
94 myDebugUIEnvironment = environment;
96 final DefaultActionGroup focus = new DefaultActionGroup();
97 focus.add(ActionManager.getInstance().getAction("Debugger.FocusOnBreakpoint"));
98 myUi.getOptions().setAdditionalFocusActions(focus);
100 final DebuggerSettings debuggerSettings = DebuggerSettings.getInstance();
101 if (!ApplicationManager.getApplication().isUnitTestMode()) {
102 getContextManager().addListener(new DebuggerContextListener() {
104 public void changeEvent(DebuggerContextImpl newContext, int event) {
106 case DebuggerSession.EVENT_DETACHED:
107 myUi.updateActionsNow();
109 if (XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings().isHideDebuggerOnProcessTermination()) {
111 ExecutionManager.getInstance(project).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor);
113 catch (NullPointerException e) {
114 //if we can get closeProcess after the project have been closed
124 DefaultActionGroup topToolbar = new DefaultActionGroup();
125 ActionManager actionManager = ActionManager.getInstance();
126 topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP));
127 topToolbar.add(actionManager.getAction(DebuggerActions.POP_FRAME), new Constraints(Anchor.AFTER, XDebuggerActions.STEP_OUT));
128 topToolbar.add(Separator.getInstance(), new Constraints(Anchor.BEFORE, DebuggerActions.POP_FRAME));
129 topToolbar.add(Separator.getInstance(), new Constraints(Anchor.AFTER, DebuggerActions.POP_FRAME));
130 myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
132 myWatchPanel = new MainWatchPanel(project, getContextManager());
133 myFramesPanel = new FramesPanel(project, getContextManager());
135 final AlertIcon breakpointAlert = new AlertIcon(AllIcons.Debugger.BreakpointAlert);
138 Content watches = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchPanel, XDebuggerBundle.message("debugger.session.tab.watches.title"),
139 AllIcons.Debugger.Watches, null);
140 watches.setCloseable(false);
141 watches.setAlertIcon(breakpointAlert);
142 myUi.addContent(watches, 0, PlaceInGrid.right, false);
145 Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, myFramesPanel, XDebuggerBundle.message("debugger.session.tab.frames.title"),
146 AllIcons.Debugger.Frame, myFramesPanel.getFramesList());
147 framesContent.setCloseable(false);
148 framesContent.setAlertIcon(breakpointAlert);
150 myUi.addContent(framesContent, 0, PlaceInGrid.left, false);
153 myVariablesPanel = new VariablesPanel(project, myStateManager, this);
154 myVariablesPanel.getFrameTree().setAutoVariablesMode(debuggerSettings.AUTO_VARIABLES_MODE);
155 Content vars = myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, myVariablesPanel, XDebuggerBundle.message("debugger.session.tab.variables.title"),
156 AllIcons.Debugger.Value, null);
157 vars.setCloseable(false);
158 vars.setAlertIcon(breakpointAlert);
159 myUi.addContent(vars, 0, PlaceInGrid.center, false);
162 myThreadsPanel = new ThreadsPanel(project, getContextManager());
163 Content threadsContent = myUi.createContent(DebuggerContentInfo.THREADS_CONTENT, myThreadsPanel, XDebuggerBundle.message("debugger.session.tab.threads.title"),
164 AllIcons.Debugger.Threads, null);
165 threadsContent.setCloseable(false);
166 //threadsContent.setAlertIcon(breakpointAlert);
168 //final DefaultActionGroup threadsGroup = new DefaultActionGroup();
169 //threadsContent.setActions(threadsGroup, ActionPlaces.DEBUGGER_TOOLBAR, threadsPanel.getThreadsTree());
171 myUi.addContent(threadsContent, 0, PlaceInGrid.left, true);
173 for (Content each : myUi.getContents()) {
177 myUi.addListener(new ContentManagerAdapter() {
179 public void selectionChanged(ContentManagerEvent event) {
180 updateStatus(event.getContent());
184 debuggerSession.getContextManager().addListener(new DebuggerContextListener() {
186 public void changeEvent(DebuggerContextImpl newContext, int event) {
187 if (!myUi.isDisposed()) {
188 attractFramesOnPause(event);
189 myStateManager.fireStateChanged(newContext, event);
190 SwingUtilities.invokeLater(new Runnable() {
193 if (!myUi.isDisposed()) {
194 myUi.updateActionsNow();
202 // ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult();
203 // myConsole = executionResult.getExecutionConsole();
204 // myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName(),
205 // environment.getIcon());
206 // initUI(executionResult);
209 private static void updateStatus(final Content content) {
210 if (content.getComponent() instanceof DebuggerView) {
211 final DebuggerView view = (DebuggerView)content.getComponent();
212 if (content.isSelected()) {
213 view.setUpdateEnabled(true);
214 if (view.isRefreshNeeded()) {
215 view.rebuildIfVisible(DebuggerSession.EVENT_CONTEXT);
219 view.setUpdateEnabled(false);
224 public MainWatchPanel getWatchPanel() {
228 private void initUI(ExecutionResult executionResult) {
229 if (ApplicationManager.getApplication().isUnitTestMode()) {
234 myUi.removeContent(myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT), true);
236 Content console = null;
237 if (myConsole instanceof ExecutionConsoleEx) {
238 ((ExecutionConsoleEx)myConsole).buildUi(myUi);
239 console = myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT);
240 if (console == null) {
241 LOG.debug("Reuse console created with non-debug runner");
244 if (console == null) {
245 console = myUi.createContent(DebuggerContentInfo.CONSOLE_CONTENT, myConsole.getComponent(),
246 XDebuggerBundle.message("debugger.session.tab.console.content.name"),
247 AllIcons.Debugger.Console, myConsole.getPreferredFocusableComponent());
249 console.setCloseable(false);
250 myUi.addContent(console, 1, PlaceInGrid.bottom, false);
252 attachNotificationTo(console);
254 if (myConsole != null) {
255 Disposer.register(this, myConsole);
258 final DefaultActionGroup consoleActions = new DefaultActionGroup();
259 if (myConsole instanceof ConsoleView) {
260 AnAction[] actions = ((ConsoleView)myConsole).createConsoleActions();
261 for (AnAction goAction : actions) {
262 consoleActions.add(goAction);
265 console.setActions(consoleActions, ActionPlaces.DEBUGGER_TOOLBAR, myConsole.getPreferredFocusableComponent());
267 // myDebugUIEnvironment.initLogs(myRunContentDescriptor, myManager);
269 DefaultActionGroup leftToolbar = new DefaultActionGroup();
271 if (executionResult instanceof DefaultExecutionResult) {
272 final AnAction[] actions = ((DefaultExecutionResult)executionResult).getRestartActions();
273 leftToolbar.addAll(actions);
274 if (actions.length > 0) {
275 leftToolbar.addSeparator();
278 final AnAction[] profileActions = executionResult.getActions();
279 leftToolbar.addAll(profileActions);
281 leftToolbar.add(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_LEFT_TOOLBAR_GROUP));
282 if (executionResult instanceof DefaultExecutionResult) {
283 AnAction[] actions = ((DefaultExecutionResult)executionResult).getAdditionalStopActions();
284 for (AnAction action : actions) {
285 leftToolbar.add(action, new Constraints(Anchor.AFTER, IdeActions.ACTION_STOP_PROGRAM));
289 leftToolbar.addSeparator();
290 addAction(leftToolbar, DebuggerActions.DUMP_THREADS);
291 leftToolbar.addSeparator();
293 leftToolbar.add(myUi.getOptions().getLayoutActions());
295 final AnAction[] commonSettings = myUi.getOptions().getSettingsActionsList();
296 final AnAction commonSettingsList = myUi.getOptions().getSettingsActions();
298 final DefaultActionGroup settings = new DefaultActionGroup("DebuggerSettings", true) {
300 public void update(AnActionEvent e) {
301 e.getPresentation().setText(ActionsBundle.message("group.XDebugger.settings.text"));
302 e.getPresentation().setIcon(commonSettingsList.getTemplatePresentation().getIcon());
306 public boolean isDumbAware() {
310 for (AnAction each : commonSettings) {
313 if (commonSettings.length > 0) {
314 settings.addSeparator();
316 settings.add(new WatchLastMethodReturnValueAction());
317 settings.add(new AutoVarsSwitchAction());
318 settings.addSeparator();
319 addActionToGroup(settings, XDebuggerActions.INLINE_DEBUGGER);
320 addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP);
321 addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP_ON_SELECTION);
323 leftToolbar.add(settings);
325 leftToolbar.addSeparator();
327 addActionToGroup(leftToolbar, PinToolwindowTabAction.ACTION_NAME);
329 myDebugUIEnvironment.initActions(myRunContentDescriptor, leftToolbar);
331 myUi.getOptions().setLeftToolbar(leftToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
334 private static void addAction(DefaultActionGroup group, String actionId) {
335 group.add(ActionManager.getInstance().getAction(actionId));
338 private static void addActionToGroup(final DefaultActionGroup group, final String actionId) {
339 AnAction action = ActionManager.getInstance().getAction(actionId);
340 if (action != null) group.add(action);
344 public void dispose() {
346 myFramesPanel.dispose();
347 myVariablesPanel.dispose();
348 myWatchPanel.dispose();
349 myThreadsPanel.dispose();
354 private void disposeSession() {
355 final DebuggerSession session = myDebuggerSession;
356 myDebuggerSession = null;
357 if (session != null) {
363 private DebugProcessImpl getDebugProcess() {
364 final DebuggerSession session = myDebuggerSession;
365 return session != null ? session.getProcess() : null;
368 public void reuse(DebuggerSessionTab reuseSession) {
369 DebuggerTreeNodeImpl[] watches = reuseSession.getWatchPanel().getWatchTree().getWatches();
371 final WatchDebuggerTree watchTree = getWatchPanel().getWatchTree();
372 for (DebuggerTreeNodeImpl watch : watches) {
373 watchTree.addWatch((WatchItemDescriptor)watch.getDescriptor());
376 DebugProcessImpl process = getDebugProcess();
377 DebugProcessImpl reuseProcess = reuseSession.getDebugProcess();
378 if (process != null && reuseProcess != null) {
379 //process.setBreakpointsMuted(reuseProcess.areBreakpointsMuted());
383 public String getSessionName() {
384 return myDebugUIEnvironment.getEnvironment().getSessionName();
387 public DebuggerStateManager getContextManager() {
388 return myStateManager;
392 public TextWithImports getSelectedExpression() {
393 final DebuggerSession session = myDebuggerSession;
394 if (session == null || session.getState() != DebuggerSession.STATE_PAUSED) {
397 JTree tree = myVariablesPanel.getFrameTree();
398 if (tree == null || !tree.hasFocus()) {
399 tree = myWatchPanel.getWatchTree();
400 if (tree == null || !tree.hasFocus()) {
404 TreePath path = tree.getSelectionPath();
408 DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)path.getLastPathComponent();
412 NodeDescriptorImpl descriptor = node.getDescriptor();
413 if (!(descriptor instanceof ValueDescriptorImpl)) {
416 if (descriptor instanceof WatchItemDescriptor) {
417 return ((WatchItemDescriptor)descriptor).getEvaluationText();
420 return DebuggerTreeNodeExpression.createEvaluationText(node, getContextManager().getContext());
422 catch (EvaluateException e) {
429 protected RunProfile getRunProfile() {
430 return myDebugUIEnvironment.getRunProfile();
433 private void attractFramesOnPause(final int event) {
434 if (DebuggerSession.EVENT_PAUSE == event) {
435 myUi.attractBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
437 else if (DebuggerSession.EVENT_RESUME == event) {
438 myUi.clearAttractionBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
442 public DebuggerSession getSession() {
443 return myDebuggerSession;
446 public void showFramePanel() {
447 myUi.selectAndFocus(myUi.findContent(DebuggerContentInfo.FRAME_CONTENT), true, false);
450 private static int myThreadDumpsCount = 0;
451 private static int myCurrentThreadDumpId = 1;
453 public static void addThreadDump(Project project, List<ThreadState> threads, final RunnerLayoutUi ui, DebuggerSession session) {
454 final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
455 consoleBuilder.filters(ExceptionFilters.getFilters(session.getSearchScope()));
456 final ConsoleView consoleView = consoleBuilder.getConsole();
457 final DefaultActionGroup toolbarActions = new DefaultActionGroup();
458 consoleView.allowHeavyFilters();
459 final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads);
461 final String id = createThreadDumpContentId();
462 final Content content = ui.createContent(id, panel, id, null, null);
463 content.setCloseable(true);
464 content.setDescription("Thread Dump");
465 ui.addContent(content);
466 ui.selectAndFocus(content, true, true);
467 myThreadDumpsCount += 1;
468 myCurrentThreadDumpId += 1;
469 //Disposer.register(this, new Disposable() {
471 // public void dispose() {
472 // ui.removeContent(content, true);
475 Disposer.register(content, new Disposable() {
477 public void dispose() {
478 myThreadDumpsCount -= 1;
479 if (myThreadDumpsCount == 0) {
480 myCurrentThreadDumpId = 1;
484 Disposer.register(content, consoleView);
485 ui.selectAndFocus(content, true, false);
486 if (threads.size() > 0) {
487 panel.selectStackFrame(0);
491 private static String createThreadDumpContentId() {
492 return THREAD_DUMP_CONTENT_PREFIX + " #" + myCurrentThreadDumpId;
495 private class MyDebuggerStateManager extends DebuggerStateManager {
497 public void fireStateChanged(DebuggerContextImpl newContext, int event) {
498 super.fireStateChanged(newContext, event);
502 public DebuggerContextImpl getContext() {
503 final DebuggerSession session = myDebuggerSession;
504 return session != null ? session.getContextManager().getContext() : DebuggerContextImpl.EMPTY_CONTEXT;
508 public void setState(DebuggerContextImpl context, int state, int event, String description) {
509 final DebuggerSession session = myDebuggerSession;
510 if (session != null) {
511 session.getContextManager().setState(context, state, event, description);
516 private class AutoVarsSwitchAction extends ToggleAction {
517 private volatile boolean myAutoModeEnabled;
519 public AutoVarsSwitchAction() {
520 super("", "", AllIcons.Debugger.AutoVariablesMode);
521 myAutoModeEnabled = DebuggerSettings.getInstance().AUTO_VARIABLES_MODE;
525 public void update(@NotNull final AnActionEvent e) {
527 final Presentation presentation = e.getPresentation();
528 final boolean autoModeEnabled = Boolean.TRUE.equals(presentation.getClientProperty(SELECTED_PROPERTY));
529 presentation.setText(autoModeEnabled ? "All-Variables Mode" : "Auto-Variables Mode");
533 public boolean isSelected(AnActionEvent e) {
534 return myAutoModeEnabled;
538 public void setSelected(AnActionEvent e, boolean enabled) {
539 myAutoModeEnabled = enabled;
540 DebuggerSettings.getInstance().AUTO_VARIABLES_MODE = enabled;
541 myVariablesPanel.getFrameTree().setAutoVariablesMode(enabled);
545 private class WatchLastMethodReturnValueAction extends ToggleAction {
546 private volatile boolean myWatchesReturnValues;
547 private final String myTextEnable;
548 private final String myTextUnavailable;
549 private final String myMyTextDisable;
551 public WatchLastMethodReturnValueAction() {
552 super("", DebuggerBundle.message("action.watch.method.return.value.description"), null);
553 myWatchesReturnValues = DebuggerSettings.getInstance().WATCH_RETURN_VALUES;
554 myTextEnable = DebuggerBundle.message("action.watches.method.return.value.enable");
555 myMyTextDisable = DebuggerBundle.message("action.watches.method.return.value.disable");
556 myTextUnavailable = DebuggerBundle.message("action.watches.method.return.value.unavailable.reason");
560 public void update(@NotNull final AnActionEvent e) {
562 final Presentation presentation = e.getPresentation();
563 final boolean watchValues = Boolean.TRUE.equals(presentation.getClientProperty(SELECTED_PROPERTY));
564 final DebugProcessImpl process = getDebugProcess();
565 final String actionText = watchValues ? myMyTextDisable : myTextEnable;
566 if (process != null && process.canGetMethodReturnValue()) {
567 presentation.setEnabled(true);
568 presentation.setText(actionText);
571 presentation.setEnabled(false);
572 presentation.setText(process == null ? actionText : myTextUnavailable);
577 public boolean isSelected(AnActionEvent e) {
578 return myWatchesReturnValues;
582 public void setSelected(AnActionEvent e, boolean watch) {
583 myWatchesReturnValues = watch;
584 DebuggerSettings.getInstance().WATCH_RETURN_VALUES = watch;
585 final DebugProcessImpl process = getDebugProcess();
586 if (process != null) {
587 process.setWatchMethodReturnValuesEnabled(watch);