Skip to content

Commit 87927dd

Browse files
SougandhStrancexpress
authored andcommitted
Fix Possible UI Freeze By Avoiding Source Lookup For Lambda Frames
Handle lambda-specific editor positioning in JDIModelPresentation using IDebugEditorPresentation. Fixes UI lag during step operations.
1 parent 90f2169 commit 87927dd

File tree

5 files changed

+162
-38
lines changed

5 files changed

+162
-38
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package selectiontests;
2+
3+
import java.util.Arrays;
4+
import java.util.List;
5+
6+
/**
7+
* The test resumes at lambda chain and at each resume expects selecting the next lambda expression.
8+
*/
9+
public class LambdaSelectionTest {
10+
11+
public static void main(String[] main) {
12+
List<String> list = Arrays.asList("A");
13+
list.stream()
14+
.map(s -> s.toLowerCase()).filter(s -> s.equals("b")).forEach(System.out::println); // line 14, test breakpoint is set here
15+
}
16+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Simeon Andreev and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Simeon Andreev - initial API and implementation
13+
*******************************************************************************/
14+
15+
package org.eclipse.jdt.debug.tests.ui;
16+
17+
import org.eclipse.debug.core.ILaunchConfiguration;
18+
import org.eclipse.jdt.core.IJavaProject;
19+
import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
20+
import org.eclipse.jdt.debug.core.IJavaThread;
21+
import org.eclipse.jdt.debug.testplugin.JavaProjectHelper;
22+
import org.eclipse.jdt.debug.tests.TestUtil;
23+
import org.eclipse.jface.text.ITextSelection;
24+
import org.eclipse.test.OrderedTestSuite;
25+
import org.eclipse.ui.texteditor.ITextEditor;
26+
27+
import junit.framework.Test;
28+
29+
public class DebugSelectionTests extends AbstractDebugUiTests {
30+
31+
public static Test suite() {
32+
return new OrderedTestSuite(DebugSelectionTests.class);
33+
}
34+
35+
private IJavaProject project;
36+
37+
public DebugSelectionTests(String name) {
38+
super(name);
39+
}
40+
41+
@Override
42+
protected IJavaProject getProjectContext() {
43+
return project;
44+
}
45+
46+
@Override
47+
public void setUp() throws Exception {
48+
super.setUp();
49+
project = createProject("DebugSelectionTests", "testfiles/DebugSelectionTests/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false);
50+
waitForBuild();
51+
}
52+
53+
@Override
54+
public void tearDown() throws Exception {
55+
closeAllEditors();
56+
project.getProject().delete(true, null);
57+
super.tearDown();
58+
}
59+
60+
/**
61+
* Resume at lambda chain and at each resume expect selecting the next lambda expression.
62+
*/
63+
public void testLambdaEditorSelection() throws Exception {
64+
IJavaThread thread = null;
65+
try {
66+
String typeName = "selectiontests.LambdaSelectionTest";
67+
IJavaLineBreakpoint bp = createLineBreakpoint(14, typeName);
68+
bp.setEnabled(true);
69+
ILaunchConfiguration config = createLaunchConfiguration(project, typeName);
70+
thread = launchAndSuspend(config);
71+
resume(thread);
72+
waitForSelection("s -> s.toLowerCase()", 10_000L);
73+
resume(thread);
74+
waitForSelection("s -> s.equals(\"b\")", 10_000L);
75+
} finally {
76+
removeAllBreakpoints();
77+
if (thread != null) {
78+
terminateAndRemove(thread);
79+
}
80+
}
81+
}
82+
83+
private static void waitForSelection(String expectedSelection, long timeout) throws InterruptedException {
84+
long s = System.currentTimeMillis();
85+
while (System.currentTimeMillis() - s < timeout) {
86+
if (expectedSelection.equals(getActiveEditorSelectionText())) {
87+
return;
88+
}
89+
TestUtil.runEventLoop();
90+
Thread.sleep(50L);
91+
}
92+
assertEquals("Timed out while waiting for selection", expectedSelection, getActiveEditorSelectionText());
93+
}
94+
95+
private static String getActiveEditorSelectionText() {
96+
ITextEditor editor = (ITextEditor) getActivePage().getActiveEditor();
97+
if (editor != null) {
98+
ITextSelection selection = (ITextSelection) editor.getSelectionProvider().getSelection();
99+
if (selection != null) {
100+
return selection.getText();
101+
}
102+
}
103+
return "";
104+
}
105+
}

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.eclipse.debug.internal.ui.DefaultLabelProvider;
4343
import org.eclipse.debug.internal.ui.views.variables.VariablesView;
4444
import org.eclipse.debug.ui.DebugUITools;
45+
import org.eclipse.debug.ui.IDebugEditorPresentation;
4546
import org.eclipse.debug.ui.IDebugModelPresentation;
4647
import org.eclipse.debug.ui.IDebugModelPresentationExtension;
4748
import org.eclipse.debug.ui.IDebugUIConstants;
@@ -111,6 +112,7 @@
111112
import org.eclipse.swt.graphics.Point;
112113
import org.eclipse.ui.IEditorDescriptor;
113114
import org.eclipse.ui.IEditorInput;
115+
import org.eclipse.ui.IEditorPart;
114116
import org.eclipse.ui.IViewPart;
115117
import org.eclipse.ui.IWorkbenchPage;
116118
import org.eclipse.ui.IWorkbenchWindow;
@@ -125,7 +127,7 @@
125127
* @see IDebugModelPresentation
126128
*/
127129
@SuppressWarnings("deprecation")
128-
public class JDIModelPresentation extends LabelProvider implements IDebugModelPresentationExtension, IColorProvider {
130+
public class JDIModelPresentation extends LabelProvider implements IDebugModelPresentationExtension, IColorProvider, IDebugEditorPresentation {
129131

130132
/**
131133
* Qualified names presentation property (value <code>"DISPLAY_QUALIFIED_NAMES"</code>).
@@ -157,12 +159,16 @@ public class JDIModelPresentation extends LabelProvider implements IDebugModelPr
157159
* */
158160
private static final String BREAKPOINT_LABEL_SUFFIX = "JDT_BREAKPOINT_LABEL_SUFFIX"; //$NON-NLS-1$
159161

162+
private final JavaStackFrameEditorPresenter fJavaStackFrameEditorPresenter;
163+
160164
private JavaElementLabelProvider fJavaLabelProvider;
161165

162166
private StackFramePresentationProvider fStackFrameProvider;
163167

168+
164169
public JDIModelPresentation() {
165170
super();
171+
fJavaStackFrameEditorPresenter = new JavaStackFrameEditorPresenter();
166172
}
167173

168174
/* (non-Javadoc)
@@ -2281,4 +2287,14 @@ private void processInLineLambdaLabel(IJavaMethodBreakpoint methodBreakpoint, St
22812287
}
22822288
}
22832289
}
2290+
2291+
@Override
2292+
public boolean addAnnotations(IEditorPart editorPart, IStackFrame frame) {
2293+
return fJavaStackFrameEditorPresenter.addAnnotations(editorPart, frame);
2294+
}
2295+
2296+
@Override
2297+
public void removeAnnotations(IEditorPart editorPart, IThread thread) {
2298+
fJavaStackFrameEditorPresenter.removeAnnotations(editorPart, thread);
2299+
}
22842300
}

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/LambdaStackFrameSourceDisplayAdapter.java renamed to org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFrameEditorPresenter.java

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 IBM Corporation.
2+
* Copyright (c) 2026 IBM Corporation.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -11,18 +11,18 @@
1111
* Contributors:
1212
* IBM Corporation - initial API and implementation
1313
*******************************************************************************/
14-
package org.eclipse.jdt.internal.debug.ui.sourcelookup;
14+
package org.eclipse.jdt.internal.debug.ui;
1515

1616
import java.util.List;
1717

1818
import org.eclipse.core.runtime.CoreException;
19-
import org.eclipse.debug.internal.ui.DebugUIPlugin;
20-
import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupFacility;
21-
import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupResult;
22-
import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
19+
import org.eclipse.debug.core.model.IStackFrame;
20+
import org.eclipse.debug.core.model.IThread;
21+
import org.eclipse.debug.ui.IDebugEditorPresentation;
2322
import org.eclipse.jdt.core.IJavaElement;
2423
import org.eclipse.jdt.core.dom.IMethodBinding;
2524
import org.eclipse.jdt.core.dom.LambdaExpression;
25+
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
2626
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
2727
import org.eclipse.jdt.internal.debug.ui.actions.ToggleBreakpointAdapter;
2828
import org.eclipse.jdt.ui.JavaUI;
@@ -31,43 +31,41 @@
3131
import org.eclipse.jface.text.IRegion;
3232
import org.eclipse.ui.IEditorInput;
3333
import org.eclipse.ui.IEditorPart;
34-
import org.eclipse.ui.IWorkbenchPage;
3534
import org.eclipse.ui.texteditor.IDocumentProvider;
3635
import org.eclipse.ui.texteditor.ITextEditor;
3736

38-
/**
39-
* @since 3.2
40-
*/
41-
public class LambdaStackFrameSourceDisplayAdapter implements ISourceDisplay {
37+
public class JavaStackFrameEditorPresenter implements IDebugEditorPresentation {
4238

4339
@Override
44-
public void displaySource(Object element, IWorkbenchPage page, boolean forceSourceLookup) {
45-
JDIStackFrame jdiFrame = (JDIStackFrame) element;
40+
public boolean addAnnotations(IEditorPart editor, IStackFrame frame) {
4641
try {
47-
SourceLookupResult sourceRes = SourceLookupFacility.getDefault().lookup(element, jdiFrame.getLaunch().getSourceLocator(), forceSourceLookup);
48-
IDocumentProvider provider = JavaUI.getDocumentProvider();
49-
IEditorInput editorInput = sourceRes.getEditorInput();
50-
provider.connect(editorInput);
51-
IDocument document = provider.getDocument(editorInput);
52-
IRegion region = document.getLineInformation(jdiFrame.getLineNumber() - 1);
53-
IJavaElement je = JavaUI.getEditorInputJavaElement(editorInput);
54-
if (je != null) {
55-
IEditorPart part = JavaUI.openInEditor(je);
56-
if (part instanceof ITextEditor textEditor) {
42+
if (editor instanceof ITextEditor textEditor && frame instanceof JDIStackFrame jdiFrame
43+
&& org.eclipse.jdt.internal.debug.core.model.LambdaUtils.isLambdaFrame(jdiFrame)) {
44+
IEditorInput editorInput = editor.getEditorInput();
45+
IDocumentProvider provider = textEditor.getDocumentProvider();
46+
IDocument document = provider.getDocument(editorInput);
47+
IRegion region = document.getLineInformation(jdiFrame.getLineNumber() - 1);
48+
IJavaElement javaElement = JavaUI.getEditorInputJavaElement(editorInput);
49+
if (javaElement != null) {
5750
List<LambdaExpression> inLineLambdas = ToggleBreakpointAdapter.findLambdaExpressions(textEditor, region);
5851
for (LambdaExpression exp : inLineLambdas) {
5952
IMethodBinding methodBinding = exp.resolveMethodBinding();
6053
String key = methodBinding.getKey();
6154
if (key.contains(jdiFrame.getName())) {
6255
textEditor.selectAndReveal(exp.getStartPosition(), exp.getLength());
63-
return;
56+
return true;
6457
}
6558
}
6659
}
6760
}
6861
} catch (CoreException | BadLocationException e) {
69-
DebugUIPlugin.log(e);
62+
JDIDebugPlugin.log(e);
7063
}
71-
SourceLookupFacility.getDefault().displaySource(jdiFrame, page, forceSourceLookup);
64+
return false;
65+
}
66+
67+
@Override
68+
public void removeAnnotations(IEditorPart editorPart, IThread thread) {
69+
// nothing to clean up
7270
}
7371
}

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/JavaDebugShowInAdapterFactory.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,10 @@
1414
package org.eclipse.jdt.internal.debug.ui.sourcelookup;
1515

1616
import org.eclipse.core.runtime.IAdapterFactory;
17-
import org.eclipse.debug.core.DebugException;
18-
import org.eclipse.debug.internal.ui.DebugUIPlugin;
1917
import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupFacility;
2018
import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
2119
import org.eclipse.jdt.debug.core.IJavaStackFrame;
2220
import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame;
23-
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
2421
import org.eclipse.ui.part.IShowInSource;
2522
import org.eclipse.ui.part.IShowInTargetList;
2623

@@ -54,14 +51,6 @@ public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
5451
SourceLookupFacility.getDefault().displaySource(frame, page, forceSourceLookup);
5552
};
5653
}
57-
try {
58-
if (adaptableObject instanceof JDIStackFrame jdiFrame
59-
&& org.eclipse.jdt.internal.debug.core.model.LambdaUtils.isLambdaFrame(jdiFrame)) {
60-
return (T) new LambdaStackFrameSourceDisplayAdapter();
61-
}
62-
} catch (DebugException e) {
63-
DebugUIPlugin.log(e);
64-
}
6554
}
6655
return null;
6756
}

0 commit comments

Comments
 (0)