mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
lambda support in smart step into
This commit is contained in:
@@ -44,7 +44,7 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<StepTarget> findSmartStepTargets(final SourcePosition position) {
|
||||
public List<SmartStepTarget> findSmartStepTargets(final SourcePosition position) {
|
||||
final int line = position.getLine();
|
||||
if (line < 0) {
|
||||
return Collections.emptyList(); // the document has been changed
|
||||
@@ -77,11 +77,12 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
|
||||
while(true);
|
||||
|
||||
//noinspection unchecked
|
||||
final List<StepTarget> targets = new OrderedSet<StepTarget>();
|
||||
final List<SmartStepTarget> targets = new OrderedSet<SmartStepTarget>();
|
||||
|
||||
final PsiElementVisitor methodCollector = new JavaRecursiveElementVisitor() {
|
||||
final Stack<PsiMethod> myContextStack = new Stack<PsiMethod>();
|
||||
final Stack<String> myParamNameStack = new Stack<String>();
|
||||
private int myNextLambdaExpressionOrdinal = 0;
|
||||
|
||||
@Nullable
|
||||
private String getCurrentParamName() {
|
||||
@@ -91,10 +92,14 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
|
||||
@Override
|
||||
public void visitAnonymousClass(PsiAnonymousClass aClass) {
|
||||
for (PsiMethod psiMethod : aClass.getMethods()) {
|
||||
targets.add(new StepTarget(psiMethod, getCurrentParamName(), psiMethod.getBody(), true));
|
||||
targets.add(new MethodSmartStepTarget(psiMethod, getCurrentParamName(), psiMethod.getBody(), true));
|
||||
}
|
||||
}
|
||||
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {
|
||||
targets.add(new LambdaSmartStepTarget(expression, getCurrentParamName(), expression.getBody(), myNextLambdaExpressionOrdinal++));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStatement(PsiStatement statement) {
|
||||
if (lineRange.intersects(statement.getTextRange())) {
|
||||
@@ -130,7 +135,7 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
|
||||
final PsiMethod psiMethod = expression.resolveMethod();
|
||||
if (psiMethod != null) {
|
||||
myContextStack.push(psiMethod);
|
||||
targets.add(new StepTarget(
|
||||
targets.add(new MethodSmartStepTarget(
|
||||
psiMethod,
|
||||
null,
|
||||
expression instanceof PsiMethodCallExpression?
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
package com.intellij.debugger.actions;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.AnonymousClassMethodFilter;
|
||||
import com.intellij.debugger.engine.BasicStepMethodFilter;
|
||||
import com.intellij.debugger.engine.LambdaMethodFilter;
|
||||
import com.intellij.debugger.engine.MethodFilter;
|
||||
import com.intellij.debugger.impl.DebuggerSession;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -44,68 +47,10 @@ public abstract class JvmSmartStepIntoHandler {
|
||||
public static ExtensionPointName<JvmSmartStepIntoHandler> EP_NAME = ExtensionPointName.create("com.intellij.debugger.jvmSmartStepIntoHandler");
|
||||
|
||||
@NotNull
|
||||
public abstract List<StepTarget> findSmartStepTargets(SourcePosition position);
|
||||
public abstract List<SmartStepTarget> findSmartStepTargets(SourcePosition position);
|
||||
|
||||
public abstract boolean isAvailable(SourcePosition position);
|
||||
|
||||
public class StepTarget {
|
||||
private final PsiMethod myMethod;
|
||||
private final PsiElement myHighlightElement;
|
||||
private final String myLabel;
|
||||
private final boolean myNeedBreakpointRequest;
|
||||
|
||||
public StepTarget(@NotNull PsiMethod method) {
|
||||
this(method, null, null, false);
|
||||
}
|
||||
|
||||
public StepTarget(@NotNull PsiMethod method, @Nullable String additionalLabel, @Nullable PsiElement highlightElement, boolean needBreakpointRequest) {
|
||||
myMethod = method;
|
||||
myHighlightElement = highlightElement;
|
||||
myLabel = additionalLabel;
|
||||
myNeedBreakpointRequest = needBreakpointRequest;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement getHighlightElement() {
|
||||
return myHighlightElement;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMethodLabel() {
|
||||
return myLabel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiMethod getMethod() {
|
||||
return myMethod;
|
||||
}
|
||||
|
||||
public boolean needsBreakpointRequest() {
|
||||
return myNeedBreakpointRequest;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StepTarget that = (StepTarget)o;
|
||||
|
||||
if (!myMethod.equals(that.myMethod)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return myMethod.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this if you haven't PsiMethod, like in Kotlin.
|
||||
* @param position
|
||||
@@ -114,16 +59,16 @@ 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<StepTarget> targets = findSmartStepTargets(position);
|
||||
final List<SmartStepTarget> targets = findSmartStepTargets(position);
|
||||
if (!targets.isEmpty()) {
|
||||
final StepTarget firstTarget = targets.get(0);
|
||||
final SmartStepTarget firstTarget = targets.get(0);
|
||||
if (targets.size() == 1) {
|
||||
session.stepInto(true, createMethodFilter(firstTarget));
|
||||
}
|
||||
else {
|
||||
final Editor editor = fileEditor.getEditor();
|
||||
final PsiMethodListPopupStep popupStep = new PsiMethodListPopupStep(editor, targets, new PsiMethodListPopupStep.OnChooseRunnable() {
|
||||
public void execute(StepTarget chosenTarget) {
|
||||
public void execute(SmartStepTarget chosenTarget) {
|
||||
session.stepInto(true, createMethodFilter(chosenTarget));
|
||||
}
|
||||
});
|
||||
@@ -132,7 +77,7 @@ public abstract class JvmSmartStepIntoHandler {
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
popupStep.getScopeHighlighter().dropHighlight();
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
final StepTarget selectedTarget = (StepTarget)((JBList)e.getSource()).getSelectedValue();
|
||||
final SmartStepTarget selectedTarget = (SmartStepTarget)((JBList)e.getSource()).getSelectedValue();
|
||||
if (selectedTarget != null) {
|
||||
highlightTarget(popupStep, selectedTarget);
|
||||
}
|
||||
@@ -148,7 +93,7 @@ public abstract class JvmSmartStepIntoHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void highlightTarget(PsiMethodListPopupStep popupStep, StepTarget target) {
|
||||
private static void highlightTarget(PsiMethodListPopupStep popupStep, SmartStepTarget target) {
|
||||
final PsiElement highlightElement = target.getHighlightElement();
|
||||
if (highlightElement != null) {
|
||||
popupStep.getScopeHighlighter().highlight(highlightElement, Arrays.asList(highlightElement));
|
||||
@@ -161,7 +106,16 @@ public abstract class JvmSmartStepIntoHandler {
|
||||
* @param stepTarget
|
||||
* @return SmartStepFilter
|
||||
*/
|
||||
protected MethodFilter createMethodFilter(StepTarget stepTarget) {
|
||||
return new MethodFilter(stepTarget.getMethod(), stepTarget.needsBreakpointRequest());
|
||||
@Nullable
|
||||
protected MethodFilter createMethodFilter(SmartStepTarget stepTarget) {
|
||||
if (stepTarget instanceof MethodSmartStepTarget) {
|
||||
final PsiMethod method = ((MethodSmartStepTarget)stepTarget).getMethod();
|
||||
return stepTarget.needsBreakpointRequest()? new AnonymousClassMethodFilter(method) : new BasicStepMethodFilter(method);
|
||||
}
|
||||
if (stepTarget instanceof LambdaSmartStepTarget) {
|
||||
final LambdaSmartStepTarget lambdaTarget = (LambdaSmartStepTarget)stepTarget;
|
||||
return new LambdaMethodFilter(lambdaTarget.getLambda(), lambdaTarget.getOrdinal());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.actions;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiLambdaExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/25/13
|
||||
*/
|
||||
public class LambdaSmartStepTarget extends SmartStepTarget{
|
||||
private final PsiLambdaExpression myLambda;
|
||||
private final int myOrdinal;
|
||||
|
||||
public LambdaSmartStepTarget(@NotNull PsiLambdaExpression lambda, @Nullable String label, @Nullable PsiElement highlightElement, int ordinal) {
|
||||
super(label, highlightElement, true);
|
||||
myLambda = lambda;
|
||||
myOrdinal = ordinal;
|
||||
}
|
||||
|
||||
public PsiLambdaExpression getLambda() {
|
||||
return myLambda;
|
||||
}
|
||||
|
||||
public int getOrdinal() {
|
||||
return myOrdinal;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final LambdaSmartStepTarget that = (LambdaSmartStepTarget)o;
|
||||
|
||||
if (myOrdinal != that.myOrdinal) {
|
||||
return false;
|
||||
}
|
||||
if (!myLambda.equals(that.myLambda)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = myLambda.hashCode();
|
||||
result = 31 * result + myOrdinal;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.actions;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/25/13
|
||||
*/
|
||||
public class MethodSmartStepTarget extends SmartStepTarget{
|
||||
private final PsiMethod myMethod;
|
||||
|
||||
public MethodSmartStepTarget(@NotNull PsiMethod method) {
|
||||
this(method, null, null, false);
|
||||
}
|
||||
|
||||
public MethodSmartStepTarget(@NotNull PsiMethod method, @Nullable String label, @Nullable PsiElement highlightElement, boolean needBreakpointRequest) {
|
||||
super(label, highlightElement, needBreakpointRequest);
|
||||
myMethod = method;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiMethod getMethod() {
|
||||
return myMethod;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final MethodSmartStepTarget that = (MethodSmartStepTarget)o;
|
||||
|
||||
if (!myMethod.equals(that.myMethod)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return myMethod.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@ package com.intellij.debugger.actions;
|
||||
|
||||
import com.intellij.codeInsight.unwrap.ScopeHighlighter;
|
||||
import com.intellij.debugger.DebuggerBundle;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.ui.popup.*;
|
||||
import com.intellij.psi.PsiLambdaExpression;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiSubstitutor;
|
||||
import com.intellij.psi.util.PsiFormatUtil;
|
||||
@@ -31,16 +33,16 @@ import java.util.List;
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: Nov 21, 2006
|
||||
*/
|
||||
class PsiMethodListPopupStep implements ListPopupStep<JvmSmartStepIntoHandler.StepTarget> {
|
||||
private final List<JvmSmartStepIntoHandler.StepTarget> myTargets;
|
||||
class PsiMethodListPopupStep implements ListPopupStep<SmartStepTarget> {
|
||||
private final List<SmartStepTarget> myTargets;
|
||||
private final OnChooseRunnable myStepRunnable;
|
||||
private final ScopeHighlighter myScopeHighlighter;
|
||||
|
||||
public interface OnChooseRunnable {
|
||||
void execute(JvmSmartStepIntoHandler.StepTarget stepTarget);
|
||||
void execute(SmartStepTarget stepTarget);
|
||||
}
|
||||
|
||||
public PsiMethodListPopupStep(Editor editor, final List<JvmSmartStepIntoHandler.StepTarget> targets, final OnChooseRunnable stepRunnable) {
|
||||
public PsiMethodListPopupStep(Editor editor, final List<SmartStepTarget> targets, final OnChooseRunnable stepRunnable) {
|
||||
myTargets = targets;
|
||||
myScopeHighlighter = new ScopeHighlighter(editor);
|
||||
myStepRunnable = stepRunnable;
|
||||
@@ -52,33 +54,49 @@ class PsiMethodListPopupStep implements ListPopupStep<JvmSmartStepIntoHandler.St
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<JvmSmartStepIntoHandler.StepTarget> getValues() {
|
||||
public List<SmartStepTarget> getValues() {
|
||||
return myTargets;
|
||||
}
|
||||
|
||||
public boolean isSelectable(JvmSmartStepIntoHandler.StepTarget value) {
|
||||
public boolean isSelectable(SmartStepTarget value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Icon getIconFor(JvmSmartStepIntoHandler.StepTarget aValue) {
|
||||
return aValue.getMethod().getIcon(0);
|
||||
public Icon getIconFor(SmartStepTarget aValue) {
|
||||
if (aValue instanceof MethodSmartStepTarget) {
|
||||
return ((MethodSmartStepTarget)aValue).getMethod().getIcon(0);
|
||||
}
|
||||
if (aValue instanceof LambdaSmartStepTarget) {
|
||||
return AllIcons.Nodes.Function;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTextFor(JvmSmartStepIntoHandler.StepTarget value) {
|
||||
final PsiMethod method = value.getMethod();
|
||||
final String methodLabel = value.getMethodLabel();
|
||||
final String methodRender = PsiFormatUtil.formatMethod(
|
||||
method,
|
||||
PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS,
|
||||
PsiFormatUtil.SHOW_TYPE,
|
||||
999
|
||||
);
|
||||
return methodLabel != null? methodLabel + methodRender : methodRender;
|
||||
public String getTextFor(SmartStepTarget value) {
|
||||
final String label = value.getLabel();
|
||||
final String formatted;
|
||||
if (value instanceof MethodSmartStepTarget) {
|
||||
final PsiMethod method = ((MethodSmartStepTarget)value).getMethod();
|
||||
formatted = PsiFormatUtil.formatMethod(
|
||||
method,
|
||||
PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS,
|
||||
PsiFormatUtil.SHOW_TYPE,
|
||||
999
|
||||
);
|
||||
}
|
||||
else if (value instanceof LambdaSmartStepTarget){
|
||||
final PsiLambdaExpression lambda = ((LambdaSmartStepTarget)value).getLambda();
|
||||
formatted = PsiFormatUtil.formatType(lambda.getType(), 0, PsiSubstitutor.EMPTY);
|
||||
}
|
||||
else {
|
||||
formatted = "";
|
||||
}
|
||||
return label != null? label + formatted : formatted;
|
||||
}
|
||||
|
||||
public ListSeparator getSeparatorAbove(JvmSmartStepIntoHandler.StepTarget value) {
|
||||
public ListSeparator getSeparatorAbove(SmartStepTarget value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -90,7 +108,7 @@ class PsiMethodListPopupStep implements ListPopupStep<JvmSmartStepIntoHandler.St
|
||||
return DebuggerBundle.message("title.smart.step.popup");
|
||||
}
|
||||
|
||||
public PopupStep onChosen(JvmSmartStepIntoHandler.StepTarget selectedValue, final boolean finalChoice) {
|
||||
public PopupStep onChosen(SmartStepTarget selectedValue, final boolean finalChoice) {
|
||||
if (finalChoice) {
|
||||
myScopeHighlighter.dropHighlight();
|
||||
myStepRunnable.execute(selectedValue);
|
||||
@@ -102,7 +120,7 @@ class PsiMethodListPopupStep implements ListPopupStep<JvmSmartStepIntoHandler.St
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasSubstep(JvmSmartStepIntoHandler.StepTarget selectedValue) {
|
||||
public boolean hasSubstep(SmartStepTarget selectedValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.actions;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/25/13
|
||||
*/
|
||||
public abstract class SmartStepTarget {
|
||||
private final PsiElement myHighlightElement;
|
||||
private final String myLabel;
|
||||
private final boolean myNeedBreakpointRequest;
|
||||
|
||||
protected SmartStepTarget(@Nullable String label, @Nullable PsiElement highlightElement, boolean needBreakpointRequest) {
|
||||
myHighlightElement = highlightElement;
|
||||
myLabel = label;
|
||||
myNeedBreakpointRequest = needBreakpointRequest;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement getHighlightElement() {
|
||||
return myHighlightElement;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLabel() {
|
||||
return myLabel;
|
||||
}
|
||||
|
||||
public boolean needsBreakpointRequest() {
|
||||
return myNeedBreakpointRequest;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.engine;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.psi.PsiCodeBlock;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiStatement;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/26/13
|
||||
*/
|
||||
public class AnonymousClassMethodFilter extends BasicStepMethodFilter implements BreakpointStepMethodFilter{
|
||||
@Nullable
|
||||
private final SourcePosition myBreakpointPosition;
|
||||
|
||||
public AnonymousClassMethodFilter(PsiMethod psiMethod) {
|
||||
super(psiMethod);
|
||||
myBreakpointPosition = calcBreakpointPosition(psiMethod.getBody());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SourcePosition getBreakpointPosition() {
|
||||
return myBreakpointPosition;
|
||||
}
|
||||
|
||||
private static SourcePosition calcBreakpointPosition(final PsiCodeBlock body) {
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiStatement[] statements = body.getStatements();
|
||||
if (statements.length == 0) {
|
||||
return null;
|
||||
}
|
||||
final PsiStatement firstStatement = statements[0];
|
||||
return SourcePosition.createFromElement(firstStatement);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.engine;
|
||||
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.Method;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/26/13
|
||||
*/
|
||||
public class BasicStepMethodFilter implements MethodFilter{
|
||||
@NotNull
|
||||
protected final JVMName myDeclaringClassName;
|
||||
@NotNull
|
||||
private final String myTargetMethodName;
|
||||
@Nullable
|
||||
protected final JVMName myTargetMethodSignature;
|
||||
|
||||
public BasicStepMethodFilter(PsiMethod psiMethod) {
|
||||
myDeclaringClassName = JVMNameUtil.getJVMQualifiedName(psiMethod.getContainingClass());
|
||||
myTargetMethodName = psiMethod.isConstructor() ? "<init>" : psiMethod.getName();
|
||||
myTargetMethodSignature = JVMNameUtil.getJVMSignature(psiMethod);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMethodName() {
|
||||
return myTargetMethodName;
|
||||
}
|
||||
|
||||
public boolean locationMatches(DebugProcessImpl process, Location location) throws EvaluateException {
|
||||
final Method method = location.method();
|
||||
if (!myTargetMethodName.equals(method.name())) {
|
||||
return false;
|
||||
}
|
||||
if (myTargetMethodSignature != null) {
|
||||
if (!signatureMatches(method, myTargetMethodSignature.getName(process))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return DebuggerUtilsEx.isAssignableFrom(myDeclaringClassName.getName(process), location.declaringType());
|
||||
}
|
||||
|
||||
private static boolean signatureMatches(Method method, final String expectedSignature) throws EvaluateException {
|
||||
if (expectedSignature.equals(method.signature())) {
|
||||
return true;
|
||||
}
|
||||
// check if there are any bridge methods that match
|
||||
for (Method candidate : method.declaringType().methodsByName(method.name())) {
|
||||
if (candidate != method && candidate.isBridge() && expectedSignature.equals(candidate.signature())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.engine;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/26/13
|
||||
*/
|
||||
public interface BreakpointStepMethodFilter extends MethodFilter{
|
||||
@Nullable
|
||||
SourcePosition getBreakpointPosition();
|
||||
}
|
||||
@@ -394,8 +394,8 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
getSuspendManager().voteSuspend(suspendContext);
|
||||
if (hint != null) {
|
||||
final MethodFilter methodFilter = hint.getMethodFilter();
|
||||
if (methodFilter != null && methodFilter.getBreakpointPosition() == null && !hint.wasStepTargetMethodMatched()) {
|
||||
final String message = "Method <b>" + methodFilter.getMethodName() + "()</b> has not been called";
|
||||
if (methodFilter instanceof BasicStepMethodFilter && !hint.wasStepTargetMethodMatched()) {
|
||||
final String message = "Method <b>" + ((BasicStepMethodFilter)methodFilter).getMethodName() + "()</b> has not been called";
|
||||
XDebugSessionImpl.NOTIFICATION_GROUP.createNotification(message, MessageType.INFO).notify(project);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1378,7 +1378,9 @@ public abstract class DebugProcessImpl implements DebugProcess {
|
||||
super(suspendContext);
|
||||
myForcedIgnoreFilters = ignoreFilters || methodFilter != null;
|
||||
mySmartStepFilter = methodFilter;
|
||||
myBreakpoint = methodFilter == null ? null : DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().addStepIntoBreakpoint(methodFilter);
|
||||
myBreakpoint = methodFilter instanceof BreakpointStepMethodFilter ?
|
||||
DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().addStepIntoBreakpoint(((BreakpointStepMethodFilter)methodFilter)) :
|
||||
null;
|
||||
}
|
||||
|
||||
public void contextAction() {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.debugger.engine;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.psi.PsiCodeBlock;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiLambdaExpression;
|
||||
import com.intellij.psi.PsiStatement;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.Method;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/26/13
|
||||
*/
|
||||
public class LambdaMethodFilter implements BreakpointStepMethodFilter{
|
||||
private static final String LAMBDA_METHOD_PREFIX = "lambda$";
|
||||
private final int myLambdaOrdinal;
|
||||
@Nullable
|
||||
private final SourcePosition myFirstStatementPosition;
|
||||
private final int myLastStatementLine;
|
||||
|
||||
public LambdaMethodFilter(PsiLambdaExpression lambda, int expressionOrdinal) {
|
||||
myLambdaOrdinal = expressionOrdinal;
|
||||
|
||||
SourcePosition firstStatementPosition = null;
|
||||
SourcePosition lastStatementPosition = null;
|
||||
final PsiElement body = lambda.getBody();
|
||||
if (body instanceof PsiCodeBlock) {
|
||||
final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements();
|
||||
final int statementCount = statements.length;
|
||||
if (statementCount > 0) {
|
||||
firstStatementPosition = SourcePosition.createFromElement(statements[0]);
|
||||
if (statementCount > 1) {
|
||||
lastStatementPosition = SourcePosition.createFromElement(statements[statementCount - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (body != null){
|
||||
firstStatementPosition = SourcePosition.createFromElement(body);
|
||||
}
|
||||
myFirstStatementPosition = firstStatementPosition;
|
||||
myLastStatementLine = lastStatementPosition != null? lastStatementPosition.getLine() : -1;
|
||||
}
|
||||
|
||||
public int getLambdaOrdinal() {
|
||||
return myLambdaOrdinal;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SourcePosition getBreakpointPosition() {
|
||||
return myFirstStatementPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a zero-based line number of the last lambda statement, or -1 if not available
|
||||
*/
|
||||
public int getLastStatementLine() {
|
||||
return myLastStatementLine;
|
||||
}
|
||||
|
||||
public boolean locationMatches(DebugProcessImpl process, Location location) throws EvaluateException {
|
||||
final Method method = location.method();
|
||||
return method.name().startsWith(LAMBDA_METHOD_PREFIX) && method.isSynthetic();
|
||||
}
|
||||
}
|
||||
@@ -15,91 +15,9 @@
|
||||
*/
|
||||
package com.intellij.debugger.engine;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.psi.PsiCodeBlock;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiStatement;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.Method;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/23/13
|
||||
*/
|
||||
public final class MethodFilter {
|
||||
@NotNull
|
||||
private final JVMName myDeclaringClassName;
|
||||
@NotNull
|
||||
private final String myTargetMethodName;
|
||||
@NotNull
|
||||
private final JVMName myTargetMethodSignature;
|
||||
@Nullable
|
||||
private final SourcePosition myBreakpointPosition;
|
||||
|
||||
public MethodFilter(PsiMethod psiMethod, boolean createBreakpointRequest) {
|
||||
this(
|
||||
JVMNameUtil.getJVMQualifiedName(psiMethod.getContainingClass()),
|
||||
psiMethod.isConstructor()? "<init>" : psiMethod.getName(),
|
||||
JVMNameUtil.getJVMSignature(psiMethod),
|
||||
createBreakpointRequest? calcBreakpointPosition(psiMethod) : null
|
||||
);
|
||||
}
|
||||
|
||||
public MethodFilter(@NotNull JVMName declaringClassName, @NotNull String targetMethodName, @NotNull JVMName targetMethodSignature, @Nullable SourcePosition breakpointPosition) {
|
||||
myDeclaringClassName = declaringClassName;
|
||||
myTargetMethodName = targetMethodName;
|
||||
myTargetMethodSignature = targetMethodSignature;
|
||||
myBreakpointPosition = breakpointPosition;
|
||||
}
|
||||
|
||||
private static SourcePosition calcBreakpointPosition(PsiMethod method) {
|
||||
final PsiCodeBlock body = method.getBody();
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiStatement[] statements = body.getStatements();
|
||||
if (statements.length == 0) {
|
||||
return null;
|
||||
}
|
||||
final PsiStatement firstStatement = statements[0];
|
||||
return SourcePosition.createFromElement(firstStatement);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMethodName() {
|
||||
return myTargetMethodName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SourcePosition getBreakpointPosition() {
|
||||
return myBreakpointPosition;
|
||||
}
|
||||
|
||||
public boolean locationMatches(DebugProcessImpl process, Location location) throws EvaluateException {
|
||||
final Method method = location.method();
|
||||
if (!myTargetMethodName.equals(method.name())) {
|
||||
return false;
|
||||
}
|
||||
if (!signatureMatches(method, myTargetMethodSignature.getName(process))) {
|
||||
return false;
|
||||
}
|
||||
return DebuggerUtilsEx.isAssignableFrom(myDeclaringClassName.getName(process), location.declaringType());
|
||||
}
|
||||
|
||||
private static boolean signatureMatches(Method method, final String expectedSignature) throws EvaluateException {
|
||||
if (expectedSignature.equals(method.signature())) {
|
||||
return true;
|
||||
}
|
||||
// check if there are any bridge methods that match
|
||||
for (Method candidate : method.declaringType().methodsByName(method.name())) {
|
||||
if (candidate != method && candidate.isBridge() && expectedSignature.equals(candidate.signature())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public interface MethodFilter {
|
||||
boolean locationMatches(DebugProcessImpl process, Location location) throws EvaluateException;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public class RequestHint {
|
||||
}
|
||||
|
||||
public boolean wasStepTargetMethodMatched() {
|
||||
return myTargetMethodMatched;
|
||||
return myMethodFilter instanceof BreakpointStepMethodFilter || myTargetMethodMatched;
|
||||
}
|
||||
|
||||
public int getNextStepDepth(final SuspendContextImpl context) {
|
||||
@@ -227,7 +227,7 @@ public class RequestHint {
|
||||
}
|
||||
// smart step feature
|
||||
if (myMethodFilter != null) {
|
||||
if (myMethodFilter.getBreakpointPosition() != null) {
|
||||
if (myMethodFilter instanceof BreakpointStepMethodFilter) {
|
||||
// continue stepping if stop criterion is implemented as breakpoint request
|
||||
return StepRequest.STEP_OUT;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import com.intellij.codeInsight.folding.impl.actions.ExpandRegionAction;
|
||||
import com.intellij.debugger.DebuggerBundle;
|
||||
import com.intellij.debugger.DebuggerInvocationUtil;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.BreakpointStepMethodFilter;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.MethodFilter;
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
|
||||
import com.intellij.debugger.engine.requests.RequestManagerImpl;
|
||||
@@ -400,7 +400,7 @@ public class BreakpointManager {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public StepIntoBreakpoint addStepIntoBreakpoint(@NotNull MethodFilter filter) {
|
||||
public StepIntoBreakpoint addStepIntoBreakpoint(@NotNull BreakpointStepMethodFilter filter) {
|
||||
return StepIntoBreakpoint.create(myProject, filter);
|
||||
}
|
||||
|
||||
|
||||
+99
-6
@@ -16,16 +16,21 @@
|
||||
package com.intellij.debugger.ui.breakpoints;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.BreakpointStepMethodFilter;
|
||||
import com.intellij.debugger.engine.CompoundPositionManager;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.MethodFilter;
|
||||
import com.intellij.debugger.engine.LambdaMethodFilter;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.requests.RequestManagerImpl;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.ReferenceType;
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.request.BreakpointRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: Sep 13, 2006
|
||||
@@ -33,9 +38,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
public class StepIntoBreakpoint extends RunToCursorBreakpoint {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.StepIntoBreakpoint");
|
||||
@NotNull
|
||||
private final MethodFilter myFilter;
|
||||
private final BreakpointStepMethodFilter myFilter;
|
||||
|
||||
StepIntoBreakpoint(@NotNull Project project, @NotNull SourcePosition pos, @NotNull MethodFilter filter) {
|
||||
StepIntoBreakpoint(@NotNull Project project, @NotNull SourcePosition pos, @NotNull BreakpointStepMethodFilter filter) {
|
||||
super(project, pos, false);
|
||||
myFilter = filter;
|
||||
}
|
||||
@@ -44,6 +49,94 @@ public class StepIntoBreakpoint extends RunToCursorBreakpoint {
|
||||
super.createOrWaitPrepare(debugProcess, classPosition);
|
||||
}
|
||||
|
||||
protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) {
|
||||
try {
|
||||
final CompoundPositionManager positionManager = debugProcess.getPositionManager();
|
||||
final SourcePosition startPosition = getSourcePosition();
|
||||
List<Location> locations = positionManager.locationsOfLine(classType, startPosition);
|
||||
|
||||
if (locations.isEmpty() && myFilter instanceof LambdaMethodFilter) {
|
||||
// sometimes first statements are mapped to some weird line number,
|
||||
// so if lambda spans for more than one lines, try get some locations from these lines
|
||||
final int lastLine = ((LambdaMethodFilter)myFilter).getLastStatementLine();
|
||||
if (lastLine >= 0) {
|
||||
int nextLine = startPosition.getLine() + 1;
|
||||
while (nextLine <= lastLine && locations.isEmpty()) {
|
||||
locations = positionManager.locationsOfLine(classType, SourcePosition.createFromLine(startPosition.getFile(), nextLine++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!locations.isEmpty()) {
|
||||
final Set<Method> methods = new HashSet<Method>();
|
||||
for (Location loc : locations) {
|
||||
if (acceptLocation(debugProcess, classType, loc)) {
|
||||
methods.add(loc.method());
|
||||
}
|
||||
}
|
||||
Location location = null;
|
||||
final int methodsFound = methods.size();
|
||||
if (methodsFound == 1) {
|
||||
location = methods.iterator().next().location();
|
||||
}
|
||||
else {
|
||||
if (myFilter instanceof LambdaMethodFilter) {
|
||||
final LambdaMethodFilter lambdaFilter = (LambdaMethodFilter)myFilter;
|
||||
if (lambdaFilter.getLambdaOrdinal() < methodsFound) {
|
||||
final Method[] candidates = methods.toArray(new Method[methodsFound]);
|
||||
Arrays.sort(candidates, new Comparator<Method>() {
|
||||
public int compare(Method m1, Method m2) {
|
||||
return getMethodOrdinal(m1) - getMethodOrdinal(m2);
|
||||
}
|
||||
});
|
||||
location = candidates[lambdaFilter.getLambdaOrdinal()].location();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (methodsFound > 0) {
|
||||
location = methods.iterator().next().location();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (location != null) {
|
||||
final RequestManagerImpl requestsManager = debugProcess.getRequestsManager();
|
||||
final BreakpointRequest request = requestsManager.createBreakpointRequest(this, location);
|
||||
requestsManager.enableRequest(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClassNotPreparedException ex) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("ClassNotPreparedException: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
catch (ObjectCollectedException ex) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("ObjectCollectedException: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
catch (InternalException ex) {
|
||||
LOG.info(ex);
|
||||
}
|
||||
catch(Exception ex) {
|
||||
LOG.info(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getMethodOrdinal(Method m) {
|
||||
final String name = m.name();
|
||||
final int dollarIndex = name.lastIndexOf("$");
|
||||
if (dollarIndex < 0) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(name.substring(dollarIndex + 1));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean acceptLocation(DebugProcessImpl debugProcess, ReferenceType classType, Location loc) {
|
||||
try {
|
||||
return myFilter.locationMatches(debugProcess, loc);
|
||||
@@ -55,7 +148,7 @@ public class StepIntoBreakpoint extends RunToCursorBreakpoint {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static StepIntoBreakpoint create(@NotNull Project project, @NotNull MethodFilter filter) {
|
||||
protected static StepIntoBreakpoint create(@NotNull Project project, @NotNull BreakpointStepMethodFilter filter) {
|
||||
final SourcePosition pos = filter.getBreakpointPosition();
|
||||
if (pos != null) {
|
||||
final StepIntoBreakpoint breakpoint = new StepIntoBreakpoint(project, pos, filter);
|
||||
|
||||
Reference in New Issue
Block a user