mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 23:06:24 +07:00
IDEA-131134 Support multiple breakpoints within on single line - initial commit
This commit is contained in:
@@ -79,6 +79,7 @@ public class CompoundPositionManager extends PositionManagerEx implements MultiR
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SourcePosition getSourcePosition(final Location location) {
|
||||
if (location == null) return null;
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.debugger.NoDataException;
|
||||
import com.intellij.debugger.PositionManager;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
|
||||
import com.intellij.debugger.requests.ClassPrepareRequestor;
|
||||
import com.intellij.execution.filters.LineNumbersMapping;
|
||||
@@ -122,6 +123,7 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SourcePosition getSourcePosition(final Location location) throws NoDataException {
|
||||
DebuggerManagerThreadImpl.assertIsManagerThread();
|
||||
if(location == null) {
|
||||
@@ -254,51 +256,17 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio
|
||||
PsiFile file = original.getFile();
|
||||
int line = original.getLine();
|
||||
if (LambdaMethodFilter.isLambdaName(myExpectedMethodName) && myLambdaOrdinal > -1) {
|
||||
List<PsiLambdaExpression> lambdas = DebuggerUtilsEx.collectLambdas(original, false);
|
||||
|
||||
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
if (document == null || line >= document.getLineCount()) {
|
||||
return original;
|
||||
}
|
||||
PsiElement element = original.getElementAt();
|
||||
TextRange lineRange = new TextRange(document.getLineStartOffset(line), document.getLineEndOffset(line));
|
||||
do {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent == null || (parent.getTextOffset() < lineRange.getStartOffset())) {
|
||||
break;
|
||||
}
|
||||
element = parent;
|
||||
}
|
||||
while(true);
|
||||
final List<PsiLambdaExpression> lambdas = new ArrayList<PsiLambdaExpression>(3);
|
||||
final PsiElementVisitor lambdaCollector = new JavaRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {
|
||||
super.visitLambdaExpression(expression);
|
||||
lambdas.add(expression);
|
||||
}
|
||||
};
|
||||
element.accept(lambdaCollector);
|
||||
// add initial lambda if we're inside already
|
||||
NavigatablePsiElement method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiLambdaExpression.class);
|
||||
if (method instanceof PsiLambdaExpression) {
|
||||
lambdas.add((PsiLambdaExpression)method);
|
||||
}
|
||||
for (PsiElement sibling = getNextElement(element); sibling != null; sibling = getNextElement(sibling)) {
|
||||
if (!lineRange.intersects(sibling.getTextRange())) {
|
||||
break;
|
||||
}
|
||||
sibling.accept(lambdaCollector);
|
||||
}
|
||||
if (myLambdaOrdinal < lambdas.size()) {
|
||||
PsiElement body = lambdas.get(myLambdaOrdinal).getBody();
|
||||
if (body instanceof PsiCodeBlock) {
|
||||
for (PsiStatement statement : ((PsiCodeBlock)body).getStatements()) {
|
||||
if (lineRange.intersects(statement.getTextRange())) {
|
||||
body = statement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PsiElement firstElem = DebuggerUtilsEx.getFirstElementOnTheLine(lambdas.get(myLambdaOrdinal), document, line);
|
||||
if (firstElem != null) {
|
||||
return SourcePosition.createFromElement(firstElem);
|
||||
}
|
||||
return SourcePosition.createFromElement(body);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -316,14 +284,6 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio
|
||||
});
|
||||
}
|
||||
|
||||
private static PsiElement getNextElement(PsiElement element) {
|
||||
PsiElement sibling = element.getNextSibling();
|
||||
if (sibling != null) return sibling;
|
||||
element = element.getParent();
|
||||
if (element != null) return getNextElement(element);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RangeHighlighter createHighlighter(Document document, Project project, TextAttributes attributes) {
|
||||
|
||||
@@ -58,6 +58,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.classFilter.ClassFilter;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.unscramble.ThreadDumpPanel;
|
||||
@@ -779,4 +780,85 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils {
|
||||
res.append(location.method().name());
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
private static PsiElement getNextElement(PsiElement element) {
|
||||
PsiElement sibling = element.getNextSibling();
|
||||
if (sibling != null) return sibling;
|
||||
element = element.getParent();
|
||||
if (element != null) return getNextElement(element);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<PsiLambdaExpression> collectLambdas(SourcePosition position, final boolean onlyOnTheLine) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
PsiFile file = position.getFile();
|
||||
int line = position.getLine();
|
||||
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
if (document == null || line >= document.getLineCount()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
PsiElement element = position.getElementAt();
|
||||
final TextRange lineRange = new TextRange(document.getLineStartOffset(line), document.getLineEndOffset(line));
|
||||
do {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent == null || (parent.getTextOffset() < lineRange.getStartOffset())) {
|
||||
break;
|
||||
}
|
||||
element = parent;
|
||||
}
|
||||
while(true);
|
||||
|
||||
final List<PsiLambdaExpression> lambdas = new ArrayList<PsiLambdaExpression>(3);
|
||||
final PsiElementVisitor lambdaCollector = new JavaRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {
|
||||
super.visitLambdaExpression(expression);
|
||||
if (!onlyOnTheLine || lineRange.intersects(expression.getTextRange())) {
|
||||
lambdas.add(expression);
|
||||
}
|
||||
}
|
||||
};
|
||||
element.accept(lambdaCollector);
|
||||
// add initial lambda if we're inside already
|
||||
NavigatablePsiElement method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiLambdaExpression.class);
|
||||
if (method instanceof PsiLambdaExpression) {
|
||||
lambdas.add((PsiLambdaExpression)method);
|
||||
}
|
||||
for (PsiElement sibling = getNextElement(element); sibling != null; sibling = getNextElement(sibling)) {
|
||||
if (!lineRange.intersects(sibling.getTextRange())) {
|
||||
break;
|
||||
}
|
||||
sibling.accept(lambdaCollector);
|
||||
}
|
||||
return lambdas;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement getFirstElementOnTheLine(PsiLambdaExpression lambda, Document document, int line) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
TextRange lineRange = new TextRange(document.getLineStartOffset(line), document.getLineEndOffset(line));
|
||||
if (!lineRange.intersects(lambda.getTextRange())) return null;
|
||||
PsiElement body = lambda.getBody();
|
||||
if (body instanceof PsiCodeBlock) {
|
||||
for (PsiStatement statement : ((PsiCodeBlock)body).getStatements()) {
|
||||
if (lineRange.intersects(statement.getTextRange())) {
|
||||
return statement;
|
||||
}
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
public static boolean inTheSameMethod(@NotNull SourcePosition pos1, @NotNull SourcePosition pos2) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
PsiElement elem1 = pos1.getElementAt();
|
||||
PsiElement elem2 = pos2.getElementAt();
|
||||
if (elem1 == null) return elem2 == null;
|
||||
if (elem2 != null) {
|
||||
NavigatablePsiElement expectedMethod = PsiTreeUtil.getParentOfType(elem1, PsiMethod.class, PsiLambdaExpression.class);
|
||||
NavigatablePsiElement currentMethod = PsiTreeUtil.getParentOfType(elem2, PsiMethod.class, PsiLambdaExpression.class);
|
||||
return Comparing.equal(expectedMethod, currentMethod);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+118
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -17,26 +17,39 @@ package com.intellij.debugger.ui.breakpoints;
|
||||
|
||||
import com.intellij.debugger.DebuggerBundle;
|
||||
import com.intellij.debugger.HelpID;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.xdebugger.XDebuggerUtil;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.breakpoints.XBreakpoint;
|
||||
import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
|
||||
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
|
||||
import com.intellij.xdebugger.impl.XSourcePositionImpl;
|
||||
import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointVariant;
|
||||
import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointVariantsProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperties;
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for java line-connected exceptions (line, method, field)
|
||||
* @author egor
|
||||
*/
|
||||
public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaBreakpointProperties> implements JavaBreakpointType {
|
||||
public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaBreakpointProperties>
|
||||
implements JavaBreakpointType, XLineBreakpointVariantsProvider<JavaLineBreakpointType.JavaBreakpointVariant> {
|
||||
public JavaLineBreakpointType() {
|
||||
super("java-line", DebuggerBundle.message("line.breakpoints.tab.title"));
|
||||
}
|
||||
@@ -58,13 +71,13 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaBreak
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JavaBreakpointProperties createProperties() {
|
||||
public JavaLineBreakpointProperties createProperties() {
|
||||
return new JavaLineBreakpointProperties();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JavaBreakpointProperties createBreakpointProperties(@NotNull VirtualFile file, int line) {
|
||||
public JavaLineBreakpointProperties createBreakpointProperties(@NotNull VirtualFile file, int line) {
|
||||
return new JavaLineBreakpointProperties();
|
||||
}
|
||||
|
||||
@@ -78,4 +91,104 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaBreak
|
||||
public int getPriority() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<JavaBreakpointVariant> computeLineBreakpointVariants(@NotNull Project project, @NotNull XSourcePosition position) {
|
||||
PsiFile file = PsiManager.getInstance(project).findFile(position.getFile());
|
||||
if (file == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
SourcePosition pos = SourcePosition.createFromLine(file, position.getLine());
|
||||
List<PsiLambdaExpression> lambdas = DebuggerUtilsEx.collectLambdas(pos, true);
|
||||
if (lambdas.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
NavigatablePsiElement startMethod = PsiTreeUtil.getParentOfType(pos.getElementAt(), PsiMethod.class, PsiLambdaExpression.class);
|
||||
//noinspection SuspiciousMethodCalls
|
||||
if (lambdas.contains(startMethod) && lambdas.size() == 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
if (document == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<JavaBreakpointVariant> res = new SmartList<JavaBreakpointVariant>();
|
||||
res.add(new JavaBreakpointVariant(position)); //all
|
||||
|
||||
if (startMethod instanceof PsiMethod) {
|
||||
res.add(new ExactJavaBreakpointVariant(position, startMethod)); // base method
|
||||
}
|
||||
|
||||
for (PsiLambdaExpression lambda : lambdas) { //lambdas
|
||||
PsiElement firstElem = DebuggerUtilsEx.getFirstElementOnTheLine(lambda, document, position.getLine());
|
||||
res.add(new ExactJavaBreakpointVariant(XSourcePositionImpl.createByElement(firstElem), lambda));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
class JavaBreakpointVariant extends XLineBreakpointVariant {
|
||||
protected final XSourcePosition mySourcePosition;
|
||||
|
||||
private JavaBreakpointVariant(XSourcePosition position) {
|
||||
mySourcePosition = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "All";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextRange getHighlightRange() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaLineBreakpointProperties createProperties() {
|
||||
return createBreakpointProperties(mySourcePosition.getFile(),
|
||||
mySourcePosition.getLine());
|
||||
}
|
||||
}
|
||||
|
||||
private class ExactJavaBreakpointVariant extends JavaBreakpointVariant {
|
||||
private final PsiElement myElement;
|
||||
|
||||
public ExactJavaBreakpointVariant(XSourcePosition position, PsiElement element) {
|
||||
super(position);
|
||||
myElement = element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return myElement.getIcon(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return StringUtil.shortenTextWithEllipsis(myElement.getText(), 100, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextRange getHighlightRange() {
|
||||
return myElement.getTextRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaLineBreakpointProperties createProperties() {
|
||||
JavaLineBreakpointProperties properties = super.createProperties();
|
||||
properties.setOffset(mySourcePosition.getOffset());
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import com.sun.jdi.request.BreakpointRequest;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties;
|
||||
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -198,12 +199,30 @@ public class LineBreakpoint extends BreakpointWithHighlighter {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean acceptLocation(DebugProcessImpl debugProcess, ReferenceType classType, Location loc) {
|
||||
protected boolean acceptLocation(final DebugProcessImpl debugProcess, ReferenceType classType, final Location loc) {
|
||||
Method method = loc.method();
|
||||
if (DebuggerUtils.isSynthetic(method)) {
|
||||
return false;
|
||||
}
|
||||
return !(method.isConstructor() && loc.codeIndex() == 0 && isAnonymousClass(classType));
|
||||
boolean res = !(method.isConstructor() && loc.codeIndex() == 0 && isAnonymousClass(classType));
|
||||
if (!res) return false;
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
if (getProperties() instanceof JavaLineBreakpointProperties) {
|
||||
Integer offset = ((JavaLineBreakpointProperties)getProperties()).getOffset();
|
||||
if (offset == null) return true;
|
||||
PsiFile file = getPsiFile();
|
||||
if (file != null) {
|
||||
SourcePosition exactPosition = SourcePosition.createFromOffset(file, offset);
|
||||
SourcePosition position = debugProcess.getPositionManager().getSourcePosition(loc);
|
||||
if (position == null) return false;
|
||||
return DebuggerUtilsEx.inTheSameMethod(exactPosition, position);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isInScopeOf(DebugProcessImpl debugProcess, String className) {
|
||||
|
||||
+6
-16
@@ -17,12 +17,12 @@ package com.intellij.debugger.ui.breakpoints;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.ReferenceType;
|
||||
@@ -125,19 +125,9 @@ public class RunToCursorBreakpoint extends LineBreakpoint {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
PsiElement expectedElement = myCustomPosition.getElementAt();
|
||||
if (expectedElement != null) {
|
||||
SourcePosition position = debugProcess.getPositionManager().getSourcePosition(loc);
|
||||
if (position != null) {
|
||||
PsiElement currentElement = position.getElementAt();
|
||||
if (currentElement != null) {
|
||||
NavigatablePsiElement expectedMethod = PsiTreeUtil.getParentOfType(expectedElement, PsiMethod.class, PsiLambdaExpression.class);
|
||||
NavigatablePsiElement currentMethod = PsiTreeUtil.getParentOfType(currentElement, PsiMethod.class, PsiLambdaExpression.class);
|
||||
return Comparing.equal(expectedMethod, currentMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
SourcePosition position = debugProcess.getPositionManager().getSourcePosition(loc);
|
||||
if (position == null) return false;
|
||||
return DebuggerUtilsEx.inTheSameMethod(myCustomPosition, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+13
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -15,8 +15,20 @@
|
||||
*/
|
||||
package org.jetbrains.java.debugger.breakpoints.properties;
|
||||
|
||||
import com.intellij.util.xmlb.annotations.OptionTag;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
public class JavaLineBreakpointProperties extends JavaBreakpointProperties<JavaLineBreakpointProperties> {
|
||||
private Integer myOffset = null;
|
||||
|
||||
@OptionTag("offset")
|
||||
public Integer getOffset() {
|
||||
return myOffset;
|
||||
}
|
||||
|
||||
public void setOffset(Integer offset) {
|
||||
myOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ public abstract class SourcePosition implements Navigatable{
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SourcePosition createFromElement(PsiElement element) {
|
||||
public static SourcePosition createFromElement(@NotNull PsiElement element) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
PsiElement navigationElement = element.getNavigationElement();
|
||||
final SmartPsiElementPointer<PsiElement> pointer =
|
||||
|
||||
Reference in New Issue
Block a user