1515
1616import java .util .ArrayList ;
1717import java .util .Collections ;
18+ import java .util .Comparator ;
1819import java .util .HashMap ;
1920import java .util .Iterator ;
2021import java .util .List ;
6566import org .eclipse .jdt .core .dom .ClassInstanceCreation ;
6667import org .eclipse .jdt .core .dom .CompilationUnit ;
6768import org .eclipse .jdt .core .dom .IBinding ;
69+ import org .eclipse .jdt .core .dom .IMethodBinding ;
6870import org .eclipse .jdt .core .dom .ITypeBinding ;
6971import org .eclipse .jdt .core .dom .LambdaExpression ;
7072import org .eclipse .jdt .core .dom .NodeFinder ;
8486import org .eclipse .jdt .internal .corext .template .java .JavaContextType ;
8587import org .eclipse .jdt .internal .debug .core .JavaDebugUtils ;
8688import org .eclipse .jdt .internal .debug .core .breakpoints .LambdaCollector ;
87- import org .eclipse .jdt .internal .debug .core .breakpoints .LambdaLocationLocator ;
89+ import org .eclipse .jdt .internal .debug .core .breakpoints .LambdaLocationLocatorHelper ;
8890import org .eclipse .jdt .internal .debug .core .breakpoints .ValidBreakpointLocationLocator ;
8991import org .eclipse .jdt .internal .debug .ui .BreakpointUtils ;
9092import org .eclipse .jdt .internal .debug .ui .DebugWorkingCopyManager ;
115117import org .eclipse .jface .text .templates .TemplateException ;
116118import org .eclipse .jface .viewers .ISelection ;
117119import org .eclipse .jface .viewers .IStructuredSelection ;
120+ import org .eclipse .jface .viewers .LabelProvider ;
118121import org .eclipse .jface .viewers .StructuredSelection ;
122+ import org .eclipse .jface .window .Window ;
119123import org .eclipse .swt .SWT ;
120- import org .eclipse .swt .events .MouseAdapter ;
121- import org .eclipse .swt .events .MouseEvent ;
122124import org .eclipse .swt .graphics .Point ;
123- import org .eclipse .swt .layout .GridData ;
124- import org .eclipse .swt .layout .GridLayout ;
125- import org .eclipse .swt .widgets .Button ;
126125import org .eclipse .swt .widgets .Composite ;
127126import org .eclipse .swt .widgets .Display ;
128127import org .eclipse .swt .widgets .Event ;
129128import org .eclipse .swt .widgets .Shell ;
130- import org .eclipse .swt .widgets .Table ;
131- import org .eclipse .swt .widgets .TableItem ;
132129import org .eclipse .ui .IEditorInput ;
133130import org .eclipse .ui .IEditorPart ;
134131import org .eclipse .ui .IWorkbenchPart ;
135132import org .eclipse .ui .PlatformUI ;
133+ import org .eclipse .ui .dialogs .ElementListSelectionDialog ;
134+ import org .eclipse .ui .dialogs .FilteredList ;
136135import org .eclipse .ui .dialogs .PreferencesUtil ;
137136import org .eclipse .ui .texteditor .IDocumentProvider ;
138137import org .eclipse .ui .texteditor .ITextEditor ;
146145public class ToggleBreakpointAdapter implements IToggleBreakpointsTargetExtension2 {
147146
148147 private static final String EMPTY_STRING = "" ; //$NON-NLS-1$
148+ private static final String TRUNCATION_SIGN = "... " ; //$NON-NLS-1$
149+ private static final String LAMBDA_SEPARATOR = " " ; //$NON-NLS-1$
150+ private static final int MAX_LAMBDA_LINE_LENGTH = 25 ;
151+ private static final int MAX_TOTAL_LAMBDA_LINE_LENGTH = MAX_LAMBDA_LINE_LENGTH * 2 ;
149152
150153 /**
151154 * Constructor
@@ -1698,30 +1701,31 @@ private void toggleFieldOrMethodBreakpoints(IWorkbenchPart part, ISelection sele
16981701 IDocument document = documentProvider .getDocument (editorInput );
16991702 try {
17001703 IRegion region = document .getLineInformation (ts .getStartLine ());
1701- CompilationUnit unitForLambdas = parseCompilationUnit (editor );
1702- LambdaCollector lambdas = new LambdaCollector (region .getOffset (), region .getOffset () + region .getLength ());
1703- unitForLambdas .accept (lambdas );
1704- List <LambdaExpression > lambdaExpresions = lambdas .getLambdaExpressions ();
1705- int selected = -1 ;
1706- if (lambdaExpresions .size () > 1 ) {
1707- selected = selectLambda (lambdaExpresions );
1708- if (selected == -1 ) {
1704+ List <LambdaExpression > lambdaExpresions = findLambdaExpressions (editor , region );
1705+ if (lambdaExpresions .isEmpty ()) {
1706+ return ;
1707+ }
1708+ int lambdaPosition ;
1709+ if (lambdaExpresions .size () == 1 ) {
1710+ lambdaPosition = 0 ;
1711+ } else {
1712+ lambdaPosition = selectLambda (lambdaExpresions );
1713+ if (lambdaPosition == -1 ) {
17091714 return ;
17101715 }
17111716 }
1712-
1713- LambdaLocationLocator selectedLambda = new LambdaLocationLocator (region .getOffset (), region .getOffset ()
1714- + region .getLength (), selected == -1 ? selected = 0 : selected );
1715- unit .accept (selectedLambda );
1716- if (selectedLambda .getNodeLength () == -1 || selectedLambda .getNodeOffset () == -1 ) {
1717+ LambdaExpression selectedLambda = lambdaExpresions .get (lambdaPosition );
1718+ IMethodBinding methodBinding = selectedLambda .resolveMethodBinding ();
1719+ if (methodBinding == null ) {
17171720 BreakpointToggleUtils .report (ActionMessages .LambdaEntryBreakpointToggleAction_Unavailable , part );
17181721 return ;
17191722 }
1720- ITextSelection textSelection = new TextSelection (document , selectedLambda .getNodeOffset (), selectedLambda .getNodeLength ());
1721- LambdaBreakpoint lambdaBp = new LambdaBreakpoint (lambdaExpresions .get (selected ).toString (), selected , lambdaExpresions .size () > 1
1722- ? true
1723- : false );
1724- toggleLambdaEntryMethodBreakpoints (part , textSelection , selectedLambda .getLambdaMethodName (), selectedLambda .getfLambdaMethodSignature (), lambdaBp );
1723+ String lambdaMethodName = LambdaLocationLocatorHelper .toMethodName (methodBinding );
1724+ String lambdaMethodSignature = LambdaLocationLocatorHelper .toMethodSignature (methodBinding );
1725+ ITextSelection textSelection = new TextSelection (document , selectedLambda .getStartPosition (), selectedLambda .getLength ());
1726+ boolean inline = lambdaExpresions .size () > 1 ? true : false ;
1727+ LambdaBreakpoint lambdaBp = new LambdaBreakpoint (lambdaExpresions .get (lambdaPosition ).toString (), lambdaPosition , inline );
1728+ toggleLambdaEntryMethodBreakpoints (part , textSelection , lambdaMethodName , lambdaMethodSignature , lambdaBp );
17251729 } catch (BadLocationException e ) {
17261730 BreakpointToggleUtils .report (ActionMessages .LambdaEntryBreakpointToggleAction_Unavailable , part );
17271731 }
@@ -1732,9 +1736,16 @@ private void toggleFieldOrMethodBreakpoints(IWorkbenchPart part, ISelection sele
17321736 }
17331737 }
17341738
1735- private record LambdaBreakpoint (String lambdaName , int lambdaPosition , boolean isInline ) {
1739+ private static List <LambdaExpression > findLambdaExpressions (ITextEditor editor , IRegion region ) {
1740+ LambdaCollector lambdas = new LambdaCollector (region .getOffset (), region .getOffset () + region .getLength ());
1741+ CompilationUnit unitForLambdas = parseCompilationUnit (editor );
1742+ unitForLambdas .accept (lambdas );
1743+ return lambdas .getLambdaExpressions ();
1744+ }
1745+
1746+ private static record LambdaBreakpoint (String lambdaName , int lambdaPosition , boolean isInline ) {
17361747 public String getLambdaName () {
1737- return shortenExpression (lambdaName );
1748+ return shortenLambdaExpression (lambdaName );
17381749 }
17391750 }
17401751
@@ -1953,109 +1964,110 @@ private static ICompilationUnit getCompilationUnit(ITextEditor editor) {
19531964 }
19541965
19551966 public int selectLambda (List <LambdaExpression > lambdaExps ) {
1956- if (lambdaExps .isEmpty ()) {
1957- return -1 ;
1967+ List <String > lambdaNames = lambdaExps .stream ().map (expr -> shortenLambdaExpression (expr .toString ())).toList ();
1968+ LambdaLabelProvider lambdaLabelProvider = new LambdaLabelProvider (lambdaExps , lambdaNames );
1969+ ElementListSelectionDialog dialog = new ElementListSelectionDialog (DebugUIPlugin .getShellForModalDialog (), lambdaLabelProvider ) {
1970+ @ Override
1971+ protected FilteredList createFilteredList (Composite parent ) {
1972+ FilteredList filteredList = super .createFilteredList (parent );
1973+ // Disable default sorting to keep the original order
1974+ filteredList .setComparator (new LambdaPositionComparator (lambdaNames ));
1975+ return filteredList ;
1976+ }
1977+
1978+ @ Override
1979+ protected void setShellStyle (int newShellStyle ) {
1980+ super .setShellStyle (newShellStyle & ~SWT .APPLICATION_MODAL );
1981+ }
1982+ };
1983+ dialog .setMultipleSelection (false );
1984+ dialog .setTitle (ActionMessages .LambdaSelectionDialog_title );
1985+ dialog .setElements (lambdaExps .toArray ());
1986+ dialog .open ();
1987+ if (dialog .getReturnCode () == Window .OK ) {
1988+ Object result = dialog .getFirstResult ();
1989+ return lambdaExps .indexOf (result );
1990+ }
1991+ return -1 ;
1992+ }
1993+
1994+ private static class LambdaPositionComparator implements Comparator <Object > {
1995+ private List <String > lambdaNames ;
1996+
1997+ public LambdaPositionComparator (List <String > lambdaNames ) {
1998+ this .lambdaNames = lambdaNames ;
19581999 }
19592000
1960- int [] selection = { -1 };
1961- Shell parentShell = PlatformUI .getWorkbench ().getActiveWorkbenchWindow ().getShell ();
1962- Shell popup = new Shell (parentShell , SWT .TOOL | SWT .RESIZE );
1963- popup .setText (ActionMessages .lambdaSelection );
1964- GridLayout popupLayout = new GridLayout (1 , false );
1965- popupLayout .marginWidth = 10 ;
1966- popupLayout .marginHeight = 10 ;
1967- popupLayout .verticalSpacing = 10 ;
1968- popup .setLayout (popupLayout );
2001+ @ Override
2002+ public int compare (Object o1 , Object o2 ) {
2003+ int index1 = lambdaNames .indexOf (o1 );
2004+ int index2 = lambdaNames .indexOf (o2 );
2005+ return index1 - index2 ;
2006+ }
2007+ }
19692008
1970- Table table = new Table (popup , SWT .BORDER | SWT .V_SCROLL | SWT .H_SCROLL );
1971- GridData tableData = new GridData (SWT .FILL , SWT .FILL , true , true );
1972- table .setLayoutData (tableData );
1973- List <String > itemsName = toLambdaEntries (lambdaExps );
2009+ private static class LambdaLabelProvider extends LabelProvider {
2010+ private List <LambdaExpression > lambdaExps ;
2011+ private List <String > lambdaNames ;
19742012
1975- for (String item : itemsName ) {
1976- new TableItem (table , SWT .NONE ).setText (item );
2013+ public LambdaLabelProvider (List <LambdaExpression > lambdaExps , List <String > lambdaNames ) {
2014+ this .lambdaExps = lambdaExps ;
2015+ this .lambdaNames = lambdaNames ;
19772016 }
19782017
1979- table .addMouseListener (new MouseAdapter () {
1980- @ Override
1981- public void mouseDoubleClick (MouseEvent e ) {
1982- int ind = table .getSelectionIndex ();
1983- if (ind >= 0 ) {
1984- selection [0 ] = ind ;
1985- popup .close ();
2018+ @ Override
2019+ public String getText (Object element ) {
2020+ if (element instanceof LambdaExpression ) {
2021+ int index = lambdaExps .indexOf (element );
2022+ if (index != -1 ) {
2023+ return lambdaNames .get (index );
19862024 }
19872025 }
1988- });
1989-
1990- Composite buttonBar = new Composite (popup , SWT .NONE );
1991- GridLayout buttonLayout = new GridLayout (2 , true );
1992- buttonLayout .marginWidth = 0 ;
1993- buttonLayout .marginHeight = 0 ;
1994- buttonLayout .horizontalSpacing = 10 ;
1995- buttonBar .setLayout (buttonLayout );
1996- buttonBar .setLayoutData (new GridData (SWT .END , SWT .CENTER , true , false ));
1997- Button selectButton = new Button (buttonBar , SWT .PUSH );
1998- selectButton .setText (ActionMessages .lambdaSelect );
1999- selectButton .setLayoutData (new GridData (SWT .FILL , SWT .CENTER , true , false ));
2000-
2001- selectButton .addListener (SWT .Selection , e -> {
2002- int index = table .getSelectionIndex ();
2003- if (index >= 0 ) {
2004- selection [0 ] = index ;
2005- popup .close ();
2006- }
2007- });
2008- Button closeButton = new Button (buttonBar , SWT .PUSH );
2009- closeButton .setText (ActionMessages .lambdaClose );
2010- closeButton .setLayoutData (new GridData (SWT .FILL , SWT .CENTER , true , false ));
2011- closeButton .addListener (SWT .Selection , e -> popup .close ());
2012- int tableHeight = Math .min (lambdaExps .size (), 10 ) * table .getItemHeight () + 80 ;
2013- popup .setSize (300 , tableHeight );
2014- Point location = Display .getDefault ().getCursorLocation ();
2015- popup .setLocation (location );
2016- popup .addListener (SWT .Deactivate , e -> popup .close ());
2017- popup .open ();
2018- while (!popup .isDisposed ()) {
2019- if (!Display .getDefault ().readAndDispatch ()) {
2020- Display .getDefault ().sleep ();
2021- }
2022- }
2023- if (!popup .isDisposed ()) {
2024- popup .dispose ();
2025- }
2026- return selection [0 ];
2026+ return super .getText (element );
2027+ }
20272028 }
20282029
2029- public static String shortenExpression (String input ) {
2030+ /**
2031+ * Shorten and "flatten" the (possible multi-line) lambda expression
2032+ *
2033+ * @param input
2034+ * non null
2035+ * @return shortened and flattened lambda expression
2036+ */
2037+ static String shortenLambdaExpression (String input ) {
2038+ input = input .strip ();
2039+ StringBuilder result = new StringBuilder ();
20302040 if (!input .contains ("\n " )) { //$NON-NLS-1$
2031- return input ;
2032- }
2033- String singleLine = new String (input );
2034- StringBuilder shortened = new StringBuilder ();
2035- for (String token : singleLine .split ("\n " )) { //$NON-NLS-1$
2036- token = token .trim ();
2037- if (token .length () > 20 ) {
2038- shortened .append (token , 0 , 14 ).append (".. " ); //$NON-NLS-1$
2041+ if (input .length () > MAX_TOTAL_LAMBDA_LINE_LENGTH ) {
2042+ result .append (input , 0 , MAX_TOTAL_LAMBDA_LINE_LENGTH ).append (TRUNCATION_SIGN );
20392043 } else {
2040- shortened .append (token ). append ( " " ); //$NON-NLS-1$
2044+ result .append (input );
20412045 }
2046+ return result .toString ();
20422047 }
2043- return shortened .toString ().trim ();
2044- }
2045-
2046- private List <String > toLambdaEntries (List <LambdaExpression > lambdaExps ) {
2047- List <String > itemsName = lambdaExps .stream ().map (LambdaExpression ::toString ).toList ();
2048- itemsName = itemsName .stream ().map (item -> item .trim ().replaceAll ("\\ n+$" , "" )).toList (); //$NON-NLS-1$ //$NON-NLS-2$
2049- if (Platform .getOS ().equals (Platform .OS_MACOSX )) { // If an expression is of multiline then we should shorten it to single line.
2050- itemsName = itemsName .stream ().map (ToggleBreakpointAdapter ::shortenExpression ).toList ();
2048+ boolean shortened = false ;
2049+ for (String line : input .split ("\n " )) { //$NON-NLS-1$
2050+ if (result .length () > 0 ) {
2051+ result .append (LAMBDA_SEPARATOR );
2052+ }
2053+ line = line .strip ();
2054+ if (line .length () > MAX_LAMBDA_LINE_LENGTH ) {
2055+ line = line .substring (0 , MAX_LAMBDA_LINE_LENGTH ) + TRUNCATION_SIGN ;
2056+ shortened = true ;
2057+ }
2058+ result .append (line );
2059+ if (result .length () > MAX_TOTAL_LAMBDA_LINE_LENGTH ) {
2060+ shortened = true ;
2061+ break ;
2062+ }
20512063 }
2052- itemsName = itemsName . stream (). map ( item -> {
2053- if (! item . isEmpty () && item . charAt ( item . length () - 1 ) == '\n' ) {
2054- int newLength = Math . max ( 0 , item . length () - 2 );
2055- return item . substring ( 0 , newLength ). trim ( );
2064+
2065+ if (shortened ) {
2066+ if ( result . lastIndexOf ( TRUNCATION_SIGN ) != result . length () - TRUNCATION_SIGN . length ()) {
2067+ result . append ( TRUNCATION_SIGN );
20562068 }
2057- return item ;
2058- }).toList ();
2059- return itemsName ;
2069+ }
2070+ return result .toString ().trim ();
20602071 }
2072+
20612073}
0 commit comments