From 0908de1bde2dcd4720f8f2452f08ca1e93d6d093 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Thu, 11 Jun 2015 19:01:24 +0300 Subject: [PATCH] IDEA-131134 Support multiple breakpoints within on single line - initial commit --- .../engine/CompoundPositionManager.java | 1 + .../debugger/engine/PositionManagerImpl.java | 54 +------- .../debugger/impl/DebuggerUtilsEx.java | 82 ++++++++++++ .../breakpoints/JavaLineBreakpointType.java | 123 +++++++++++++++++- .../ui/breakpoints/LineBreakpoint.java | 23 +++- .../ui/breakpoints/RunToCursorBreakpoint.java | 22 +--- .../JavaLineBreakpointProperties.java | 14 +- .../com/intellij/debugger/SourcePosition.java | 2 +- .../xdebugger/impl/XDebuggerUtilImpl.java | 90 ++++++++++--- .../XToggleLineBreakpointActionHandler.java | 4 +- .../impl/breakpoints/XBreakpointUtil.java | 38 ++++-- .../breakpoints/XLineBreakpointManager.java | 36 +++-- .../breakpoints/XLineBreakpointVariant.java | 34 +++++ .../XLineBreakpointVariantsProvider.java | 30 +++++ 14 files changed, 434 insertions(+), 119 deletions(-) create mode 100644 platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariant.java create mode 100644 platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariantsProvider.java diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java b/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java index f17787b67de6..52665815abb2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java @@ -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; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java index 43b131c92c25..2ed9b5283ada 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java @@ -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 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 lambdas = new ArrayList(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) { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 6d908e1c7476..359f61fdf403 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -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 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 lambdas = new ArrayList(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; + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java index a3c442fb410b..ff1b10d1d8d6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaLineBreakpointType.java @@ -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 implements JavaBreakpointType { +public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase + implements JavaBreakpointType, XLineBreakpointVariantsProvider { public JavaLineBreakpointType() { super("java-line", DebuggerBundle.message("line.breakpoints.tab.title")); } @@ -58,13 +71,13 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase 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 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 res = new SmartList(); + 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; + } + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java index c322f67641d2..8cf083f49710 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/LineBreakpoint.java @@ -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() { + @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) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/RunToCursorBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/RunToCursorBreakpoint.java index 52af16623eb7..9fbc28c0ef49 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/RunToCursorBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/RunToCursorBreakpoint.java @@ -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() { @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); } }); } diff --git a/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java b/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java index 9a892346ac04..1ddf268f5895 100644 --- a/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java +++ b/java/debugger/impl/src/org/jetbrains/java/debugger/breakpoints/properties/JavaLineBreakpointProperties.java @@ -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 { + private Integer myOffset = null; + + @OptionTag("offset") + public Integer getOffset() { + return myOffset; + } + + public void setOffset(Integer offset) { + myOffset = offset; + } } diff --git a/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java b/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java index 831a5fc3c4ae..1b748b7691e6 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java +++ b/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java @@ -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 pointer = diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java index cc2ba00f239f..cb93462706f6 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java @@ -18,6 +18,7 @@ package com.intellij.xdebugger.impl; import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.editor.Caret; @@ -28,12 +29,19 @@ import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.openapi.util.AsyncResult; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Processor; +import com.intellij.util.SmartList; import com.intellij.xdebugger.*; import com.intellij.xdebugger.breakpoints.*; import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; @@ -45,6 +53,8 @@ import com.intellij.xdebugger.frame.XSuspendContext; import com.intellij.xdebugger.frame.XValueContainer; import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; +import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointVariant; +import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointVariantsProvider; import com.intellij.xdebugger.impl.breakpoints.ui.grouping.XBreakpointFileGroupingRule; import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager; import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager; @@ -54,6 +64,7 @@ import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.util.*; /** @@ -107,28 +118,76 @@ public class XDebuggerUtilImpl extends XDebuggerUtil { @NotNull final VirtualFile file, final int line, final boolean temporary) { - toggleAndReturnLineBreakpoint(project, type, file, line, temporary); + XSourcePositionImpl position = XSourcePositionImpl.create(file, line); + if (position != null) { + toggleAndReturnLineBreakpoint(project, type, position, temporary, null); + } } - public static

XLineBreakpoint toggleAndReturnLineBreakpoint(@NotNull final Project project, + @NotNull + public static

AsyncResult toggleAndReturnLineBreakpoint(@NotNull final Project project, @NotNull final XLineBreakpointType

type, - @NotNull final VirtualFile file, - final int line, - final boolean temporary) { - return new WriteAction() { + @NotNull final XSourcePosition position, + final boolean temporary, + final RelativePoint relativePoint) { + return ApplicationManager.getApplication().runWriteAction(new Computable>() { @Override - protected void run(@NotNull final Result result) { - XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager(); + public AsyncResult compute() { + final VirtualFile file = position.getFile(); + final int line = position.getLine(); + final XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager(); XLineBreakpoint

breakpoint = breakpointManager.findBreakpointAtLine(type, file, line); if (breakpoint != null) { breakpointManager.removeBreakpoint(breakpoint); } else { + if (type instanceof XLineBreakpointVariantsProvider) { + final XLineBreakpointVariantsProvider provider = + (XLineBreakpointVariantsProvider)type; + List variants = provider.computeLineBreakpointVariants(project, position); + if (!variants.isEmpty()) { + if (variants.size() > 1 && relativePoint != null) { + final AsyncResult res = new AsyncResult(); + JBPopupFactory.getInstance().createListPopup( + new BaseListPopupStep("Create breakpoint for", variants) { + @NotNull + @Override + public String getTextFor(XLineBreakpointVariant value) { + return value.getText(); + } + + @Override + public Icon getIconFor(XLineBreakpointVariant value) { + return value.getIcon(); + } + + @Override + public PopupStep onChosen(final XLineBreakpointVariant selectedValue, boolean finalChoice) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + P properties = (P)selectedValue.createProperties(); + res.setDone(breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary)); + } + }); + return FINAL_CHOICE; + } + }).show(relativePoint); + return res; + } + else { + P properties = (P)variants.get(0).createProperties(); + return AsyncResult.done( + (XLineBreakpoint)breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary)); + } + } + } P properties = type.createBreakpointProperties(file, line); - result.setResult(breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary)); + return AsyncResult.done((XLineBreakpoint)breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary)); } + return AsyncResult.rejected(); } - }.execute().getResultObject(); + }); } @Override @@ -199,13 +258,10 @@ public class XDebuggerUtilImpl extends XDebuggerUtil { return Collections.emptyList(); } - final Document document = editor.getDocument(); - VirtualFile file = FileDocumentManager.getInstance().getFile(document); - Collection res = new ArrayList(); - List carets = editor.getCaretModel().getAllCarets(); - for (Caret caret : carets) { - int line = caret.getLogicalPosition().line; - XSourcePositionImpl position = XSourcePositionImpl.create(file, line); + VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument()); + List res = new SmartList(); + for (Caret caret : editor.getCaretModel().getAllCarets()) { + XSourcePositionImpl position = XSourcePositionImpl.createByOffset(file, caret.getOffset()); if (position != null) { res.add(position); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java index 78462bbee293..b9a8beafed14 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -65,7 +65,7 @@ public class XToggleLineBreakpointActionHandler extends DebuggerActionHandler { Set processedLines = new HashSet(); for (XSourcePosition position : XDebuggerUtilImpl.getAllCaretsPositions(project, event.getDataContext())) { if (processedLines.add(position.getLine())) { - XBreakpointUtil.toggleLineBreakpoint(project, position.getFile(), editor, position.getLine(), myTemporary, true); + XBreakpointUtil.toggleLineBreakpoint(project, position, editor, myTemporary, true); } } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointUtil.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointUtil.java index 7bfc31989e3b..d5280fc94e6e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointUtil.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -22,16 +22,20 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.FoldRegion; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AsyncResult; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerUtil; +import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.breakpoints.*; import com.intellij.xdebugger.impl.DebuggerSupport; import com.intellij.xdebugger.impl.XDebuggerUtilImpl; +import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointItem; import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointPanelProvider; +import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -130,12 +134,14 @@ public class XBreakpointUtil { * - unfolds folded block on the line * - if folded, checks if line breakpoints could be toggled inside folded text */ - public static XLineBreakpoint toggleLineBreakpoint(Project project, - VirtualFile file, - Editor editor, - int lineStart, + @NotNull + public static AsyncResult toggleLineBreakpoint(@NotNull Project project, + @NotNull XSourcePosition position, + @Nullable Editor editor, boolean temporary, boolean moveCarret) { + int lineStart = position.getLine(); + VirtualFile file = position.getFile(); // for folded text check each line and find out type with the biggest priority int linesEnd = lineStart; if (editor != null) { @@ -147,7 +153,7 @@ public class XBreakpointUtil { final XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager(); XLineBreakpointType[] lineTypes = XDebuggerUtil.getInstance().getLineBreakpointTypes(); - XLineBreakpointType typeWinner = null; + XLineBreakpointType typeWinner = null; int lineWinner = -1; for (int line = lineStart; line <= linesEnd; line++) { int maxPriority = 0; @@ -170,18 +176,22 @@ public class XBreakpointUtil { } if (typeWinner != null) { - XLineBreakpoint res = XDebuggerUtilImpl.toggleAndReturnLineBreakpoint(project, typeWinner, file, lineWinner, temporary); + XSourcePosition winPosition = (lineStart == lineWinner) ? position : XSourcePositionImpl.create(file, lineWinner); + if (winPosition != null) { + AsyncResult res = XDebuggerUtilImpl.toggleAndReturnLineBreakpoint(project, typeWinner, winPosition, temporary, + DebuggerUIUtil.calcPopupLocation(editor, lineWinner)); - if (editor != null && lineStart != lineWinner) { - int offset = editor.getDocument().getLineStartOffset(lineWinner); - ExpandRegionAction.expandRegionAtOffset(project, editor, offset); - if (moveCarret) { - editor.getCaretModel().moveToOffset(offset); + if (editor != null && lineStart != lineWinner) { + int offset = editor.getDocument().getLineStartOffset(lineWinner); + ExpandRegionAction.expandRegionAtOffset(project, editor, offset); + if (moveCarret) { + editor.getCaretModel().moveToOffset(offset); + } } + return res; } - return res; } - return null; + return AsyncResult.rejected(); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java index acf896a8d357..626b22e068f0 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java @@ -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. @@ -38,6 +38,7 @@ import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.AsyncResult; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -45,6 +46,7 @@ import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileUrlChangeAdapter; import com.intellij.psi.PsiDocumentManager; +import com.intellij.util.Consumer; import com.intellij.util.SmartList; import com.intellij.util.containers.BidirectionalMap; import com.intellij.util.ui.update.MergingUpdateQueue; @@ -53,6 +55,7 @@ import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.breakpoints.SuspendPolicy; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.XLineBreakpoint; +import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; @@ -297,20 +300,25 @@ public class XLineBreakpointManager { if (!myProject.isDisposed() && myProject.isInitialized() && file.isValid()) { ActionManagerEx.getInstanceEx().fireBeforeActionPerformed("ToggleLineBreakpoint", e.getMouseEvent()); - XLineBreakpoint breakpoint = - XBreakpointUtil.toggleLineBreakpoint(myProject, file, editor, line, mouseEvent.isAltDown(), false); - if (!mouseEvent.isAltDown() && mouseEvent.isShiftDown() && breakpoint != null) { - breakpoint.setSuspendPolicy(SuspendPolicy.NONE); - String selection = editor.getSelectionModel().getSelectedText(); - if (selection != null) { - breakpoint.setLogExpression(selection); + AsyncResult result = XBreakpointUtil.toggleLineBreakpoint( + myProject, XSourcePositionImpl.create(file, line), editor, mouseEvent.isAltDown(), false); + result.doWhenDone(new Consumer() { + @Override + public void consume(XLineBreakpoint breakpoint) { + if (!mouseEvent.isAltDown() && mouseEvent.isShiftDown() && breakpoint != null) { + breakpoint.setSuspendPolicy(SuspendPolicy.NONE); + String selection = editor.getSelectionModel().getSelectedText(); + if (selection != null) { + breakpoint.setLogExpression(selection); + } + else { + breakpoint.setLogMessage(true); + } + // edit breakpoint + DebuggerUIUtil.showXBreakpointEditorBalloon(myProject, mouseEvent.getPoint(), ((EditorEx)editor).getGutterComponentEx(), false, breakpoint); + } } - else { - breakpoint.setLogMessage(true); - } - // edit breakpoint - DebuggerUIUtil.showXBreakpointEditorBalloon(myProject, mouseEvent.getPoint(), ((EditorEx)editor).getGutterComponentEx(), false, breakpoint); - } + }); } } }); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariant.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariant.java new file mode 100644 index 000000000000..18b4db2b6891 --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariant.java @@ -0,0 +1,34 @@ +/* + * 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. + * 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.xdebugger.impl.breakpoints; + +import com.intellij.openapi.util.TextRange; +import com.intellij.xdebugger.breakpoints.XBreakpointProperties; + +import javax.swing.*; + +/** + * @author egor + */ +public abstract class XLineBreakpointVariant

{ + public abstract String getText(); + + public abstract Icon getIcon(); + + public abstract TextRange getHighlightRange(); + + public abstract P createProperties(); +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariantsProvider.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariantsProvider.java new file mode 100644 index 000000000000..9861ef0f8a29 --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointVariantsProvider.java @@ -0,0 +1,30 @@ +/* + * 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. + * 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.xdebugger.impl.breakpoints; + +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XSourcePosition; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author egor + */ +public interface XLineBreakpointVariantsProvider { + @NotNull + List computeLineBreakpointVariants(@NotNull Project project, @NotNull XSourcePosition position); +}