diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/attach/JavaDebuggerAddExceptionBreakpointFilter.java b/java/debugger/impl/src/com/intellij/debugger/impl/attach/JavaDebuggerAddExceptionBreakpointFilter.java new file mode 100644 index 000000000000..f4e7f7d881b9 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/impl/attach/JavaDebuggerAddExceptionBreakpointFilter.java @@ -0,0 +1,69 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.debugger.impl.attach; + +import com.intellij.codeInsight.hints.presentation.InlayPresentation; +import com.intellij.codeInsight.hints.presentation.PresentationFactory; +import com.intellij.codeInsight.hints.presentation.PresentationRenderer; +import com.intellij.debugger.DebuggerManagerEx; +import com.intellij.debugger.ui.breakpoints.ExceptionBreakpoint; +import com.intellij.debugger.ui.breakpoints.JavaExceptionBreakpointType; +import com.intellij.execution.filters.Filter.ResultItem; +import com.intellij.execution.filters.JvmExceptionOccurrenceFilter; +import com.intellij.execution.impl.InlayProvider; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorCustomElementRenderer; +import com.intellij.openapi.editor.impl.EditorImpl; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiClass; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointsDialogFactory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.java.debugger.breakpoints.properties.JavaExceptionBreakpointProperties; + +import java.util.List; +import java.util.Objects; + +public class JavaDebuggerAddExceptionBreakpointFilter implements JvmExceptionOccurrenceFilter { + @Override + public @Nullable ResultItem applyFilter(@NotNull String exceptionClassName, + @NotNull List classes, + int exceptionStartOffset) { + return new CreateExceptionBreakpointResult(exceptionStartOffset, exceptionStartOffset + exceptionClassName.length(), + exceptionClassName); + } + + private static class CreateExceptionBreakpointResult extends ResultItem implements InlayProvider { + private final String myExceptionFqn; + + CreateExceptionBreakpointResult(int highlightStartOffset, int highlightEndOffset, String exceptionFqn) { + super(highlightStartOffset, highlightEndOffset, null); + myExceptionFqn = exceptionFqn; + } + + @Override + public EditorCustomElementRenderer createInlayRenderer(Editor editor) { + PresentationFactory factory = new PresentationFactory((EditorImpl)editor); + InlayPresentation presentation = + factory.referenceOnHover(factory.roundWithBackground(factory.smallText("Create breakpoint")), (event, point) -> { + Project project = editor.getProject(); + XBreakpoint breakpoint = + XDebuggerManager.getInstance(project).getBreakpointManager().getBreakpoints(JavaExceptionBreakpointType.class).stream() + .filter(b -> Objects.equals(myExceptionFqn, b.getProperties().myQualifiedName)).findFirst().orElse(null); + if (breakpoint == null) { + ExceptionBreakpoint ebpt = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager() + .addExceptionBreakpoint(myExceptionFqn, StringUtil.getPackageName(myExceptionFqn)); + if (ebpt != null) { + breakpoint = ebpt.getXBreakpoint(); + } + } + if (breakpoint != null) { + BreakpointsDialogFactory.getInstance(project).showDialog(breakpoint); + } + }); + return new PresentationRenderer(presentation); + } + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/attach/JavaDebuggerConsoleFilterProvider.java b/java/debugger/impl/src/com/intellij/debugger/impl/attach/JavaDebuggerConsoleFilterProvider.java index cc10e303dae4..51610a1f5009 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/attach/JavaDebuggerConsoleFilterProvider.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/attach/JavaDebuggerConsoleFilterProvider.java @@ -4,34 +4,24 @@ package com.intellij.debugger.impl.attach; import com.intellij.codeInsight.hints.presentation.InlayPresentation; import com.intellij.codeInsight.hints.presentation.PresentationFactory; import com.intellij.codeInsight.hints.presentation.PresentationRenderer; -import com.intellij.debugger.DebuggerManagerEx; -import com.intellij.debugger.ui.breakpoints.ExceptionBreakpoint; -import com.intellij.debugger.ui.breakpoints.JavaExceptionBreakpointType; import com.intellij.execution.filters.ConsoleFilterProvider; -import com.intellij.execution.filters.ExceptionInfo; import com.intellij.execution.filters.Filter; import com.intellij.execution.impl.InlayProvider; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorCustomElementRenderer; import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.breakpoints.XBreakpoint; -import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointsDialogFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.java.debugger.breakpoints.properties.JavaExceptionBreakpointProperties; import java.util.Arrays; -import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaDebuggerConsoleFilterProvider implements ConsoleFilterProvider { @Override public Filter @NotNull [] getDefaultFilters(@NotNull Project project) { - return new Filter[]{new JavaDebuggerAttachFilter(), new JavaDebuggerExceptionFilter()}; + return new Filter[]{new JavaDebuggerAttachFilter()}; } private static class JavaDebuggerAttachFilter implements Filter { @@ -75,53 +65,4 @@ public class JavaDebuggerConsoleFilterProvider implements ConsoleFilterProvider return new PresentationRenderer(presentation); } } - - private static class JavaDebuggerExceptionFilter implements Filter { - @Override - public @Nullable Result applyFilter(@NotNull String line, int entireLength) { - ExceptionInfo exceptionInfo = ExceptionInfo.parseMessage(line); - if (exceptionInfo == null) { - return null; - } - String exceptionFqn = exceptionInfo.getExceptionClassName(); - int start = entireLength - line.length() + exceptionInfo.getClassNameOffset(); - - // to trick the code unwrapping single results in com.intellij.execution.filters.CompositeFilter#createFinalResult - return new Result(Arrays.asList( - new CreateExceptionBreakpointResult(start, start + exceptionFqn.length(), exceptionFqn), - new ResultItem(0, 0, null))); - } - } - - private static class CreateExceptionBreakpointResult extends Filter.ResultItem implements InlayProvider { - private final String myExceptionFqn; - - CreateExceptionBreakpointResult(int highlightStartOffset, int highlightEndOffset, String exceptionFqn) { - super(highlightStartOffset, highlightEndOffset, null); - myExceptionFqn = exceptionFqn; - } - - @Override - public EditorCustomElementRenderer createInlayRenderer(Editor editor) { - PresentationFactory factory = new PresentationFactory((EditorImpl)editor); - InlayPresentation presentation = - factory.referenceOnHover(factory.roundWithBackground(factory.smallText("Create breakpoint")), (event, point) -> { - Project project = editor.getProject(); - XBreakpoint breakpoint = - XDebuggerManager.getInstance(project).getBreakpointManager().getBreakpoints(JavaExceptionBreakpointType.class).stream() - .filter(b -> Objects.equals(myExceptionFqn, b.getProperties().myQualifiedName)).findFirst().orElse(null); - if (breakpoint == null) { - ExceptionBreakpoint ebpt = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager() - .addExceptionBreakpoint(myExceptionFqn, StringUtil.getPackageName(myExceptionFqn)); - if (ebpt != null) { - breakpoint = ebpt.getXBreakpoint(); - } - } - if (breakpoint != null) { - BreakpointsDialogFactory.getInstance(project).showDialog(breakpoint); - } - }); - return new PresentationRenderer(presentation); - } - } } diff --git a/java/execution/openapi/src/com/intellij/execution/filters/AdvancedExceptionFilter.java b/java/execution/openapi/src/com/intellij/execution/filters/AdvancedExceptionFilter.java new file mode 100644 index 000000000000..9a9526cc4682 --- /dev/null +++ b/java/execution/openapi/src/com/intellij/execution/filters/AdvancedExceptionFilter.java @@ -0,0 +1,42 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.execution.filters; + +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * This filter includes decorations not only for stack-trace lines, but also for exception names + */ +class AdvancedExceptionFilter extends ExceptionFilter { + AdvancedExceptionFilter(@NotNull GlobalSearchScope scope) { + super(scope); + } + + @Override + @NotNull List getExceptionClassNameItems(ExceptionInfo prevLineException) { + ExceptionInfoCache.ClassResolveInfo info = myCache.resolveClass(prevLineException.getExceptionClassName()); + List classMap = new ArrayList<>(); + info.myClasses.forEach((key, value) -> { + PsiClass psiClass = ObjectUtils.tryCast(value, PsiClass.class); + if (InheritanceUtil.isInheritor(psiClass, CommonClassNames.JAVA_LANG_THROWABLE)) { + classMap.add(psiClass); + } + }); + List exceptionResults = new ArrayList<>(); + if (!classMap.isEmpty()) { + JvmExceptionOccurrenceFilter.EP_NAME.forEachExtensionSafe(filter -> { + ResultItem res = filter.applyFilter(prevLineException.getExceptionClassName(), classMap, prevLineException.getClassNameOffset()); + ContainerUtil.addIfNotNull(exceptionResults, res); + }); + } + return exceptionResults; + } +} diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java index 897adca6ce62..2e9602e185e5 100644 --- a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java @@ -22,6 +22,6 @@ public class ExceptionBaseFilterFactory implements ExceptionFilterFactory { @NotNull @Override public Filter create(@NotNull GlobalSearchScope searchScope) { - return new ExceptionFilter(searchScope); + return new AdvancedExceptionFilter(searchScope); } } diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java index 80672a9bb9c5..e628ed0e4f31 100644 --- a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java @@ -16,15 +16,15 @@ package com.intellij.execution.filters; import com.intellij.openapi.project.DumbAware; -import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; -import java.util.function.Predicate; +import java.util.Collections; +import java.util.List; public class ExceptionFilter implements Filter, DumbAware { - private final ExceptionInfoCache myCache; - private Predicate myNextLineRefiner; + final ExceptionInfoCache myCache; + private ExceptionLineRefiner myNextLineRefiner; public ExceptionFilter(@NotNull final GlobalSearchScope scope) { myCache = new ExceptionInfoCache(scope); @@ -35,12 +35,24 @@ public class ExceptionFilter implements Filter, DumbAware { ExceptionWorker worker = new ExceptionWorker(myCache); Result result = worker.execute(line, textEndOffset, myNextLineRefiner); if (result == null) { - ExceptionInfo exceptionInfo = ExceptionInfo.parseMessage(line); - if (exceptionInfo == null) return null; - myNextLineRefiner = exceptionInfo.getPositionRefiner(); - return exceptionInfo.makeClassLink(myCache, textEndOffset - line.length()); + ExceptionInfo exceptionInfo = ExceptionInfo.parseMessage(line, textEndOffset); + myNextLineRefiner = exceptionInfo == null ? null : exceptionInfo.getPositionRefiner(); + return null; } + ExceptionInfo prevLineException = myNextLineRefiner == null ? null : myNextLineRefiner.getExceptionInfo(); myNextLineRefiner = worker.getLocationRefiner(); + if (prevLineException != null) { + List exceptionResults = getExceptionClassNameItems(prevLineException); + if (!exceptionResults.isEmpty()) { + exceptionResults.add(result); + return new Result(exceptionResults); + } + } return result; } + + @NotNull + List getExceptionClassNameItems(ExceptionInfo prevLineException) { + return Collections.emptyList(); + } } diff --git a/java/execution/openapi/src/com/intellij/execution/filters/JvmExceptionOccurrenceFilter.java b/java/execution/openapi/src/com/intellij/execution/filters/JvmExceptionOccurrenceFilter.java new file mode 100644 index 000000000000..172a5f2234cd --- /dev/null +++ b/java/execution/openapi/src/com/intellij/execution/filters/JvmExceptionOccurrenceFilter.java @@ -0,0 +1,29 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.execution.filters; + +import com.intellij.execution.filters.Filter.ResultItem; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.PsiClass; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Filters occurrence of JVM exception (e.g. provide link to source code) + */ +@ApiStatus.Experimental +public interface JvmExceptionOccurrenceFilter { + ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.jvm.exceptionFilter"); + + /** + * @param exceptionClassName exception class name that occurs in the log + * @param classes non-empty list of resolved class candidates + * @param exceptionStartOffset exception class name start offset in the log + * @return new filtering result item or null if nothing should be returned + */ + @Nullable ResultItem applyFilter(@NotNull String exceptionClassName, + @NotNull List classes, + int exceptionStartOffset); +} diff --git a/java/execution/openapi/src/com/intellij/execution/filters/NavigateToExceptionClassFilter.java b/java/execution/openapi/src/com/intellij/execution/filters/NavigateToExceptionClassFilter.java new file mode 100644 index 000000000000..4d5ec41be446 --- /dev/null +++ b/java/execution/openapi/src/com/intellij/execution/filters/NavigateToExceptionClassFilter.java @@ -0,0 +1,26 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.execution.filters; + +import com.intellij.execution.filters.Filter.ResultItem; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.psi.PsiClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class NavigateToExceptionClassFilter implements JvmExceptionOccurrenceFilter { + @Override + public @Nullable ResultItem applyFilter(@NotNull String exceptionClassName, + @NotNull List classes, + int exceptionStartOffset) { + PsiClass psiClass = classes.get(0); + boolean inContent = + ProjectRootManager.getInstance(psiClass.getProject()).getFileIndex().isInContent(psiClass.getContainingFile().getVirtualFile()); + TextAttributes attributes = ExceptionInfoCache.ClassResolveInfo.getLinkAttributes(!inContent); + HyperlinkInfo hyperlink = HyperlinkInfoFactory.getInstance().createMultiplePsiElementHyperlinkInfo(classes); + return new Filter.Result(exceptionStartOffset, exceptionStartOffset + exceptionClassName.length(), + hyperlink, attributes, attributes); + } +} diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 619b2228e498..7aa2264fd5b0 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -270,6 +270,8 @@ + + @@ -714,6 +716,8 @@ + + diff --git a/java/openapi/src/com/intellij/execution/filters/ExceptionInfo.java b/java/openapi/src/com/intellij/execution/filters/ExceptionInfo.java index 2721daf539c0..aeac8ca7e436 100644 --- a/java/openapi/src/com/intellij/execution/filters/ExceptionInfo.java +++ b/java/openapi/src/com/intellij/execution/filters/ExceptionInfo.java @@ -1,7 +1,6 @@ // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.execution.filters; -import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; @@ -18,7 +17,7 @@ import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ExceptionInfo { +class ExceptionInfo { private static final Pattern AIOOBE_MESSAGE = Pattern.compile("(?:Index )?(\\d{1,9})(?: out of bounds for length \\d+)?"); private static final Pattern CCE_MESSAGE = Pattern.compile("(?:class )?(\\S+) cannot be cast to (?:class )?(\\S+)(?: \\(.+\\))?"); // See JEP 358 Helpful NullPointerExceptions for details @@ -46,41 +45,35 @@ public class ExceptionInfo { myExceptionMessage = exceptionMessage; } - @NotNull ExceptionInfo withMessage(@Nullable String message) { + @NotNull ExceptionInfo adjust(@Nullable String message, int offset) { message = StringUtil.notNullize(message); - return message.equals(myExceptionMessage) ? this : new ExceptionInfo(myClassNameOffset, myExceptionClassName, message); + return new ExceptionInfo(myClassNameOffset + offset, myExceptionClassName, message); } - /** - * @param cache cache to use - * @param offset line start offset - * @return a hyperlink to exception class; null if class cannot be resolved - */ - @Nullable Filter.Result makeClassLink(ExceptionInfoCache cache, int offset) { - ExceptionInfoCache.ClassResolveInfo info = cache.resolveClass(getExceptionClassName()); - if (info.myClasses.isEmpty()) return null; - TextAttributes attributes = info.getLinkAttributes(); - HyperlinkInfo hyperlink = HyperlinkInfoFactory.getInstance().createMultiplePsiElementHyperlinkInfo(info.myClasses); - int base = myClassNameOffset + offset; - return new Filter.Result(base, base + getExceptionClassName().length(), hyperlink, attributes, attributes); - } - - /** * @return a predicate that matches an element within the source line that is likely an exception source */ - @NotNull Predicate getPositionRefiner() { - Predicate exceptionCreationFilter = e -> { - // We look for new Exception() expression rather than throw statement, because stack-trace is filled in exception constructor - if (!(e instanceof PsiKeyword) || !(e.textMatches(PsiKeyword.NEW))) return false; - PsiNewExpression newExpression = ObjectUtils.tryCast(e.getParent(), PsiNewExpression.class); - if (newExpression == null) return false; - PsiType type = newExpression.getType(); - return type != null && type.equalsToText(getExceptionClassName()); + @NotNull ExceptionLineRefiner getPositionRefiner() { + Predicate specificFilter = getExceptionSpecificFilter(); + return new ExceptionLineRefiner() { + @Override + public boolean test(PsiElement element) { + // We look for new Exception() expression rather than throw statement, because stack-trace is filled in exception constructor + if (element instanceof PsiKeyword && element.textMatches(PsiKeyword.NEW)) { + PsiNewExpression newExpression = ObjectUtils.tryCast(element.getParent(), PsiNewExpression.class); + if (newExpression != null) { + PsiType type = newExpression.getType(); + if (type != null && type.equalsToText(getExceptionClassName())) return true; + } + } + return specificFilter != null && specificFilter.test(element); + } + + @Override + public ExceptionInfo getExceptionInfo() { + return ExceptionInfo.this; + } }; - Predicate specificFilter = this.getExceptionSpecificFilter(); - if (specificFilter == null) return exceptionCreationFilter; - return exceptionCreationFilter.or(specificFilter); } @Nullable @@ -369,7 +362,7 @@ public class ExceptionInfo { } @Nullable - public static ExceptionInfo parseMessage(String line) { + public static ExceptionInfo parseMessage(String line, int textEndOffset) { int firstSpace = line.indexOf(' '); int colonPos = -1; ExceptionInfo info = null; @@ -397,7 +390,7 @@ public class ExceptionInfo { } if (info == null) return null; String message = colonPos == -1 ? null : line.substring(colonPos + 1).trim(); - return info.withMessage(message); + return info.adjust(message, textEndOffset - line.length()); } private static int getLength(String line) { diff --git a/java/openapi/src/com/intellij/execution/filters/ExceptionInfoCache.java b/java/openapi/src/com/intellij/execution/filters/ExceptionInfoCache.java index 24b3fc822c84..84905be6b6a5 100644 --- a/java/openapi/src/com/intellij/execution/filters/ExceptionInfoCache.java +++ b/java/openapi/src/com/intellij/execution/filters/ExceptionInfoCache.java @@ -82,9 +82,9 @@ public class ExceptionInfoCache { myInLibrary = library; } - TextAttributes getLinkAttributes() { + static TextAttributes getLinkAttributes(boolean inLibrary) { TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); - if (myInLibrary) { + if (inLibrary) { Color libTextColor = UIUtil.getInactiveTextColor(); attributes = attributes.clone(); attributes.setForegroundColor(libTextColor); diff --git a/java/openapi/src/com/intellij/execution/filters/ExceptionLineRefiner.java b/java/openapi/src/com/intellij/execution/filters/ExceptionLineRefiner.java new file mode 100644 index 000000000000..bc9e05bc580a --- /dev/null +++ b/java/openapi/src/com/intellij/execution/filters/ExceptionLineRefiner.java @@ -0,0 +1,16 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.execution.filters; + +import com.intellij.psi.PsiElement; + +import java.util.function.Predicate; + +/** + * Allows to refine the stacktrace navigation position based on the previous line + */ +@FunctionalInterface +public interface ExceptionLineRefiner extends Predicate { + default ExceptionInfo getExceptionInfo() { + return null; + } +} diff --git a/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java index e7f33721ae26..ba31907918fc 100644 --- a/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java +++ b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java @@ -50,7 +50,7 @@ public class ExceptionWorker { private String myMethod; private ParsedLine myInfo; private final ExceptionInfoCache myCache; - private Predicate myLocationRefiner; + private ExceptionLineRefiner myLocationRefiner; public ExceptionWorker(@NotNull ExceptionInfoCache cache) { myProject = cache.getProject(); @@ -61,7 +61,7 @@ public class ExceptionWorker { return execute(line, textEndOffset, null); } - public Filter.Result execute(@NotNull String line, final int textEndOffset, @Nullable Predicate elementMatcher) { + public Filter.Result execute(@NotNull String line, final int textEndOffset, @Nullable ExceptionLineRefiner elementMatcher) { myResult = null; myInfo = parseExceptionLine(line); if (myInfo == null || myProject.isDisposed()) { @@ -93,7 +93,7 @@ public class ExceptionWorker { int highlightEndOffset = textStartOffset + myInfo.fileLineRange.getEndOffset(); List virtualFiles = new ArrayList<>(myClassResolveInfo.myClasses.keySet()); - TextAttributes attributes = myClassResolveInfo.getLinkAttributes(); + TextAttributes attributes = ExceptionInfoCache.ClassResolveInfo.getLinkAttributes(myClassResolveInfo.myInLibrary); ToIntFunction columnFinder = elementMatcher == null || myInfo.lineNumber <= 0 ? null : new ExceptionColumnFinder(elementMatcher, myInfo.lineNumber - 1); HyperlinkInfo linkInfo = @@ -112,7 +112,7 @@ public class ExceptionWorker { return result; } - public Predicate getLocationRefiner() { + public ExceptionLineRefiner getLocationRefiner() { return myLocationRefiner; } @@ -309,7 +309,7 @@ public class ExceptionWorker { } } - private static class StackFrameMatcher implements Predicate { + private static class StackFrameMatcher implements ExceptionLineRefiner { private final String myMethodName; private final String myClassName; private final boolean myHasDollarInName; @@ -394,7 +394,7 @@ public class ExceptionWorker { } } - private static class FunctionCallMatcher implements Predicate { + private static class FunctionCallMatcher implements ExceptionLineRefiner { @Override public boolean test(@NotNull PsiElement element) { if (!(element instanceof PsiIdentifier)) return false; diff --git a/platform/lang-api/src/com/intellij/execution/filters/HyperlinkInfoFactory.java b/platform/lang-api/src/com/intellij/execution/filters/HyperlinkInfoFactory.java index ddf8816dd707..d44bfba37a19 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/HyperlinkInfoFactory.java +++ b/platform/lang-api/src/com/intellij/execution/filters/HyperlinkInfoFactory.java @@ -22,8 +22,8 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.function.ToIntFunction; public abstract class HyperlinkInfoFactory { @@ -51,11 +51,11 @@ public abstract class HyperlinkInfoFactory { ToIntFunction columnFinder); /** - * Creates a hyperlink that points to several files with ability to navigate to specific element within the file + * Creates a hyperlink that points to elements with ability to navigate to specific element within the file * - * @param fileToElement map from files to elements + * @param elements elements list * @return newly create HyperlinkInfo that navigates to given psi elements */ @NotNull - public abstract HyperlinkInfo createMultiplePsiElementHyperlinkInfo(@NotNull Map fileToElement); + public abstract HyperlinkInfo createMultiplePsiElementHyperlinkInfo(@NotNull Collection elements); } diff --git a/platform/lang-impl/src/com/intellij/execution/filters/impl/HyperlinkInfoFactoryImpl.java b/platform/lang-impl/src/com/intellij/execution/filters/impl/HyperlinkInfoFactoryImpl.java index 6640309b19f3..c6218d0bda93 100644 --- a/platform/lang-impl/src/com/intellij/execution/filters/impl/HyperlinkInfoFactoryImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/filters/impl/HyperlinkInfoFactoryImpl.java @@ -24,8 +24,8 @@ import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.function.ToIntFunction; public class HyperlinkInfoFactoryImpl extends HyperlinkInfoFactory { @@ -47,7 +47,7 @@ public class HyperlinkInfoFactoryImpl extends HyperlinkInfoFactory { } @Override - public @NotNull HyperlinkInfo createMultiplePsiElementHyperlinkInfo(@NotNull Map fileToElement) { - return new MultiPsiElementHyperlinkInfo(fileToElement); + public @NotNull HyperlinkInfo createMultiplePsiElementHyperlinkInfo(@NotNull Collection elements) { + return new MultiPsiElementHyperlinkInfo(elements); } } diff --git a/platform/lang-impl/src/com/intellij/execution/filters/impl/MultiPsiElementHyperlinkInfo.java b/platform/lang-impl/src/com/intellij/execution/filters/impl/MultiPsiElementHyperlinkInfo.java index 61848d8137cb..d5408689b68d 100644 --- a/platform/lang-impl/src/com/intellij/execution/filters/impl/MultiPsiElementHyperlinkInfo.java +++ b/platform/lang-impl/src/com/intellij/execution/filters/impl/MultiPsiElementHyperlinkInfo.java @@ -20,22 +20,21 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; class MultiPsiElementHyperlinkInfo extends HyperlinkInfoBase { private final Map> myMap; - MultiPsiElementHyperlinkInfo(Map pointers) { + MultiPsiElementHyperlinkInfo(Collection elements) { SmartPointerManager manager = null; myMap = new LinkedHashMap<>(); - for (Map.Entry entry : pointers.entrySet()) { - VirtualFile file = entry.getKey(); - PsiElement element = entry.getValue(); + for (PsiElement element : elements) { if (manager == null) { manager = SmartPointerManager.getInstance(element.getProject()); } - myMap.put(file, manager.createSmartPsiElementPointer(element)); + myMap.put(element.getContainingFile().getVirtualFile(), manager.createSmartPsiElementPointer(element)); } }