fix assertion; SmartStep makes sense in sources only, not in compiled code
[fedora-idea.git] / java / debugger / impl / src / com / intellij / debugger / actions / SmartStepIntoActionHandler.java
blobd656c07121245b42f3d1d557f6913b72177c84d4
1 /*
2 * Copyright 2000-2009 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.actions;
18 import com.intellij.debugger.DebuggerManagerEx;
19 import com.intellij.debugger.SourcePosition;
20 import com.intellij.debugger.engine.RequestHint;
21 import com.intellij.debugger.engine.SuspendContextImpl;
22 import com.intellij.debugger.impl.DebuggerContextImpl;
23 import com.intellij.debugger.impl.DebuggerSession;
24 import com.intellij.openapi.actionSystem.AnActionEvent;
25 import com.intellij.openapi.editor.Document;
26 import com.intellij.openapi.fileEditor.FileDocumentManager;
27 import com.intellij.openapi.fileEditor.FileEditor;
28 import com.intellij.openapi.fileEditor.FileEditorManager;
29 import com.intellij.openapi.fileEditor.TextEditor;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.ui.popup.JBPopupFactory;
32 import com.intellij.openapi.ui.popup.ListPopup;
33 import com.intellij.openapi.util.TextRange;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import com.intellij.psi.*;
36 import com.intellij.ui.awt.RelativePoint;
37 import com.intellij.util.containers.OrderedSet;
38 import com.intellij.util.text.CharArrayUtil;
39 import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
40 import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
41 import gnu.trove.TObjectHashingStrategy;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
45 import java.util.Collections;
46 import java.util.List;
48 public class SmartStepIntoActionHandler extends DebuggerActionHandler {
49 public void perform(@NotNull final Project project, final AnActionEvent event) {
50 final DebuggerContextImpl debuggerContext = (DebuggerManagerEx.getInstanceEx(project)).getContext();
51 doStep(project, debuggerContext.getSourcePosition(), debuggerContext.getDebuggerSession());
55 private static void doStep(final @NotNull Project project, final @Nullable SourcePosition position, final @NotNull DebuggerSession session) {
56 final VirtualFile file = position != null ? position.getFile().getVirtualFile() : null;
57 final FileEditor fileEditor = file != null? FileEditorManager.getInstance(project).getSelectedEditor(file) : null;
58 if (fileEditor instanceof TextEditor) {
59 final List<PsiMethod> methods = findReferencedMethods(position);
60 if (methods.size() > 0) {
61 if (methods.size() == 1) {
62 session.stepInto(true, createSmartStepFilter(methods.get(0)));
64 else {
65 final PsiMethodListPopupStep popupStep = new PsiMethodListPopupStep(methods, new PsiMethodListPopupStep.OnChooseRunnable() {
66 public void execute(PsiMethod chosenMethod) {
67 session.stepInto(true, createSmartStepFilter(chosenMethod));
69 });
70 final ListPopup popup = JBPopupFactory.getInstance().createListPopup(popupStep);
71 final RelativePoint point = DebuggerUIUtil.calcPopupLocation(((TextEditor)fileEditor).getEditor(), position.getLine());
72 popup.show(point);
74 return;
77 session.stepInto(true, null);
80 @Nullable
81 private static RequestHint.SmartStepFilter createSmartStepFilter(final PsiMethod method) {
82 return new RequestHint.SmartStepFilter(method);
86 private static List<PsiMethod> findReferencedMethods(final SourcePosition position) {
87 final int line = position.getLine();
88 if (line < 0) {
89 return Collections.emptyList(); // the document has been changed
92 final PsiFile file = position.getFile();
93 final VirtualFile vFile = file.getVirtualFile();
94 if (vFile == null) {
95 // the file is not physical
96 return Collections.emptyList();
99 final Document doc = FileDocumentManager.getInstance().getDocument(vFile);
101 final int startOffset = doc.getLineStartOffset(line);
102 final TextRange lineRange = new TextRange(startOffset, doc.getLineEndOffset(line));
103 final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), startOffset, " \t");
104 PsiElement element = file.findElementAt(offset);
105 if (element != null && !(element instanceof PsiCompiledElement)) {
106 do {
107 final PsiElement parent = element.getParent();
108 if (parent == null || (parent.getTextOffset() < lineRange.getStartOffset())) {
109 break;
111 element = parent;
113 while(true);
115 //noinspection unchecked
116 final List<PsiMethod> methods = new OrderedSet<PsiMethod>(TObjectHashingStrategy.CANONICAL);
117 final PsiElementVisitor methodCollector = new JavaRecursiveElementWalkingVisitor() {
118 @Override public void visitAnonymousClass(PsiAnonymousClass aClass) { /*skip annonymous classes*/ }
120 @Override public void visitStatement(PsiStatement statement) {
121 if (lineRange.intersects(statement.getTextRange())) {
122 super.visitStatement(statement);
126 @Override public void visitCallExpression(final PsiCallExpression expression) {
127 final PsiMethod psiMethod = expression.resolveMethod();
128 if (psiMethod != null) {
129 methods.add(psiMethod);
131 super.visitCallExpression(expression);
134 element.accept(methodCollector);
135 for (PsiElement sibling = element.getNextSibling(); sibling != null; sibling = sibling.getNextSibling()) {
136 if (!lineRange.intersects(sibling.getTextRange())) {
137 break;
139 sibling.accept(methodCollector);
141 return methods;
143 return Collections.emptyList();
146 public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
147 final DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(project)).getContext();
148 DebuggerSession debuggerSession = context.getDebuggerSession();
149 final boolean isPaused = debuggerSession != null && debuggerSession.isPaused();
150 final SuspendContextImpl suspendContext = context.getSuspendContext();
151 final boolean hasCurrentThread = suspendContext != null && suspendContext.getThread() != null;
152 return isPaused && hasCurrentThread;