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)));
65 final PsiMethodListPopupStep popupStep
= new PsiMethodListPopupStep(methods
, new PsiMethodListPopupStep
.OnChooseRunnable() {
66 public void execute(PsiMethod chosenMethod
) {
67 session
.stepInto(true, createSmartStepFilter(chosenMethod
));
70 final ListPopup popup
= JBPopupFactory
.getInstance().createListPopup(popupStep
);
71 final RelativePoint point
= DebuggerUIUtil
.calcPopupLocation(((TextEditor
)fileEditor
).getEditor(), position
.getLine());
77 session
.stepInto(true, null);
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();
89 return Collections
.emptyList(); // the document has been changed
92 final PsiFile file
= position
.getFile();
93 final VirtualFile vFile
= file
.getVirtualFile();
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
)) {
107 final PsiElement parent
= element
.getParent();
108 if (parent
== null || (parent
.getTextOffset() < lineRange
.getStartOffset())) {
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())) {
139 sibling
.accept(methodCollector
);
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
;