Skip to content

Commit a4aa8d5

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 a4aa8d5

File tree

7 files changed

+231
-86
lines changed

7 files changed

+231
-86
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
package selectiontests;
15+
16+
import java.util.Arrays;
17+
import java.util.List;
18+
19+
/**
20+
* The test resumes at lambda chain and at each resume expects selecting the next lambda expression.
21+
*/
22+
public class LambdaSelectionTest {
23+
24+
public static void main(String[] main) {
25+
List<String> list = Arrays.asList("A");
26+
list.stream()
27+
.map(s -> s.toLowerCase()).filter(s -> s.equals("b")).forEach(System.out::println); // line 27, test breakpoint is set here
28+
}
29+
}

org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/AbstractDebugUiTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ protected IEditorPart openEditor(String type) throws RuntimeException {
263263
return callInUi(callable);
264264
}
265265

266-
private static <T> T callInUi(Callable<T> callable) throws RuntimeException {
266+
protected static <T> T callInUi(Callable<T> callable) throws RuntimeException {
267267
if (Display.getCurrent() != null) {
268268
try {
269269
return callable.call();
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.IJavaThread;
20+
import org.eclipse.jdt.debug.testplugin.JavaProjectHelper;
21+
import org.eclipse.jdt.debug.tests.TestUtil;
22+
import org.eclipse.jface.text.ITextSelection;
23+
import org.eclipse.test.OrderedTestSuite;
24+
import org.eclipse.ui.texteditor.ITextEditor;
25+
26+
import junit.framework.Test;
27+
28+
public class DebugSelectionTests extends AbstractDebugUiTests {
29+
30+
public static Test suite() {
31+
return new OrderedTestSuite(DebugSelectionTests.class);
32+
}
33+
34+
private IJavaProject project;
35+
36+
public DebugSelectionTests(String name) {
37+
super(name);
38+
}
39+
40+
@Override
41+
protected IJavaProject getProjectContext() {
42+
return project;
43+
}
44+
45+
@Override
46+
public void setUp() throws Exception {
47+
super.setUp();
48+
project = createProject("DebugSelectionTests", "testfiles/DebugSelectionTests/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false);
49+
waitForBuild();
50+
}
51+
52+
@Override
53+
public void tearDown() throws Exception {
54+
closeAllEditors();
55+
project.getProject().delete(true, null);
56+
super.tearDown();
57+
}
58+
59+
/**
60+
* Resume at lambda chain and at each resume expect selecting the next lambda expression.
61+
*/
62+
public void testLambdaEditorSelection() throws Exception {
63+
IJavaThread thread = null;
64+
try {
65+
String typeName = "selectiontests.LambdaSelectionTest";
66+
createLineBreakpoint(27, typeName);
67+
ILaunchConfiguration config = createLaunchConfiguration(project, typeName);
68+
thread = launchAndSuspend(config);
69+
resume(thread);
70+
waitForSelection("s -> s.toLowerCase()", 10_000L);
71+
resume(thread);
72+
waitForSelection("s -> s.equals(\"b\")", 10_000L);
73+
} finally {
74+
removeAllBreakpoints();
75+
if (thread != null) {
76+
terminateAndRemove(thread);
77+
}
78+
}
79+
}
80+
81+
private static void waitForSelection(String expectedSelection, long timeout) throws InterruptedException {
82+
long s = System.currentTimeMillis();
83+
while (System.currentTimeMillis() - s < timeout) {
84+
if (expectedSelection.equals(getActiveEditorSelectionText())) {
85+
return;
86+
}
87+
TestUtil.runEventLoop();
88+
Thread.sleep(50L);
89+
}
90+
assertEquals("Timed out while waiting for selection", expectedSelection, getActiveEditorSelectionText());
91+
}
92+
93+
private static String getActiveEditorSelectionText() {
94+
return callInUi(() -> {
95+
ITextEditor editor = (ITextEditor) getActivePage().getActiveEditor();
96+
if (editor != null) {
97+
ITextSelection selection = (ITextSelection) editor.getSelectionProvider().getSelection();
98+
if (selection != null) {
99+
return selection.getText();
100+
}
101+
}
102+
return "";
103+
});
104+
}
105+
}

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

Lines changed: 16 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,15 @@ 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

164168
public JDIModelPresentation() {
165169
super();
170+
fJavaStackFrameEditorPresenter = new JavaStackFrameEditorPresenter();
166171
}
167172

168173
/* (non-Javadoc)
@@ -2281,4 +2286,14 @@ private void processInLineLambdaLabel(IJavaMethodBreakpoint methodBreakpoint, St
22812286
}
22822287
}
22832288
}
2289+
2290+
@Override
2291+
public boolean addAnnotations(IEditorPart editorPart, IStackFrame frame) {
2292+
return fJavaStackFrameEditorPresenter.addAnnotations(editorPart, frame);
2293+
}
2294+
2295+
@Override
2296+
public void removeAnnotations(IEditorPart editorPart, IThread thread) {
2297+
fJavaStackFrameEditorPresenter.removeAnnotations(editorPart, thread);
2298+
}
22842299
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 IBM Corporation.
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+
* IBM Corporation - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jdt.internal.debug.ui;
15+
16+
import java.util.List;
17+
18+
import org.eclipse.core.runtime.CoreException;
19+
import org.eclipse.debug.core.model.IStackFrame;
20+
import org.eclipse.debug.core.model.IThread;
21+
import org.eclipse.debug.ui.IDebugEditorPresentation;
22+
import org.eclipse.jdt.core.dom.IMethodBinding;
23+
import org.eclipse.jdt.core.dom.LambdaExpression;
24+
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
25+
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
26+
import org.eclipse.jdt.internal.debug.ui.actions.ToggleBreakpointAdapter;
27+
import org.eclipse.jdt.ui.JavaUI;
28+
import org.eclipse.jface.text.BadLocationException;
29+
import org.eclipse.jface.text.IDocument;
30+
import org.eclipse.jface.text.IRegion;
31+
import org.eclipse.ui.IEditorInput;
32+
import org.eclipse.ui.IEditorPart;
33+
import org.eclipse.ui.texteditor.IDocumentProvider;
34+
import org.eclipse.ui.texteditor.ITextEditor;
35+
36+
/**
37+
* Handles specialized selection of Java editors during debugging, such as selecting lambda expressions on a breakpoint hit.
38+
*/
39+
public class JavaStackFrameEditorPresenter implements IDebugEditorPresentation {
40+
41+
@Override
42+
public boolean addAnnotations(IEditorPart editor, IStackFrame frame) {
43+
try {
44+
if (editor instanceof ITextEditor textEditor && frame instanceof JDIStackFrame jdiFrame
45+
&& org.eclipse.jdt.internal.debug.core.model.LambdaUtils.isLambdaFrame(jdiFrame)) {
46+
IEditorInput editorInput = editor.getEditorInput();
47+
IDocumentProvider provider = textEditor.getDocumentProvider();
48+
IDocument document = provider.getDocument(editorInput);
49+
if (document != null && JavaUI.getEditorInputJavaElement(editorInput) != null) {
50+
IRegion region = document.getLineInformation(jdiFrame.getLineNumber() - 1);
51+
List<LambdaExpression> inLineLambdas = ToggleBreakpointAdapter.findLambdaExpressions(textEditor, region);
52+
for (LambdaExpression exp : inLineLambdas) {
53+
String key = getMethodBindingKey(exp);
54+
if (key != null && key.contains(jdiFrame.getName())) {
55+
textEditor.selectAndReveal(exp.getStartPosition(), exp.getLength());
56+
return true;
57+
}
58+
}
59+
}
60+
}
61+
} catch (CoreException | BadLocationException e) {
62+
JDIDebugPlugin.log(e);
63+
}
64+
return false;
65+
}
66+
67+
@Override
68+
public void removeAnnotations(IEditorPart editorPart, IThread thread) {
69+
// nothing to clean up
70+
}
71+
72+
private static String getMethodBindingKey(LambdaExpression exp) {
73+
String key = null;
74+
IMethodBinding methodBinding = exp.resolveMethodBinding();
75+
if (methodBinding != null) {
76+
key = methodBinding.getKey();
77+
}
78+
return key;
79+
}
80+
}

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
}

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

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
 (0)