fixed add stepping filter action for threads view
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / actions / PopFrameAction.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
17 /*
18  * @author Eugene Zhuravlev
19  */
20 package com.intellij.debugger.actions;
21
22 import com.intellij.CommonBundle;
23 import com.intellij.debugger.DebuggerBundle;
24 import com.intellij.debugger.SourcePosition;
25 import com.intellij.debugger.engine.DebugProcessImpl;
26 import com.intellij.debugger.engine.JavaStackFrame;
27 import com.intellij.debugger.engine.SuspendContextImpl;
28 import com.intellij.debugger.impl.DebuggerContextImpl;
29 import com.intellij.debugger.jdi.StackFrameProxyImpl;
30 import com.intellij.debugger.settings.DebuggerSettings;
31 import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
32 import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
33 import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
34 import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
35 import com.intellij.idea.ActionsBundle;
36 import com.intellij.openapi.actionSystem.ActionPlaces;
37 import com.intellij.openapi.actionSystem.AnActionEvent;
38 import com.intellij.openapi.actionSystem.CommonDataKeys;
39 import com.intellij.openapi.application.ApplicationManager;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.ui.DialogWrapper;
42 import com.intellij.openapi.ui.MessageDialogBuilder;
43 import com.intellij.openapi.ui.Messages;
44 import com.intellij.psi.PsiCodeBlock;
45 import com.intellij.psi.PsiElement;
46 import com.intellij.psi.PsiStatement;
47 import com.intellij.psi.PsiTryStatement;
48 import com.intellij.psi.util.PsiTreeUtil;
49 import com.intellij.util.containers.ContainerUtil;
50 import com.intellij.util.ui.UIUtil;
51 import com.intellij.xdebugger.XDebugSession;
52 import com.intellij.xdebugger.XDebuggerBundle;
53 import com.intellij.xdebugger.XDebuggerManager;
54 import com.intellij.xdebugger.evaluation.EvaluationMode;
55 import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
56 import com.intellij.xdebugger.frame.XStackFrame;
57 import com.intellij.xdebugger.frame.XValue;
58 import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl;
59 import com.sun.jdi.InvalidStackFrameException;
60 import com.sun.jdi.NativeMethodException;
61 import com.sun.jdi.VMDisconnectedException;
62 import org.jetbrains.annotations.NotNull;
63 import org.jetbrains.annotations.Nullable;
64
65 import java.util.ArrayList;
66 import java.util.Collections;
67 import java.util.List;
68
69 public class PopFrameAction extends DebuggerAction {
70   public void actionPerformed(@NotNull AnActionEvent e) {
71     final Project project = e.getData(CommonDataKeys.PROJECT);
72     final JavaStackFrame stackFrame = getStackFrame(e);
73     if(stackFrame == null) {
74       return;
75     }
76     try {
77       final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
78       final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
79       if(debugProcess == null) {
80         return;
81       }
82
83       debugProcess.getSession().setSteppingThrough(stackFrame.getStackFrameProxy().threadProxy());
84       if (!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) {
85         List<PsiStatement> statements = getFinallyStatements(debuggerContext.getSourcePosition());
86         if (!statements.isEmpty()) {
87           StringBuilder sb = new StringBuilder();
88           for (PsiStatement statement : statements) {
89             sb.append("\n").append(statement.getText());
90           }
91           if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) {
92             evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb);
93             return;
94           }
95           else {
96             int res = MessageDialogBuilder
97               .yesNoCancel(UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
98                            DebuggerBundle.message("warning.finally.block.detected") + sb)
99               .project(project)
100               .icon(Messages.getWarningIcon())
101               .yesText(DebuggerBundle.message("button.execute.finally"))
102               .noText(DebuggerBundle.message("button.drop.anyway"))
103               .cancelText(CommonBundle.message("button.cancel"))
104               .doNotAsk(
105                 new DialogWrapper.DoNotAskOption() {
106                   @Override
107                   public boolean isToBeShown() {
108                     return !DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME) &&
109                            !DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME);
110                   }
111
112                   @Override
113                   public void setToBeShown(boolean value, int exitCode) {
114                     if (!value) {
115                       DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME =
116                         exitCode == Messages.YES ? DebuggerSettings.EVALUATE_FINALLY_ALWAYS : DebuggerSettings.EVALUATE_FINALLY_NEVER;
117                     }
118                     else {
119                       DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK;
120                     }
121                   }
122
123                   @Override
124                   public boolean canBeHidden() {
125                     return true;
126                   }
127
128                   @Override
129                   public boolean shouldSaveOptionsOnCancel() {
130                     return false;
131                   }
132
133                   @NotNull
134                   @Override
135                   public String getDoNotShowMessage() {
136                     return CommonBundle.message("dialog.options.do.not.show");
137                   }
138                 })
139               .show();
140
141             switch (res) {
142               case Messages.CANCEL:
143                 return;
144               case Messages.NO:
145                 break;
146               case Messages.YES: // evaluate finally
147                 evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb);
148                 return;
149             }
150           }
151         }
152       }
153       debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy()));
154     }
155     catch (NativeMethodException e2){
156       Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"),
157                                  UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon());
158     }
159     catch (InvalidStackFrameException ignored) {
160     }
161     catch(VMDisconnectedException ignored) {
162     }
163   }
164
165   private static void evaluateAndPop(final Project project,
166                                      final JavaStackFrame stackFrame,
167                                      final DebuggerContextImpl debuggerContext,
168                                      final DebugProcessImpl debugProcess, StringBuilder sb) {
169     XDebuggerEvaluator evaluator = stackFrame.getEvaluator();
170     if (evaluator != null) {
171       evaluator.evaluate(XExpressionImpl.fromText(sb.toString(), EvaluationMode.CODE_FRAGMENT),
172                          new XDebuggerEvaluator.XEvaluationCallback() {
173                            @Override
174                            public void evaluated(@NotNull XValue result) {
175                              debugProcess.getManagerThread()
176                                .schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy()));
177                            }
178
179                            @Override
180                            public void errorOccurred(@NotNull final String errorMessage) {
181                              ApplicationManager.getApplication().invokeLater(new Runnable() {
182                                @Override
183                                public void run() {
184                                  Messages
185                                    .showMessageDialog(project, DebuggerBundle.message("error.executing.finally", errorMessage),
186                                                       UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
187                                                       Messages.getErrorIcon());
188                                }
189                              });
190                            }
191                          }, stackFrame.getSourcePosition());
192     }
193     else {
194       Messages.showMessageDialog(project, XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator"),
195                                  UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
196                                  Messages.getErrorIcon());
197     }
198   }
199
200   private static List<PsiStatement> getFinallyStatements(@Nullable SourcePosition position) {
201     if (position == null) {
202       return Collections.emptyList();
203     }
204     List<PsiStatement> res = new ArrayList<PsiStatement>();
205     PsiElement element = position.getFile().findElementAt(position.getOffset());
206     PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class);
207     while (tryStatement != null) {
208       PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
209       if (finallyBlock != null) {
210         ContainerUtil.addAll(res, finallyBlock.getStatements());
211       }
212       tryStatement = PsiTreeUtil.getParentOfType(tryStatement, PsiTryStatement.class);
213     }
214     return res;
215   }
216
217   private static JavaStackFrame getStackFrame(AnActionEvent e) {
218     StackFrameDescriptorImpl descriptor = getSelectedStackFrameDescriptor(e);
219     if (descriptor != null) {
220       if (descriptor.getFrameProxy().isBottom()) {
221         return null;
222       }
223       return new JavaStackFrame(descriptor, false);
224     }
225     return getSelectedStackFrame(e);
226   }
227
228   private static StackFrameProxyImpl getStackFrameProxy(AnActionEvent e) {
229     StackFrameDescriptorImpl descriptor = getSelectedStackFrameDescriptor(e);
230     if (descriptor != null) {
231       if (descriptor.getFrameProxy().isBottom()) {
232         return null;
233       }
234       return descriptor.getFrameProxy();
235     }
236     else {
237       JavaStackFrame stackFrame = getSelectedStackFrame(e);
238       if (stackFrame != null) {
239         return stackFrame.getStackFrameProxy();
240       }
241     }
242     return null;
243   }
244
245   @Nullable
246   static StackFrameDescriptorImpl getSelectedStackFrameDescriptor(AnActionEvent e) {
247     DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
248     if(selectedNode != null) {
249       NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
250       if(descriptor instanceof StackFrameDescriptorImpl) {
251         return (StackFrameDescriptorImpl)descriptor;
252         //if(selectedNode.getNextSibling() != null) {
253         //  return (StackFrameDescriptorImpl)descriptor;
254         //}
255       }
256     }
257     return null;
258   }
259
260   @Nullable
261   private static JavaStackFrame getSelectedStackFrame(AnActionEvent e) {
262     Project project = e.getProject();
263     if (project != null) {
264       XDebugSession session = e.getData(XDebugSession.DATA_KEY);
265       if (session == null) {
266         session = XDebuggerManager.getInstance(project).getCurrentSession();
267       }
268       if (session != null) {
269         XStackFrame frame = session.getCurrentStackFrame();
270         if (frame instanceof JavaStackFrame) {
271           StackFrameProxyImpl proxy = ((JavaStackFrame)frame).getStackFrameProxy();
272           return !proxy.isBottom() ? ((JavaStackFrame)frame) : null;
273         }
274       }
275     }
276
277     //DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
278     //StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy();
279     //
280     //if(frameProxy == null || frameProxy.isBottom()) {
281     //  return null;
282     //}
283     //return frameProxy;
284     return null;
285   }
286
287   private static boolean isAtBreakpoint(AnActionEvent e) {
288     DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
289     if(selectedNode != null && selectedNode.getDescriptor() instanceof StackFrameDescriptorImpl) {
290       DebuggerTreeNodeImpl parent = selectedNode.getParent();
291       if(parent != null) {
292         return ((ThreadDescriptorImpl)parent.getDescriptor()).isAtBreakpoint();
293       }
294     }
295     DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
296     SuspendContextImpl suspendContext = debuggerContext.getSuspendContext();
297     return suspendContext != null && debuggerContext.getThreadProxy() == suspendContext.getThread();
298   }
299
300   public void update(@NotNull AnActionEvent e) {
301     boolean enable = false;
302
303     StackFrameProxyImpl proxy = getStackFrameProxy(e);
304     if (proxy != null && isAtBreakpoint(e)) {
305       enable = proxy.getVirtualMachine().canPopFrames();
306     }
307
308     if(ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) {
309       e.getPresentation().setEnabled(enable);
310     }
311     else {
312       e.getPresentation().setVisible(enable);
313     }
314   }
315 }