IDEA-70251 smart step into displays in popup methods that have already been executed

This commit is contained in:
Egor.Ushakov
2016-07-14 14:25:52 +03:00
parent 2d4e6efb30
commit 243003c0b8
5 changed files with 123 additions and 18 deletions
@@ -16,19 +16,34 @@
package com.intellij.debugger.actions;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.MethodBytecodeUtil;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.DocumentUtil;
import com.intellij.util.Range;
import com.intellij.util.containers.OrderedSet;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import com.sun.jdi.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.util.Collections;
import java.util.List;
@@ -39,6 +54,8 @@ import java.util.Stack;
* Date: 22.11.11
*/
public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
private static final Logger LOG = Logger.getInstance(JavaSmartStepIntoHandler.class);
@Override
public boolean isAvailable(final SourcePosition position) {
final PsiFile file = position.getFile();
@@ -46,8 +63,37 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
}
@Override
public boolean doSmartStep(SourcePosition position, DebuggerSession session, TextEditor fileEditor) {
session.getProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(session.getContextManager().getContext()) {
@Override
public void threadAction(@NotNull SuspendContextImpl suspendContext) {
List<SmartStepTarget> targets = ApplicationManager.getApplication().runReadAction(
(Computable<List<SmartStepTarget>>)() -> findSmartStepTargets(position, suspendContext));
DebuggerUIUtil.invokeLater(() -> {
if (targets.isEmpty()) {
doStepInto(session, Registry.is("debugger.single.smart.step.force"), null);
}
else {
handleTargets(position, session, fileEditor, targets);
}
});
}
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
});
return true;
}
@NotNull
public List<SmartStepTarget> findSmartStepTargets(final SourcePosition position) {
@Override
public List<SmartStepTarget> findSmartStepTargets(SourcePosition position) {
throw new IllegalStateException("Should not be used");
}
protected List<SmartStepTarget> findSmartStepTargets(final SourcePosition position, @Nullable SuspendContextImpl suspendContext) {
final int line = position.getLine();
if (line < 0) {
return Collections.emptyList(); // the document has been changed
@@ -82,7 +128,7 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
}
element = parent;
}
while(true);
while (true);
//noinspection unchecked
final List<SmartStepTarget> targets = new OrderedSet<>();
@@ -222,10 +268,42 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
Range<Integer> lines =
new Range<>(doc.getLineNumber(textRange.get().getStartOffset()), doc.getLineNumber(textRange.get().getEndOffset()));
for (SmartStepTarget target : targets) {
target.setCallingExpressionLines(lines);
targets.forEach(t -> t.setCallingExpressionLines(lines));
if (!targets.isEmpty()) {
StackFrameProxyImpl frameProxy = suspendContext != null ? suspendContext.getFrameProxy() : null;
if (frameProxy != null) {
try {
Location location = frameProxy.location();
MethodBytecodeUtil.visit(location.declaringType(), location.method(), location.codeIndex(), new MethodVisitor(Opcodes.ASM5) {
boolean myLineMatch = false;
@Override
public void visitLineNumber(int line, Label start) {
myLineMatch = lines.isWithin(line - 1);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (myLineMatch) {
targets.removeIf(t -> {
if (t instanceof MethodSmartStepTarget) {
return DebuggerUtilsEx.methodMatches(((MethodSmartStepTarget)t).getMethod(),
owner.replace("/", "."), name, desc, suspendContext.getDebugProcess());
}
return false;
});
}
}
});
}
catch (Exception e) {
LOG.info(e);
}
}
return targets;
}
return targets;
}
return Collections.emptyList();
}
@@ -17,6 +17,7 @@ package com.intellij.debugger.actions;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.MethodFilter;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
@@ -51,8 +52,12 @@ public class JvmSmartStepIntoActionHandler extends DebuggerActionHandler {
}
}
}
doStepInto(session, Registry.is("debugger.single.smart.step.force"), null);
}
static void doStepInto(DebuggerSession session, boolean force, MethodFilter filter) {
session.sessionResumed();
session.stepInto(Registry.is("debugger.single.smart.step.force"), null);
session.stepInto(force, filter);
}
public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
@@ -57,21 +57,22 @@ public abstract class JvmSmartStepIntoHandler {
* @return false to continue for another handler or for default action (step into)
*/
public boolean doSmartStep(SourcePosition position, final DebuggerSession session, TextEditor fileEditor) {
final List<SmartStepTarget> targets = findSmartStepTargets(position);
return handleTargets(position, session, fileEditor, findSmartStepTargets(position));
}
protected final boolean handleTargets(SourcePosition position,
DebuggerSession session,
TextEditor fileEditor,
List<SmartStepTarget> targets) {
if (!targets.isEmpty()) {
final SmartStepTarget firstTarget = targets.get(0);
SmartStepTarget firstTarget = targets.get(0);
if (targets.size() == 1) {
session.sessionResumed();
session.stepInto(Registry.is("debugger.single.smart.step.force"), createMethodFilter(firstTarget));
doStepInto(session, Registry.is("debugger.single.smart.step.force"), firstTarget);
}
else {
final Editor editor = fileEditor.getEditor();
final PsiMethodListPopupStep popupStep = new PsiMethodListPopupStep(editor, targets, new PsiMethodListPopupStep.OnChooseRunnable() {
public void execute(SmartStepTarget chosenTarget) {
session.sessionResumed();
session.stepInto(true, createMethodFilter(chosenTarget));
}
});
Editor editor = fileEditor.getEditor();
PsiMethodListPopupStep popupStep =
new PsiMethodListPopupStep(editor, targets, chosenTarget -> doStepInto(session, true, chosenTarget));
ListPopupImpl popup = new ListPopupImpl(popupStep);
DebuggerUIUtil.registerExtraHandleShortcuts(popup, XDebuggerActions.STEP_INTO, XDebuggerActions.SMART_STEP_INTO);
popup.setAdText(DebuggerUIUtil.getSelectionShortcutsAdText(XDebuggerActions.STEP_INTO, XDebuggerActions.SMART_STEP_INTO));
@@ -95,6 +96,10 @@ public abstract class JvmSmartStepIntoHandler {
return false;
}
protected void doStepInto(DebuggerSession session, boolean force, SmartStepTarget target) {
JvmSmartStepIntoActionHandler.doStepInto(session, force, createMethodFilter(target));
}
private static void highlightTarget(PsiMethodListPopupStep popupStep, SmartStepTarget target) {
final PsiElement highlightElement = target.getHighlightElement();
if (highlightElement != null) {
@@ -918,6 +918,23 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils {
return false;
}
public static boolean methodMatches(@NotNull PsiMethod psiMethod,
String className,
String name,
String signature,
DebugProcessImpl process) {
PsiClass containingClass = psiMethod.getContainingClass();
try {
return containingClass != null && Objects.equals(containingClass.getQualifiedName(), className) &&
JVMNameUtil.getJVMMethodName(psiMethod).equals(name) &&
JVMNameUtil.getJVMSignature(psiMethod).getName(process).equals(signature);
}
catch (EvaluateException e) {
LOG.debug(e);
return false;
}
}
@Nullable
public static PsiElement getContainingMethod(@Nullable PsiElement elem) {
return PsiTreeUtil.getContextOfType(elem, PsiMethod.class, PsiLambdaExpression.class, PsiClassInitializer.class);
+1 -1
View File
@@ -1616,7 +1616,7 @@
<xdebugger.breakpointType implementation="com.intellij.debugger.ui.breakpoints.JavaFieldBreakpointType"/>
<xdebugger.breakpointType implementation="com.intellij.debugger.ui.breakpoints.JavaExceptionBreakpointType"/>
<xdebugger.breakpointType implementation="com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType"/>
<debugger.jvmSmartStepIntoHandler implementation="com.intellij.debugger.actions.JavaSmartStepIntoHandler"/>
<debugger.jvmSmartStepIntoHandler implementation="com.intellij.debugger.actions.JavaSmartStepIntoHandler" order="last"/>
<debugger.simplePropertyGetterProvider implementation="com.intellij.debugger.engine.JavaSimpleGetterProvider"/>
<debugger.syntheticProvider implementation="com.intellij.debugger.engine.DefaultSyntheticProvider"/>
<debugger.sourcePositionProvider implementation="com.intellij.debugger.engine.DefaultSourcePositionProvider" order="last"/>