From 0ca47d1322f97090cb714970662df5bf165a7607 Mon Sep 17 00:00:00 2001 From: sougandhs Date: Wed, 19 Nov 2025 12:34:30 +0530 Subject: [PATCH] Support precise lambda highlighting for inline chained lambdas Highlights the specific lambda in the source when a lambda stack frame is selected by introducing a new lambda stackframe adapter --- .../ui/actions/ToggleBreakpointAdapter.java | 11 ++- .../JavaDebugShowInAdapterFactory.java | 15 +++- .../LambdaStackFrameSourceDisplayAdapter.java | 73 +++++++++++++++++++ 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/LambdaStackFrameSourceDisplayAdapter.java diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java index 59d4236b05..b6aa7b97c7 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java @@ -1737,7 +1737,16 @@ private static void toggleLambdaBreakpoint(IWorkbenchPart part, ITextSelection t } } - private static List findLambdaExpressions(ITextEditor editor, IRegion region) { + /** + * Finds lambda expressions within the given region of the editor. + * + * @param editor + * the text editor to search + * @param region + * the region in which lambda expressions should be collected + * @return a list of lambda expressions found in the region, or an empty list if none are found + */ + public static List findLambdaExpressions(ITextEditor editor, IRegion region) { LambdaCollector lambdas = new LambdaCollector(region.getOffset(), region.getOffset() + region.getLength()); CompilationUnit unitForLambdas = parseCompilationUnit(editor); if (unitForLambdas == null) { diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/JavaDebugShowInAdapterFactory.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/JavaDebugShowInAdapterFactory.java index 4c7f3855e3..7115e36780 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/JavaDebugShowInAdapterFactory.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/JavaDebugShowInAdapterFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2015 IBM Corporation and others. + * Copyright (c) 2006, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,10 +14,13 @@ package org.eclipse.jdt.internal.debug.ui.sourcelookup; import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupFacility; import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; import org.eclipse.jdt.debug.core.IJavaStackFrame; import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame; +import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame; import org.eclipse.ui.part.IShowInSource; import org.eclipse.ui.part.IShowInTargetList; @@ -43,6 +46,7 @@ public T getAdapter(Object adaptableObject, Class adapterType) { return (T) new StackFrameShowInTargetListAdapter(); } } + if (adapterType == ISourceDisplay.class) { if (adaptableObject instanceof GroupedStackFrame groupedFrames) { return (T) (ISourceDisplay) (element, page, forceSourceLookup) -> { @@ -50,7 +54,14 @@ public T getAdapter(Object adaptableObject, Class adapterType) { SourceLookupFacility.getDefault().displaySource(frame, page, forceSourceLookup); }; } - + try { + if (adaptableObject instanceof JDIStackFrame jdiFrame + && org.eclipse.jdt.internal.debug.core.model.LambdaUtils.isLambdaFrame(jdiFrame)) { + return (T) new LambdaStackFrameSourceDisplayAdapter(); + } + } catch (DebugException e) { + DebugUIPlugin.log(e); + } } return null; } diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/LambdaStackFrameSourceDisplayAdapter.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/LambdaStackFrameSourceDisplayAdapter.java new file mode 100644 index 0000000000..d471414e43 --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/LambdaStackFrameSourceDisplayAdapter.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2025 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 org.eclipse.jdt.internal.debug.ui.sourcelookup; + +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupFacility; +import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupResult; +import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame; +import org.eclipse.jdt.internal.debug.ui.actions.ToggleBreakpointAdapter; +import org.eclipse.jdt.ui.JavaUI; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * @since 3.2 + */ +public class LambdaStackFrameSourceDisplayAdapter implements ISourceDisplay { + + @Override + public void displaySource(Object element, IWorkbenchPage page, boolean forceSourceLookup) { + JDIStackFrame jdiFrame = (JDIStackFrame) element; + try { + SourceLookupResult sourceRes = SourceLookupFacility.getDefault().lookup(element, jdiFrame.getLaunch().getSourceLocator(), forceSourceLookup); + IDocumentProvider provider = JavaUI.getDocumentProvider(); + IEditorInput editorInput = sourceRes.getEditorInput(); + provider.connect(editorInput); + IDocument document = provider.getDocument(editorInput); + IRegion region = document.getLineInformation(jdiFrame.getLineNumber() - 1); + IJavaElement je = JavaUI.getEditorInputJavaElement(editorInput); + if (je != null) { + IEditorPart part = JavaUI.openInEditor(je); + if (part instanceof ITextEditor textEditor) { + List inLineLambdas = ToggleBreakpointAdapter.findLambdaExpressions(textEditor, region); + for (LambdaExpression exp : inLineLambdas) { + IMethodBinding methodBinding = exp.resolveMethodBinding(); + String key = methodBinding.getKey(); + if (key.contains(jdiFrame.getName())) { + textEditor.selectAndReveal(exp.getStartPosition(), exp.getLength()); + return; + } + } + } + } + } catch (CoreException | BadLocationException e) { + DebugUIPlugin.log(e); + } + SourceLookupFacility.getDefault().displaySource(jdiFrame, page, forceSourceLookup); + } +}