diff --git a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF index d091a30d65..a6398191f7 100644 --- a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.debug.tests; singleton:=true -Bundle-Version: 3.12.500.qualifier +Bundle-Version: 3.12.600.qualifier Bundle-ClassPath: javadebugtests.jar Bundle-Activator: org.eclipse.jdt.debug.testplugin.JavaTestPlugin Bundle-Vendor: %providerName diff --git a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java new file mode 100644 index 0000000000..63af9de5e4 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - Implementation + *******************************************************************************/ +package org.eclipse.jdt.debug.tests.console; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.debug.testplugin.JavaProjectHelper; +import org.eclipse.jdt.debug.tests.TestUtil; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.console.IHyperlink; +import org.eclipse.ui.texteditor.ITextEditor; + +public class JavaStackTraceAmbiguityTest extends AbstractJavaStackTraceConsoleTest { + + public JavaStackTraceAmbiguityTest(String name) { + super(name); + } + + public void testLinkAmbiguityNavigationTrue() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/Ambiguity/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tes3(int) line: 31"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + private void waitForJobs() throws Exception { + TestUtil.waitForJobs(getName(), 250, 1000); + } + + private static String getSelectedText(IEditorPart editor) { + ITextEditor textEditor = (ITextEditor) editor; + ISelectionProvider selectionProvider = textEditor.getSelectionProvider(); + ISelection selection = selectionProvider.getSelection(); + ITextSelection textSelection = (ITextSelection) selection; + String selectedText = textSelection.getText(); + return selectedText; + } + + private IEditorPart waitForEditorOpen() throws Exception { + waitForJobs(); + IEditorPart[] editor = new IEditorPart[1]; + sync(() -> editor[0] = getActivePage().getActiveEditor()); + long timeout = 30_000; + long start = System.currentTimeMillis(); + while (editor[0] == null && System.currentTimeMillis() - start < timeout) { + waitForJobs(); + sync(() -> editor[0] = getActivePage().getActiveEditor()); + } + if (editor[0] == null) { + throw new AssertionError("Timeout occurred while waiting for editor to open"); + } + return editor[0]; + } +} diff --git a/org.eclipse.jdt.debug.tests/pom.xml b/org.eclipse.jdt.debug.tests/pom.xml index 6fd71e5434..bfedb46d53 100644 --- a/org.eclipse.jdt.debug.tests/pom.xml +++ b/org.eclipse.jdt.debug.tests/pom.xml @@ -18,7 +18,7 @@ org.eclipse.jdt org.eclipse.jdt.debug.tests - 3.12.500-SNAPSHOT + 3.12.600-SNAPSHOT eclipse-test-plugin ${project.artifactId} diff --git a/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/a/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/a/Sample.java new file mode 100644 index 0000000000..5e0e2861da --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/a/Sample.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package a; + +/** + * Test class + */ +public class Sample { + public static void main(String[] args) { + System.out.println("Main Method"); + test(); + } + public static void test() { + System.out.println("Testing.."); + } + public void testMethod() { + System.out.println("Random"); + } + public static void tes3(int x) { + System.out.println("Expected"); + } + public void tes2() { + + } + +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/b/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/b/Sample.java new file mode 100644 index 0000000000..8c47728c5d --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/b/Sample.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package b; + +/** + * Test class + */ +public class Sample { + public static void main(String[] args) { + System.out.println("Main Method"); + test(); + } + public static void test() { + System.out.println("Testing.."); + } + public void testMethod() { + System.out.println("Random"); + } + public static void tes3() { + System.out.println("Expected"); + } + public void tes2() { + + } + +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/c/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/c/Sample.java new file mode 100644 index 0000000000..6c3f2c5782 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/Ambiguity/c/Sample.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package c; + +/** + * Test class + */ +public class Sample { + public static void main(String[] args) { + System.out.println("Main Method"); + test(); + } + public static void test() { + System.out.println("Testing.."); + } + public void testMethod() { + System.out.println("Random"); + } + public static void tes32() { + System.out.println("Expected"); + } + public void tes2() { + + } + +} diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java index c9615813f3..d9b9f3eff1 100644 --- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java @@ -56,6 +56,7 @@ import org.eclipse.jdt.debug.tests.console.ConsoleTerminateAllActionTests; import org.eclipse.jdt.debug.tests.console.IOConsoleTests; import org.eclipse.jdt.debug.tests.console.JavaDebugStackTraceConsoleTest; +import org.eclipse.jdt.debug.tests.console.JavaStackTraceAmbiguityTest; import org.eclipse.jdt.debug.tests.console.JavaStackTraceConsoleTest; import org.eclipse.jdt.debug.tests.core.AlternateStratumTests; import org.eclipse.jdt.debug.tests.core.ArgumentTests; @@ -273,6 +274,7 @@ public AutomatedSuite() { addTest(new TestSuite(JavaDebugStackTraceConsoleTest.class)); addTest(new TestSuite(IOConsoleTests.class)); addTest(new TestSuite(ConsoleTerminateAllActionTests.class)); + addTest(new TestSuite(JavaStackTraceAmbiguityTest.class)); //Core tests addTest(new TestSuite(DebugEventTests.class)); diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java index 63d2e41fc5..82a1a03657 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2017 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,7 +14,6 @@ *******************************************************************************/ package org.eclipse.jdt.internal.debug.ui.console; - import java.util.ArrayList; import java.util.List; @@ -27,12 +26,15 @@ import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.ui.IDebugModelPresentation; import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.TypeNameMatch; import org.eclipse.jdt.core.search.TypeNameMatchRequestor; +import org.eclipse.jdt.internal.core.SourceType; import org.eclipse.jdt.internal.debug.core.JavaDebugUtils; import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; import org.eclipse.jdt.internal.debug.ui.actions.OpenFromClipboardAction; @@ -57,10 +59,13 @@ public class JavaStackTraceHyperlink implements IHyperlink { private final TextConsole fConsole; + private String originalHyperLink; /** * Constructor - * @param console the {@link TextConsole} this link detector is attached to + * + * @param console + * the {@link TextConsole} this link detector is attached to */ public JavaStackTraceHyperlink(TextConsole console) { fConsole = console; @@ -86,15 +91,16 @@ public void linkExited() { @Override public void linkActivated() { String typeName; - int lineNumber; - try { - String linkText = getLinkText(); - typeName = getTypeName(linkText); - lineNumber = getLineNumber(linkText); - } catch (CoreException e1) { - ErrorDialog.openError(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Error, ConsoleMessages.JavaStackTraceHyperlink_Error, e1.getStatus()); - return; - } + int lineNumber; + try { + String linkText = getLinkText(); + originalHyperLink = linkText; + typeName = getTypeName(linkText); + lineNumber = getLineNumber(linkText); + } catch (CoreException e1) { + ErrorDialog.openError(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Error, ConsoleMessages.JavaStackTraceHyperlink_Error, e1.getStatus()); + return; + } // documents start at 0 if (lineNumber > 0) { @@ -106,8 +112,10 @@ public void linkActivated() { /** * Starts a search for the type with the given name. Reports back to 'searchCompleted(...)'. * - * @param typeName the type to search for - * @param lineNumber the line number to open the editor on + * @param typeName + * the type to search for + * @param lineNumber + * the line number to open the editor on */ protected void startSourceSearch(final String typeName, final int lineNumber) { Job search = new Job(ConsoleMessages.JavaStackTraceHyperlink_2) { @@ -168,13 +176,18 @@ public void acceptTypeNameMatch(TypeNameMatch match) { searchEngine.searchAllTypeNames(qualifications, typeNames, SearchEngine.createWorkspaceScope(), requestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null); return matchingTypes; } + /** * Reported back to from {@link JavaStackTraceHyperlink#startSourceSearch(String, int)} when results are found * - * @param source the source object - * @param typeName the fully qualified type name - * @param lineNumber the line number in the type - * @param status the error status or null if none + * @param source + * the source object + * @param typeName + * the fully qualified type name + * @param lineNumber + * the line number in the type + * @param status + * the error status or null if none */ protected void searchCompleted(final Object source, final String typeName, final int lineNumber, final IStatus status) { UIJob job = new UIJob("link search complete") { //$NON-NLS-1$ @@ -183,15 +196,45 @@ public IStatus runInUIThread(IProgressMonitor monitor) { if (source == null) { if (status == null) { // did not find source - MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Information_1, NLS.bind(ConsoleMessages.JavaStackTraceHyperlink_Source_not_found_for__0__2, new String[] {typeName})); + MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Information_1, NLS.bind(ConsoleMessages.JavaStackTraceHyperlink_Source_not_found_for__0__2, new String[] { + typeName })); } else { JDIDebugUIPlugin.statusDialog(ConsoleMessages.JavaStackTraceHyperlink_3, status); } } else if (source instanceof List) { // ambiguous @SuppressWarnings("unchecked") List matches = (List) source; + List exactMatchesFiltered = new ArrayList<>(); + String originalHyperLink2 = originalHyperLink; + int firstMethodStartIndex = originalHyperLink2.indexOf('.'); + int firstMethodClosing = originalHyperLink2.indexOf(')'); + if (firstMethodStartIndex != -1 && firstMethodClosing != -1) { + String firstMethod = originalHyperLink2.substring(firstMethodStartIndex + 1, firstMethodClosing + 1); + for (Object obj : matches) { + @SuppressWarnings("restriction") + SourceType source = (SourceType) obj; + try { + @SuppressWarnings("restriction") + IMethod[] methods = source.getMethods(); + for (IMethod method : methods) { + int indexOfClosing = method.toString().indexOf(')'); + int indexOfStart = method.toString().substring(0, indexOfClosing + 1).lastIndexOf(' '); + String methodName = method.toString().substring(indexOfStart + 1, indexOfClosing + 1); + if (methodName.equals(firstMethod)) { + exactMatchesFiltered.add(obj); + } + } + } catch (JavaModelException e) { + JDIDebugUIPlugin.log(e); + } + } + } + if (exactMatchesFiltered.size() == 1) { + processSearchResult(exactMatchesFiltered.get(0), typeName, lineNumber); + return Status.OK_STATUS; + } int line = lineNumber + 1; // lineNumber starts with 0, but line with 1, see #linkActivated - OpenFromClipboardAction.handleMatches(matches, line, typeName, ConsoleMessages.JavaDebugStackTraceHyperlink_dialog_title); + OpenFromClipboardAction.handleMatches(exactMatchesFiltered, line, typeName, ConsoleMessages.JavaDebugStackTraceHyperlink_dialog_title); return Status.OK_STATUS; } else { processSearchResult(source, typeName, lineNumber); @@ -206,9 +249,12 @@ public IStatus runInUIThread(IProgressMonitor monitor) { /** * The search succeeded with the given result * - * @param source resolved source object for the search - * @param typeName type name searched for - * @param lineNumber line number on link + * @param source + * resolved source object for the search + * @param typeName + * type name searched for + * @param lineNumber + * line number on link */ protected void processSearchResult(Object source, String typeName, int lineNumber) { IDebugModelPresentation presentation = JDIDebugUIPlugin.getDefault().getModelPresentation(); @@ -219,7 +265,7 @@ protected void processSearchResult(Object source, String typeName, int lineNumbe try { IEditorPart editorPart = JDIDebugUIPlugin.getActivePage().openEditor(editorInput, editorId); if (editorPart instanceof ITextEditor && lineNumber >= 0) { - ITextEditor textEditor = (ITextEditor)editorPart; + ITextEditor textEditor = (ITextEditor) editorPart; IDocumentProvider provider = textEditor.getDocumentProvider(); provider.connect(editorInput); IDocument document = provider.getDocument(editorInput); @@ -227,7 +273,8 @@ protected void processSearchResult(Object source, String typeName, int lineNumbe IRegion line = document.getLineInformation(lineNumber); textEditor.selectAndReveal(line.getOffset(), line.getLength()); } catch (BadLocationException e) { - MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_0, NLS.bind("{0}{1}{2}", new String[] {(lineNumber+1)+"", ConsoleMessages.JavaStackTraceHyperlink_1, typeName})); //$NON-NLS-2$ //$NON-NLS-1$ + MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_0, NLS.bind("{0}{1}{2}", new String[] { //$NON-NLS-1$ + (lineNumber + 1) + "", ConsoleMessages.JavaStackTraceHyperlink_1, typeName })); //$NON-NLS-1$ } provider.disconnect(editorInput); } @@ -239,16 +286,14 @@ protected void processSearchResult(Object source, String typeName, int lineNumbe } /** - * Returns the launch associated with this hyper-link, or - * null if none + * Returns the launch associated with this hyper-link, or null if none * - * @return the launch associated with this hyper-link, or - * null if none + * @return the launch associated with this hyper-link, or null if none */ private ILaunch getLaunch() { IProcess process = (IProcess) getConsole().getAttribute(IDebugUIConstants.ATTR_CONSOLE_PROCESS); if (process != null) { - return process.getLaunch(); + return process.getLaunch(); } return null; } @@ -264,54 +309,56 @@ private ILaunch getLaunch() { */ protected String getTypeName(String linkText) throws CoreException { int start = linkText.lastIndexOf('('); - int end = linkText.indexOf(':'); - if (start >= 0 && end > start) { - //linkText could be something like packageA.TypeB(TypeA.java:45) - //need to look in packageA.TypeA for line 45 since TypeB is defined - //in TypeA.java - //Inner classes can be ignored because we're using file and line number + int end = linkText.indexOf(':'); + if (start >= 0 && end > start) { + // linkText could be something like packageA.TypeB(TypeA.java:45) + // need to look in packageA.TypeA for line 45 since TypeB is defined + // in TypeA.java + // Inner classes can be ignored because we're using file and line number - // get File name (w/o .java) - String typeName = linkText.substring(start + 1, end); - typeName = JavaCore.removeJavaLikeExtension(typeName); + // get File name (w/o .java) + String typeName = linkText.substring(start + 1, end); + typeName = JavaCore.removeJavaLikeExtension(typeName); typeName = removeModuleInfo(typeName); - String qualifier = linkText.substring(0, start); - // remove the method name - start = qualifier.lastIndexOf('.'); + String qualifier = linkText.substring(0, start); + // remove the method name + start = qualifier.lastIndexOf('.'); - if (start >= 0) { - // remove the class name - start = new String((String) qualifier.subSequence(0, start)).lastIndexOf('.'); - if (start == -1) { - start = 0; // default package - } - } + if (start >= 0) { + // remove the class name + start = new String((String) qualifier.subSequence(0, start)).lastIndexOf('.'); + if (start == -1) { + start = 0; // default package + } + } - if (start >= 0) { - qualifier = qualifier.substring(0, start); - } + if (start >= 0) { + qualifier = qualifier.substring(0, start); + } - if (qualifier.length() > 0) { - typeName = qualifier + "." + typeName; //$NON-NLS-1$ - } - // Remove the module name if exists + if (qualifier.length() > 0) { + typeName = qualifier + "." + typeName; //$NON-NLS-1$ + } + // Remove the module name if exists int index = typeName.lastIndexOf('/'); if (index != -1) { typeName = typeName.substring(index + 1); } - return typeName; - } - IStatus status = new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), 0, ConsoleMessages.JavaStackTraceHyperlink_Unable_to_parse_type_name_from_hyperlink__5, null); - throw new CoreException(status); - } + return typeName; + } + IStatus status = new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), 0, ConsoleMessages.JavaStackTraceHyperlink_Unable_to_parse_type_name_from_hyperlink__5, null); + throw new CoreException(status); + } /** * Returns the line number associated with the stack trace or -1 if none. * - * @param linkText the complete text of the link to be parsed + * @param linkText + * the complete text of the link to be parsed * @return the line number for the stack trace or -1 if one cannot be computed or has not been provided - * @exception CoreException if unable to parse the number + * @exception CoreException + * if unable to parse the number */ protected int getLineNumber(String linkText) throws CoreException { int index = linkText.lastIndexOf(':'); @@ -345,28 +392,29 @@ protected TextConsole getConsole() { * Returns this link's text * * @return the complete text of the link, never null - * @exception CoreException if unable to retrieve the text + * @exception CoreException + * if unable to retrieve the text */ protected String getLinkText() throws CoreException { - try { - IDocument document = getConsole().getDocument(); - IRegion region = getConsole().getRegion(this); - int regionOffset = region.getOffset(); + try { + IDocument document = getConsole().getDocument(); + IRegion region = getConsole().getRegion(this); + int regionOffset = region.getOffset(); - int lineNumber = document.getLineOfOffset(regionOffset); - IRegion lineInformation = document.getLineInformation(lineNumber); - int lineOffset = lineInformation.getOffset(); - String line = document.get(lineOffset, lineInformation.getLength()); + int lineNumber = document.getLineOfOffset(regionOffset); + IRegion lineInformation = document.getLineInformation(lineNumber); + int lineOffset = lineInformation.getOffset(); + String line = document.get(lineOffset, lineInformation.getLength()); - int regionOffsetInLine = regionOffset - lineOffset; + int regionOffsetInLine = regionOffset - lineOffset; - int linkEnd = line.indexOf(')', regionOffsetInLine); - int linkStart = line.lastIndexOf(' ', regionOffsetInLine); + int linkEnd = line.indexOf(')', regionOffsetInLine); + int linkStart = line.lastIndexOf(' ', regionOffsetInLine); if (linkStart == -1) { linkStart = line.lastIndexOf('\t', regionOffsetInLine); } - return line.substring(linkStart==-1?0:linkStart+1,linkEnd+1).trim(); + return line.substring(linkStart == -1 ? 0 : linkStart + 1, linkEnd + 1).trim(); } catch (BadLocationException e) { IStatus status = new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), 0, ConsoleMessages.JavaStackTraceHyperlink_Unable_to_retrieve_hyperlink_text__8, e); throw new CoreException(status);