From d7e73f15c4375fa71a8e2b4ade6c45c327b077bd Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Tue, 10 Dec 2024 23:30:26 +0100 Subject: [PATCH 01/15] Remove repetitive code from JDIModelPresentation and using smart instanceof to remove clutter --- .../debug/ui/JDIModelPresentation.java | 293 ++++++++---------- .../variables/JavaVariableLabelProvider.java | 2 +- 2 files changed, 131 insertions(+), 164 deletions(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java index 9f61178eb0..3237a571ca 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java @@ -186,7 +186,7 @@ public void computeDetail(IValue value, IValueDetailListener listener) { * the target in which a thread is required * @return thread or null */ - public static IJavaThread getEvaluationThread(IJavaDebugTarget target) { + private static IJavaThread getEvaluationThread(IJavaDebugTarget target) { IJavaStackFrame frame = EvaluationContextManager.getEvaluationContext((IWorkbenchWindow)null); IJavaThread thread = null; if (frame != null) { @@ -719,20 +719,19 @@ public Image getImage(Object item) { if (item instanceof JDIReferenceListEntryVariable){ return getReferenceImage(); } - if (item instanceof IJavaVariable) { - return getVariableImage((IAdaptable) item); + if (item instanceof IJavaVariable variable) { + return getVariableImage(variable); } - if (item instanceof IMarker) { - IBreakpoint bp = getBreakpoint((IMarker)item); - if (bp instanceof IJavaBreakpoint) { - return getBreakpointImage((IJavaBreakpoint)bp); + if (item instanceof IMarker marker) { + IBreakpoint bp = getBreakpoint(marker); + if (bp instanceof IJavaBreakpoint javaBreakpoint) { + return getBreakpointImage(javaBreakpoint); } } - if (item instanceof IJavaBreakpoint) { - return getBreakpointImage((IJavaBreakpoint)item); + if (item instanceof IJavaBreakpoint breakpoint) { + return getBreakpointImage(breakpoint); } - if (item instanceof JDIThread) { - JDIThread jt = (JDIThread) item; + if (item instanceof JDIThread jt) { if (jt.isSuspendVoteInProgress()) { return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING); } @@ -759,8 +758,7 @@ public Image getImage(Object item) { return getJavaWaitingThreadImage((JavaWaitingThread)item); } if (item instanceof NoMonitorInformationElement) { - return getDebugImageRegistry().get(new JDIImageDescriptor( - getImageDescriptor(JavaDebugImages.IMG_OBJS_MONITOR), 0)); + return getJavaDebugImage(JavaDebugImages.IMG_OBJS_MONITOR, 0); } } catch (CoreException e) { // no need to log errors - elements may no longer exist by the time we render them @@ -784,45 +782,32 @@ private synchronized void initImageRegistries() { } private Image getJavaWaitingThreadImage(JavaWaitingThread thread) { - JDIImageDescriptor descriptor; int flag= JDIImageDescriptor.IN_CONTENTION_FOR_MONITOR | (thread.getThread().isInDeadlock() ? JDIImageDescriptor.IN_DEADLOCK : 0); - if (thread.isSuspended()) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED), flag); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING), flag); - } - return getDebugImageRegistry().get(descriptor); + final var imageKey = thread.isSuspended() ? IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED : IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + return getDebugPluginImage(imageKey, flag); } private Image getJavaOwningThreadImage(JavaOwningThread thread) { - JDIImageDescriptor descriptor; int flag= JDIImageDescriptor.OWNS_MONITOR | (thread.getThread().isInDeadlock() ? JDIImageDescriptor.IN_DEADLOCK : 0); - if (thread.isSuspended()) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED), flag); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING), flag); - } - return getDebugImageRegistry().get(descriptor); + final var imageKey = thread.isSuspended() ? IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED : IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + return getDebugPluginImage(imageKey, flag); } private Image getJavaContendedMonitorImage(JavaContendedMonitor monitor) { int flag= monitor.getMonitor().isInDeadlock() ? JDIImageDescriptor.IN_DEADLOCK : 0; - JDIImageDescriptor descriptor= new JDIImageDescriptor( - getImageDescriptor(JavaDebugImages.IMG_OBJS_CONTENDED_MONITOR), flag); - return getDebugImageRegistry().get(descriptor); + return getJavaDebugImage(JavaDebugImages.IMG_OBJS_CONTENDED_MONITOR, flag); } private Image getJavaOwnedMonitorImage(JavaOwnedMonitor monitor) { int flag= monitor.getMonitor().isInDeadlock() ? JDIImageDescriptor.IN_DEADLOCK : 0; - JDIImageDescriptor descriptor= new JDIImageDescriptor(getImageDescriptor(JavaDebugImages.IMG_OBJS_OWNED_MONITOR), flag); - return getDebugImageRegistry().get(descriptor); + return getJavaDebugImage(JavaDebugImages.IMG_OBJS_OWNED_MONITOR, flag); } protected Image getBreakpointImage(IJavaBreakpoint breakpoint) throws CoreException { - if (breakpoint instanceof IJavaExceptionBreakpoint) { - return getExceptionBreakpointImage((IJavaExceptionBreakpoint)breakpoint); - } else if (breakpoint instanceof IJavaClassPrepareBreakpoint) { - return getClassPrepareBreakpointImage((IJavaClassPrepareBreakpoint)breakpoint); + if (breakpoint instanceof IJavaExceptionBreakpoint exceptionBreakpoint) { + return getExceptionBreakpointImage(exceptionBreakpoint); + } else if (breakpoint instanceof IJavaClassPrepareBreakpoint prepareBreakpoint) { + return getClassPrepareBreakpointImage(prepareBreakpoint); } if (breakpoint instanceof IJavaLineBreakpoint && BreakpointUtils.isRunToLineBreakpoint((IJavaLineBreakpoint)breakpoint)) { @@ -833,104 +818,67 @@ protected Image getBreakpointImage(IJavaBreakpoint breakpoint) throws CoreExcept protected Image getExceptionBreakpointImage(IJavaExceptionBreakpoint exception) throws CoreException { int flags= computeBreakpointAdornmentFlags(exception); - JDIImageDescriptor descriptor= null; if ((flags & JDIImageDescriptor.ENABLED) == 0) { - descriptor= new JDIImageDescriptor(getImageDescriptor(JavaDebugImages.IMG_OBJS_EXCEPTION_DISABLED), flags); + return getJavaDebugImage(JavaDebugImages.IMG_OBJS_EXCEPTION_DISABLED, flags); } else if (exception.isChecked()) { - descriptor= new JDIImageDescriptor(getImageDescriptor(JavaDebugImages.IMG_OBJS_EXCEPTION), flags); + return getJavaDebugImage(JavaDebugImages.IMG_OBJS_EXCEPTION, flags); } else { - descriptor= new JDIImageDescriptor(getImageDescriptor(JavaDebugImages.IMG_OBJS_ERROR), flags); + return getJavaDebugImage(JavaDebugImages.IMG_OBJS_ERROR, flags); } - return getDebugImageRegistry().get(descriptor); } protected Image getJavaBreakpointImage(IJavaBreakpoint breakpoint) throws CoreException { - if (breakpoint instanceof IJavaMethodBreakpoint) { - IJavaMethodBreakpoint mBreakpoint= (IJavaMethodBreakpoint)breakpoint; + if (breakpoint instanceof IJavaMethodBreakpoint mBreakpoint) { return getJavaMethodBreakpointImage(mBreakpoint); - } else if (breakpoint instanceof IJavaWatchpoint) { - IJavaWatchpoint watchpoint= (IJavaWatchpoint)breakpoint; + } else if (breakpoint instanceof IJavaWatchpoint watchpoint) { return getJavaWatchpointImage(watchpoint); - } else if (breakpoint instanceof IJavaMethodEntryBreakpoint) { - IJavaMethodEntryBreakpoint meBreakpoint = (IJavaMethodEntryBreakpoint)breakpoint; + } else if (breakpoint instanceof IJavaMethodEntryBreakpoint meBreakpoint) { return getJavaMethodEntryBreakpointImage(meBreakpoint); } else { int flags= computeBreakpointAdornmentFlags(breakpoint); - JDIImageDescriptor descriptor= null; - if (breakpoint.isEnabled()) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_BREAKPOINT), flags); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_BREAKPOINT_DISABLED), flags); - } - return getDebugImageRegistry().get(descriptor); + final var imageKey = breakpoint.isEnabled() ? IDebugUIConstants.IMG_OBJS_BREAKPOINT : IDebugUIConstants.IMG_OBJS_BREAKPOINT_DISABLED; + return getDebugPluginImage(imageKey, flags); } } protected Image getJavaMethodBreakpointImage(IJavaMethodBreakpoint mBreakpoint) throws CoreException { int flags= computeBreakpointAdornmentFlags(mBreakpoint); - JDIImageDescriptor descriptor= null; - if (mBreakpoint.isEnabled()) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_BREAKPOINT), flags); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_BREAKPOINT_DISABLED), flags); - } - - return getDebugImageRegistry().get(descriptor); + final var imageKey = mBreakpoint.isEnabled() ? IDebugUIConstants.IMG_OBJS_BREAKPOINT : IDebugUIConstants.IMG_OBJS_BREAKPOINT_DISABLED; + return getDebugPluginImage(imageKey, flags); } protected Image getJavaMethodEntryBreakpointImage(IJavaMethodEntryBreakpoint mBreakpoint) throws CoreException { int flags= computeBreakpointAdornmentFlags(mBreakpoint); - JDIImageDescriptor descriptor= null; - if (mBreakpoint.isEnabled()) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_BREAKPOINT), flags); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_BREAKPOINT_DISABLED), flags); - } - - return getDebugImageRegistry().get(descriptor); + final var imageKey = mBreakpoint.isEnabled() ? IDebugUIConstants.IMG_OBJS_BREAKPOINT : IDebugUIConstants.IMG_OBJS_BREAKPOINT_DISABLED; + return getDebugPluginImage(imageKey, flags); } protected Image getClassPrepareBreakpointImage(IJavaClassPrepareBreakpoint breakpoint) throws CoreException { int flags= computeBreakpointAdornmentFlags(breakpoint); - JDIImageDescriptor descriptor= null; if (breakpoint.getMemberType() == IJavaClassPrepareBreakpoint.TYPE_CLASS) { - descriptor= new JDIImageDescriptor(JavaUI.getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJS_CLASS), flags); - } else { - descriptor= new JDIImageDescriptor(JavaUI.getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJS_INTERFACE), flags); + return getDebugImage(JavaUI.getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJS_CLASS), flags); } - return getDebugImageRegistry().get(descriptor); + return getDebugImage(JavaUI.getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJS_INTERFACE), flags); } protected Image getJavaWatchpointImage(IJavaWatchpoint watchpoint) throws CoreException { - int flags= computeBreakpointAdornmentFlags(watchpoint); - JDIImageDescriptor descriptor= null; - boolean enabled= (flags & JDIImageDescriptor.ENABLED) != 0; + final int flags = computeBreakpointAdornmentFlags(watchpoint); + final boolean enabled = (flags & JDIImageDescriptor.ENABLED) != 0; + final String imagekey; if (watchpoint.isAccess()) { if (watchpoint.isModification()) { //access and modification - if (enabled) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_WATCHPOINT), flags); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_WATCHPOINT_DISABLED), flags); - } + imagekey = enabled ? IDebugUIConstants.IMG_OBJS_WATCHPOINT : IDebugUIConstants.IMG_OBJS_WATCHPOINT_DISABLED; } else { - if (enabled) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_ACCESS_WATCHPOINT), flags); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_ACCESS_WATCHPOINT_DISABLED), flags); - } + imagekey = enabled ? IDebugUIConstants.IMG_OBJS_ACCESS_WATCHPOINT : IDebugUIConstants.IMG_OBJS_ACCESS_WATCHPOINT_DISABLED; } } else if (watchpoint.isModification()) { - if (enabled) { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT), flags); - } else { - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT_DISABLED), flags); - } + imagekey = enabled ? IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT : IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT_DISABLED; } else { //neither access nor modification - descriptor= new JDIImageDescriptor(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_WATCHPOINT_DISABLED), flags); + imagekey = IDebugUIConstants.IMG_OBJS_WATCHPOINT_DISABLED; } - return getDebugImageRegistry().get(descriptor); + return getDebugPluginImage(imagekey, flags); } protected Image getVariableImage(IAdaptable element) { @@ -968,10 +916,10 @@ private Image getReferenceImage() { */ protected Image getDebugElementImage(Object element) { ImageDescriptor image= null; - if (element instanceof IJavaThread) { - IJavaThread thread = (IJavaThread)element; + if (element instanceof IJavaThread thread) { // image also needs to handle suspended quiet - if (thread.isSuspended() && !thread.isPerformingEvaluation() && !(thread instanceof JDIThread && ((JDIThread)thread).isSuspendVoteInProgress())) { + if (thread.isSuspended() && !thread.isPerformingEvaluation() + && !(thread instanceof JDIThread jdiThread && jdiThread.isSuspendVoteInProgress())) { image= DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED); } else if (thread.isTerminated()) { image= DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_THREAD_TERMINATED); @@ -985,8 +933,7 @@ protected Image getDebugElementImage(Object element) { return null; } int flags= computeJDIAdornmentFlags(element); - JDIImageDescriptor descriptor= new JDIImageDescriptor(image, flags); - return getDebugImageRegistry().get(descriptor); + return getDebugImage(image, flags); } /** @@ -1007,7 +954,7 @@ protected Image getExpressionImage(Object expression) { if (bigSize) { descriptor.setSize(BIG_SIZE); } - return getDebugImageRegistry().get(descriptor); + return getDebugImage(descriptor); } /** @@ -1040,11 +987,11 @@ private int computeJDIAdornmentFlags(Object element) { } return flag; } - if (element instanceof IJavaDebugTarget) { - if (((IJavaDebugTarget)element).isOutOfSynch()) { + if (element instanceof IJavaDebugTarget debugTarget) { + if (debugTarget.isOutOfSynch()) { return JDIImageDescriptor.IS_OUT_OF_SYNCH; } - if (((IJavaDebugTarget)element).mayBeOutOfSynch()) { + if (debugTarget.mayBeOutOfSynch()) { return JDIImageDescriptor.MAY_BE_OUT_OF_SYNCH; } } @@ -1073,12 +1020,11 @@ private int computeBreakpointAdornmentFlags(IJavaBreakpoint breakpoint) { } else if (DebugPlugin.getDefault().getBreakpointManager().hasActiveTriggerPoints()) { flags |= JDIImageDescriptor.TRIGGER_SUPPRESSED; } - if (breakpoint instanceof IJavaLineBreakpoint) { - if (((IJavaLineBreakpoint)breakpoint).isConditionEnabled()) { + if (breakpoint instanceof IJavaLineBreakpoint lineBreakpoint) { + if (lineBreakpoint.isConditionEnabled()) { flags |= JDIImageDescriptor.CONDITIONAL; } - if (breakpoint instanceof IJavaMethodBreakpoint) { - IJavaMethodBreakpoint mBreakpoint= (IJavaMethodBreakpoint)breakpoint; + if (breakpoint instanceof IJavaMethodBreakpoint mBreakpoint) { if (mBreakpoint.isEntry()) { flags |= JDIImageDescriptor.ENTRY; } @@ -1089,8 +1035,7 @@ private int computeBreakpointAdornmentFlags(IJavaBreakpoint breakpoint) { if (breakpoint instanceof IJavaMethodEntryBreakpoint) { flags |= JDIImageDescriptor.ENTRY; } - } else if (breakpoint instanceof IJavaExceptionBreakpoint) { - IJavaExceptionBreakpoint eBreakpoint= (IJavaExceptionBreakpoint)breakpoint; + } else if (breakpoint instanceof IJavaExceptionBreakpoint eBreakpoint) { if (eBreakpoint.isCaught()) { flags |= JDIImageDescriptor.CAUGHT; } @@ -1126,9 +1071,7 @@ private ImageDescriptor computeBaseImageDescriptor(IAdaptable element) { // no need to log errors - elements may no longer exist by the time we render them } } - if (javaVariable instanceof JDIReturnValueVariable) { - - JDIReturnValueVariable jdiReturnValueVariable = (JDIReturnValueVariable) javaVariable; + if (javaVariable instanceof JDIReturnValueVariable jdiReturnValueVariable) { if (!jdiReturnValueVariable.hasResult) { return JavaDebugImages.getImageDescriptor(JavaDebugImages.IMG_OBJS_METHOD_RESULT_DISABLED); } @@ -1157,9 +1100,9 @@ private int computeAdornmentFlags(IAdaptable element) { private int computeLogicalStructureAdornmentFlags(IAdaptable element) { int flags = 0; - if (element instanceof IVariable) { + if (element instanceof IVariable variable) { try { - IValue value= ((IVariable) element).getValue(); + IValue value = variable.getValue(); ILogicalStructureType[] types = DebugPlugin.getLogicalStructureTypes(value); if (types.length == 0) { return flags; // no logical structure is defined for the value type @@ -1192,28 +1135,27 @@ private int computeLogicalStructureAdornmentFlags(IAdaptable element) { */ @Override public IEditorInput getEditorInput(Object item) { - if (item instanceof IMarker) { - item = getBreakpoint((IMarker)item); + if (item instanceof IMarker marker) { + item = getBreakpoint(marker); } - if (item instanceof IJavaBreakpoint) { - IType type = BreakpointUtils.getType((IJavaBreakpoint)item); + if (item instanceof IJavaBreakpoint breakpoint) { + IType type = BreakpointUtils.getType(breakpoint); if (type == null) { // if the breakpoint is not associated with a type, use its resource - item = ((IJavaBreakpoint)item).getMarker().getResource(); + item = breakpoint.getMarker().getResource(); } else { item = type; } } - if (item instanceof LocalFileStorage) { - return new LocalFileStorageEditorInput((LocalFileStorage)item); + if (item instanceof LocalFileStorage localFileStorage) { + return new LocalFileStorageEditorInput(localFileStorage); } - if (item instanceof ZipEntryStorage) { - return new ZipEntryStorageEditorInput((ZipEntryStorage)item); + if (item instanceof ZipEntryStorage zipEntryStorage) { + return new ZipEntryStorageEditorInput(zipEntryStorage); } // for types that correspond to external files, return null so we do not // attempt to open a non-existing workspace file on the breakpoint (bug 184934) - if (item instanceof IType) { - IType type = (IType) item; + if (item instanceof IType type) { if (!type.exists()) { return null; } @@ -1292,8 +1234,7 @@ protected String getVariableText(IJavaVariable var) { buff.append(varLabel); // add declaring type name if required - if (var instanceof IJavaFieldVariable) { - IJavaFieldVariable field = (IJavaFieldVariable)var; + if (var instanceof IJavaFieldVariable field) { if (isDuplicateName(field)) { try { String decl = field.getDeclaringType().getName(); @@ -1562,22 +1503,22 @@ protected String getValueHexText(IJavaValue value) throws DebugException { protected String getBreakpointText(IBreakpoint breakpoint) { try { String label = null; - if (breakpoint instanceof IJavaExceptionBreakpoint) { - label = getExceptionBreakpointText((IJavaExceptionBreakpoint)breakpoint); - } else if (breakpoint instanceof IJavaWatchpoint) { - label = getWatchpointText((IJavaWatchpoint)breakpoint); - } else if (breakpoint instanceof IJavaMethodBreakpoint) { - label = getMethodBreakpointText((IJavaMethodBreakpoint)breakpoint); - } else if (breakpoint instanceof IJavaPatternBreakpoint) { - label = getJavaPatternBreakpointText((IJavaPatternBreakpoint)breakpoint); - } else if (breakpoint instanceof IJavaTargetPatternBreakpoint) { - label = getJavaTargetPatternBreakpointText((IJavaTargetPatternBreakpoint)breakpoint); - } else if (breakpoint instanceof IJavaStratumLineBreakpoint) { - label = getJavaStratumLineBreakpointText((IJavaStratumLineBreakpoint)breakpoint); - } else if (breakpoint instanceof IJavaLineBreakpoint) { - label = getLineBreakpointText((IJavaLineBreakpoint)breakpoint); - } else if (breakpoint instanceof IJavaClassPrepareBreakpoint) { - label = getClassPrepareBreakpointText((IJavaClassPrepareBreakpoint)breakpoint); + if (breakpoint instanceof IJavaExceptionBreakpoint exceptionBreakpoint) { + label = getExceptionBreakpointText(exceptionBreakpoint); + } else if (breakpoint instanceof IJavaWatchpoint watchpoint) { + label = getWatchpointText(watchpoint); + } else if (breakpoint instanceof IJavaMethodBreakpoint methodBreakpoint) { + label = getMethodBreakpointText(methodBreakpoint); + } else if (breakpoint instanceof IJavaPatternBreakpoint patternBreakpoint) { + label = getJavaPatternBreakpointText(patternBreakpoint); + } else if (breakpoint instanceof IJavaTargetPatternBreakpoint targetPatternBreakpoint) { + label = getJavaTargetPatternBreakpointText(targetPatternBreakpoint); + } else if (breakpoint instanceof IJavaStratumLineBreakpoint stratumLineBreakpoint) { + label = getJavaStratumLineBreakpointText(stratumLineBreakpoint); + } else if (breakpoint instanceof IJavaLineBreakpoint lineBreakpoint) { + label = getLineBreakpointText(lineBreakpoint); + } else if (breakpoint instanceof IJavaClassPrepareBreakpoint prepareBreakpoint) { + label = getClassPrepareBreakpointText(prepareBreakpoint); } else { // Should never get here return ""; //$NON-NLS-1$ @@ -2017,14 +1958,14 @@ private List getNameList(String listName) { /** * Plug in the single argument to the resource String for the key to get a formatted resource String */ - public static String getFormattedString(String key, String arg) { + private static String getFormattedString(String key, String arg) { return getFormattedString(key, new String[] {arg}); } /** * Plug in the arguments to the resource String for the key to get a formatted resource String */ - public static String getFormattedString(String string, String[] args) { + private static String getFormattedString(String string, String[] args) { return NLS.bind(string, args); } @@ -2064,13 +2005,42 @@ protected void appendInstanceFilter(IJavaBreakpoint breakpoint, StringBuilder bu } } - protected static org.eclipse.jdt.internal.debug.ui.ImageDescriptorRegistry getDebugImageRegistry() { + private static org.eclipse.jdt.internal.debug.ui.ImageDescriptorRegistry getDebugImageRegistry() { if (fgDebugImageRegistry == null) { fgDebugImageRegistry = JDIDebugUIPlugin.getImageDescriptorRegistry(); } return fgDebugImageRegistry; } + /** + * Gets an {@link Image} from a {@link ImageDescriptor} from the {@link JDIDebugUIPlugin}'s image registry. + */ + private static Image getDebugImage(ImageDescriptor descriptor) { + return getDebugImageRegistry().get(descriptor); + } + + /** + * Gets an {@link Image} from a {@link ImageDescriptor} from the {@link JDIDebugUIPlugin}'s image registry. + */ + private static Image getDebugImage(ImageDescriptor descriptor, int flags) { + return getDebugImageRegistry().get(new JDIImageDescriptor(descriptor, flags)); + } + + /** + * Gets an image from the image registry of {@link DebugUITools}. + */ + private static Image getDebugPluginImage(String key, int flags) { + var descriptor = DebugUITools.getImageDescriptor(key); + return getDebugImageRegistry().get(new JDIImageDescriptor(descriptor, flags)); + } + + /** + * Gets an image from the ImageRegistry of {@link JavaDebugImages}. + */ + private Image getJavaDebugImage(String key, int flags) { + return getDebugImage(JavaDebugImages.getImageDescriptor(key), flags); + } + protected JavaElementLabelProvider getJavaLabelProvider() { if (fJavaLabelProvider == null) { fJavaLabelProvider = new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT); @@ -2105,9 +2075,9 @@ protected boolean isDuplicateName(IJavaFieldVariable variable) { */ @Override public Color getForeground(Object element) { - if (element instanceof IJavaObject) { + if (element instanceof IJavaObject javaObject) { try { - var label = ((IJavaObject) element).getLabel(); + var label = javaObject.getLabel(); if (label != null) { return getColorFromRegistry(IJDIPreferencesConstants.PREF_LABELED_OBJECT_COLOR); } @@ -2115,33 +2085,33 @@ public Color getForeground(Object element) { } catch (DebugException e) { } } - if (element instanceof IJavaVariable) { + if (element instanceof IJavaVariable javaVariable) { try { - return getForeground(((IJavaVariable) element).getValue()); + return getForeground(javaVariable.getValue()); } catch (DebugException e) { } } - if (element instanceof IWatchExpression) { - var watchValue = ((IWatchExpression) element).getValue(); + if (element instanceof IWatchExpression watchExpression) { + var watchValue = watchExpression.getValue(); return getForeground(watchValue); } - if (element instanceof JavaInspectExpression) { - var value = ((JavaInspectExpression) element).getValue(); + if (element instanceof JavaInspectExpression inspectExpression) { + var value = inspectExpression.getValue(); return getForeground(value); } - if (element instanceof JavaContendedMonitor && ((JavaContendedMonitor) element).getMonitor().isInDeadlock()) { + if (element instanceof JavaContendedMonitor contendedMonitor && contendedMonitor.getMonitor().isInDeadlock()) { return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR); } - if (element instanceof JavaOwnedMonitor && ((JavaOwnedMonitor)element).getMonitor().isInDeadlock()) { + if (element instanceof JavaOwnedMonitor ownedMonitor && ownedMonitor.getMonitor().isInDeadlock()) { return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR); } - if (element instanceof JavaWaitingThread && ((JavaWaitingThread)element).getThread().isInDeadlock()) { + if (element instanceof JavaWaitingThread waitingThread && waitingThread.getThread().isInDeadlock()) { return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR); } - if (element instanceof JavaOwningThread && ((JavaOwningThread)element).getThread().isInDeadlock()) { + if (element instanceof JavaOwningThread owningThread && owningThread.getThread().isInDeadlock()) { return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR); } - if (element instanceof IJavaThread && ThreadMonitorManager.getDefault().isInDeadlock((IJavaThread)element)) { + if (element instanceof IJavaThread javaThread && ThreadMonitorManager.getDefault().isInDeadlock(javaThread)) { return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR); } return null; @@ -2162,9 +2132,6 @@ public Color getBackground(Object element) { return null; } - private ImageDescriptor getImageDescriptor(String key) { - return JavaDebugImages.getImageDescriptor(key); - } /* (non-Javadoc) * @see org.eclipse.debug.ui.IDebugModelPresentationExtension#requiresUIThread(java.lang.Object) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariableLabelProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariableLabelProvider.java index 8dbd74e8a4..244122de5c 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariableLabelProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaVariableLabelProvider.java @@ -55,7 +55,7 @@ */ public class JavaVariableLabelProvider extends VariableLabelProvider implements IPreferenceChangeListener { - public static JDIModelPresentation fLabelProvider = new JDIModelPresentation(); + private final static JDIModelPresentation fLabelProvider = new JDIModelPresentation(); /** * Map of view id to qualified name setting */ From 08085f1ef2573f182ade52ec977c9d5070906575 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Sun, 13 Jun 2021 00:40:31 +0200 Subject: [PATCH 02/15] Bug 574170 - Add coloring to the stack frames of the debug thread view and add an action to enable/disable colorization from the context menu. All of the categorize can be switched on or off Use separate images for different stack frame categories. Update the labels to 'Your Source code', 'Your Test code', 'Highlighted ...' --- .../jdt/debug/tests/AutomatedSuite.java | 2 + .../StackFramePresentationProviderTest.java | 149 ++++++ org.eclipse.jdt.debug.ui/plugin.properties | 35 ++ org.eclipse.jdt.debug.ui/plugin.xml | 118 +++++ .../internal/debug/ui/DebugUIMessages.java | 32 ++ .../debug/ui/DebugUIMessages.properties | 33 ++ .../debug/ui/IJDIPreferencesConstants.java | 58 ++- .../debug/ui/IJavaDebugHelpContextIds.java | 1 + .../ui/JDIDebugUIPreferenceInitializer.java | 13 + .../debug/ui/JDIModelPresentation.java | 54 ++- .../ui/JavaStackFramePreferencePage.java | 429 ++++++++++++++++++ .../ui/StackFramePresentationProvider.java | 290 ++++++++++++ .../ui/actions/ColorizeStackFramesAction.java | 44 ++ .../jdt/debug/core/IJavaStackFrame.java | 25 + .../debug/core/model/JDIStackFrame.java | 12 + .../launching/JavaSourceLookupDirector.java | 2 +- 16 files changed, 1284 insertions(+), 13 deletions(-) create mode 100644 org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/StackFramePresentationProviderTest.java create mode 100644 org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java create mode 100644 org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java create mode 100644 org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ColorizeStackFramesAction.java 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 a1c4b892bd..954fd2c163 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 @@ -149,6 +149,7 @@ import org.eclipse.jdt.debug.tests.ui.DetailPaneManagerTests; import org.eclipse.jdt.debug.tests.ui.JavaSnippetEditorTest; import org.eclipse.jdt.debug.tests.ui.OpenFromClipboardTests; +import org.eclipse.jdt.debug.tests.ui.StackFramePresentationProviderTest; import org.eclipse.jdt.debug.tests.ui.ViewManagementTests; import org.eclipse.jdt.debug.tests.ui.VirtualThreadsDebugViewTests; import org.eclipse.jdt.debug.tests.ui.presentation.ModelPresentationTests; @@ -243,6 +244,7 @@ public AutomatedSuite() { addTest(new TestSuite(StepFilterTests.class)); addTest(new TestSuite(StepIntoSelectionTests.class)); addTest(new TestSuite(InstanceFilterTests.class)); + addTest(new TestSuite(StackFramePresentationProviderTest.class)); if (JavaProjectHelper.isJava6Compatible()) { addTest(new TestSuite(ForceReturnTests.class)); } diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/StackFramePresentationProviderTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/StackFramePresentationProviderTest.java new file mode 100644 index 0000000000..f8e6029266 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/StackFramePresentationProviderTest.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2021 Zsombor Gegesy. + * + * 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: + * Zsombor Gegesy - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.debug.tests.ui; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.jdt.debug.core.IJavaReferenceType; +import org.eclipse.jdt.debug.core.IJavaStackFrame; +import org.eclipse.jdt.debug.tests.AbstractDebugTest; +import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants; +import org.eclipse.jdt.internal.debug.ui.StackFramePresentationProvider; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceStore; + +public class StackFramePresentationProviderTest extends AbstractDebugTest { + + public StackFramePresentationProviderTest(String name) { + super(name); + } + + private StackFramePresentationProvider provider; + private IPreferenceStore preferenceStore; + + private static class LaunchMock implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return null; + } + } + private static class JavaStackFrameMock implements InvocationHandler { + + final IJavaReferenceType referenceType; + final boolean synthetic; + + public JavaStackFrameMock(IJavaReferenceType referenceType, boolean synthetic) { + this.referenceType = referenceType; + this.synthetic = synthetic; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + var methodName = method.getName(); + if ("getReferenceType".equals(methodName)) { + return referenceType; + } + if ("isSynthetic".equals(methodName)) { + return synthetic; + } + if ("getLaunch".equals(methodName)) { + return Proxy.newProxyInstance(StackFramePresentationProviderTest.class.getClassLoader(), new Class[] { + ILaunch.class }, new LaunchMock()); + + } + return null; + } + } + + private static class JavaReferenceTypeMock implements InvocationHandler { + + final String name; + + public JavaReferenceTypeMock(String name) { + this.name = name; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("getName".equals(method.getName())) { + return name; + } + return null; + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + preferenceStore = new PreferenceStore(); + preferenceStore.setValue(IJDIPreferencesConstants.PREF_ACTIVE_PLATFORM_FRAME_FILTER_LIST, "java.*,javax.*"); + preferenceStore.setValue(IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES, true); + preferenceStore.setValue(IJDIPreferencesConstants.PREF_COLORIZE_PLATFORM_METHODS, true); + preferenceStore.setValue(IJDIPreferencesConstants.PREF_COLORIZE_CUSTOM_METHODS, true); + preferenceStore.setValue(IJDIPreferencesConstants.PREF_COLORIZE_SYNTHETIC_METHODS, true); + provider = new StackFramePresentationProvider(preferenceStore); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + provider.close(); + } + + private IJavaReferenceType createReference(String name) { + return (IJavaReferenceType) Proxy.newProxyInstance(StackFramePresentationProviderTest.class.getClassLoader(), new Class[] { + IJavaReferenceType.class }, new JavaReferenceTypeMock(name)); + } + + private IJavaStackFrame createFrame(IJavaReferenceType refType, boolean syntetic) { + return (IJavaStackFrame) Proxy.newProxyInstance(StackFramePresentationProviderTest.class.getClassLoader(), new Class[] { + IJavaStackFrame.class }, new JavaStackFrameMock(refType, syntetic)); + } + + private IJavaStackFrame.Category categorize(String refTypeName, boolean syntetic) throws DebugException { + return categorize(createReference(refTypeName), syntetic); + } + + private IJavaStackFrame.Category categorize(IJavaReferenceType refType, boolean syntetic) throws DebugException { + return provider.categorize(createFrame(refType, syntetic)); + } + + public void testFiltering() throws DebugException { + assertEquals(IJavaStackFrame.Category.SYNTHETIC, categorize("org.eclipse.Something", true)); + assertEquals(IJavaStackFrame.Category.PLATFORM, categorize("java.lang.String", false)); + assertEquals(IJavaStackFrame.Category.UNKNOWN, categorize("org.eclipse.Other", false)); + } + + public void testUpdateWorks() throws DebugException { + var something = createReference("org.eclipse.Something"); + var other = createReference("org.eclipse.Other"); + assertEquals(IJavaStackFrame.Category.UNKNOWN, categorize(something, false)); + assertEquals(IJavaStackFrame.Category.UNKNOWN, categorize(other, false)); + preferenceStore.setValue(IJDIPreferencesConstants.PREF_ACTIVE_CUSTOM_FRAME_FILTER_LIST, "org.eclipse.Something"); + + assertEquals(IJavaStackFrame.Category.CUSTOM_FILTERED, categorize(something, false)); + assertEquals(IJavaStackFrame.Category.UNKNOWN, categorize(other, false)); + } + + public void testSwitchOffPlatform() throws DebugException { + assertEquals(IJavaStackFrame.Category.PLATFORM, categorize("java.lang.String", false)); + preferenceStore.setValue(IJDIPreferencesConstants.PREF_COLORIZE_PLATFORM_METHODS, false); + assertEquals(IJavaStackFrame.Category.UNKNOWN, categorize("java.lang.String", false)); + } + +} diff --git a/org.eclipse.jdt.debug.ui/plugin.properties b/org.eclipse.jdt.debug.ui/plugin.properties index 202848d6a4..6354c2f118 100644 --- a/org.eclipse.jdt.debug.ui/plugin.properties +++ b/org.eclipse.jdt.debug.ui/plugin.properties @@ -98,6 +98,8 @@ JavaSnippetEditor.label= Scrapbook javaStepFilterPrefName=Step Filtering javaDetailFormattersPrefName=Detail Formatters +javaStackFramePrefName=Stack Frames + javaVariableHoverLabel=Variable Values javaVariableHoverDescription=Shows the value of the selected variable when debugging. @@ -178,6 +180,9 @@ showMonitorThreadInfo.tooltip=Show the Thread & Monitor Information showNullEntriesAction.label=Show &Null Array Entries showNullEntriesAction.tooltip=Show Null Array Entries +colorizeStackFrames.label=Mark Stack Frames with &Colors +colorizeStackFrames.tooltip=Based on the function mark the stack frames with different colors + stepIntoSelectionHyperlinkDetector.label=Step Into Selection stepIntoSelectionHyperlinkDetector.description=Performs the step into selection command on demand via a hyperlink @@ -289,6 +294,36 @@ InDeadlockColorDefinition.description=The color used to render deadlocked thread LabeledObjectColorDefinition.label=Labeled objects LabeledObjectColorDefinition.description=The color used to render labeled objects in debug views. +CustomFilteredStackFrameFgColorDefinition.label=Highlighted methods +CustomFilteredStackFrameBgColorDefinition.label=Highlighted methods background +CustomFilteredStackFrameFgColorDefinition.description=The color used to render the Highlighted stack frames in the Debug view. +CustomFilteredStackFrameBgColorDefinition.description=The background color used to render the Highlighted stack frames in the Debug view. + +SyntheticStackFrameFgColorDefinition.label=Synthetic methods +SyntheticStackFrameBgColorDefinition.label=Synthetic methods background +SyntheticStackFrameFgColorDefinition.description=The color used to render stack frames for synthetic methods in the Debug view. +SyntheticStackFrameBgColorDefinition.description=The background color used to render stack frames for synthetic methods in the Debug view. + +PlatformStackFrameFgColorDefinition.label=Platform methods +PlatformStackFrameBgColorDefinition.label=Platform methods background +PlatformStackFrameFgColorDefinition.description=The color used to render stack frames for Platform methods in the Debug view. +PlatformStackFrameBgColorDefinition.description=The background color used to render stack frame for Platform methods in the Debug view. + +TestStackFrameFgColorDefinition.label=Your Test code +TestStackFrameBgColorDefinition.label=Your Test code background +TestStackFrameFgColorDefinition.description=The color used to render stack frames for Your Test code in the Debug view. +TestStackFrameBgColorDefinition.description=The background color used to render stack frames for Your Test code in the Debug view. + +LibraryStackFrameFgColorDefinition.label=Library code +LibraryStackFrameBgColorDefinition.label=Library code background +LibraryStackFrameFgColorDefinition.description=The color used to render stack frames for Library code in the Debug view. +LibraryStackFrameBgColorDefinition.description=The background color used to render stack frames for Library code in the Debug view. + +ProductionStackFrameFgColorDefinition.label=Your Source code +ProductionStackFrameBgColorDefinition.label=Your Source code background +ProductionStackFrameFgColorDefinition.description=The color used used to render stack frames for Your Source code in the Debug view. +ProductionStackFrameBgColorDefinition.description=The background color used to render stack frames for Your Source code in the Debug view. + javaStackTraceConsole.label= Java Stack Trace Console FormatStackTraceActionDelegate.name= Format FormatStackTraceActionDelegate.tooltip= Format diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml index 6a4cbe0fc8..a63457b3ad 100644 --- a/org.eclipse.jdt.debug.ui/plugin.xml +++ b/org.eclipse.jdt.debug.ui/plugin.xml @@ -2208,6 +2208,15 @@ style="toggle" menubarPath="org.eclipse.jdt.debug.ui.LaunchView.javaSubmenu/javaPart" id="org.eclipse.jdt.debug.ui.launchViewActions.ShowMonitorThreadInfo"/> + + + + + %LabeledObjectColorDefinition.description + + + %CustomFilteredStackFrameFgColorDefinition.description + + + %CustomFilteredStackFrameBgColorDefinition.description + + + + %SyntheticStackFrameFgColorDefinition.description + + + %SyntheticStackFrameBgColorDefinition.description + + + + %PlatformStackFrameFgColorDefinition.description + + + %PlatformStackFrameBgColorDefinition.description + + + + %TestStackFrameFgColorDefinition.description + + + %TestStackFrameBgColorDefinition.description + + + + %ProductionStackFrameFgColorDefinition.description + + + %ProductionStackFrameBgColorDefinition.description + + + + %LibraryStackFrameFgColorDefinition.description + + + %LibraryStackFrameBgColorDefinition.description + diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java index 223d5fa756..cfcc225be1 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java @@ -100,6 +100,38 @@ public class DebugUIMessages extends NLS { public static String JavaHotCodeReplaceListener_0; + public static String JavaStackFramesPreferencePage_title; + public static String JavaStackFramesPreferencePage_description; + public static String JavaStackFramesPreferencePage__Color_stack_frames; + public static String JavaStackFramesPreferencePage_Defined_stack_frame_filters_for_platform; + public static String JavaStackFramesPreferencePage_Defined_custom_stack_frame_filters; + public static String JavaStackFramesPreferencePage_Appearance_of_stack_frames; + public static String JavaStackFramesPreferencePage_fg_color; + public static String JavaStackFramesPreferencePage_bg_color; + public static String JavaStackFramesPreferencePage_category_platform; + public static String JavaStackFramesPreferencePage_category_synthetic; + public static String JavaStackFramesPreferencePage_category_library; + public static String JavaStackFramesPreferencePage_category_test; + public static String JavaStackFramesPreferencePage_category_production; + public static String JavaStackFramesPreferencePage_category_custom_filter; + public static String JavaStackFramesPreferencePage_Filter_platform; + public static String JavaStackFramesPreferencePage_Filter_synthetic; + public static String JavaStackFramesPreferencePage_Filter_library; + public static String JavaStackFramesPreferencePage_Filter_test; + public static String JavaStackFramesPreferencePage_Filter_production; + public static String JavaStackFramesPreferencePage_Filter_custom; + public static String JavaStackFramesPreferencePage_Add__Filter; + public static String JavaStackFramesPreferencePage_Add__Package; + public static String JavaStackFramesPreferencePage_Add__Type; + public static String JavaStackFramesPreferencePage_Remove; + public static String JavaStackFramesPreferencePage_Enable_All; + public static String JavaStackFramesPreferencePage_Disable_All; + public static String JavaStackFramesPreferencePage_Add_package_for_stack_filters; + public static String JavaStackFramesPreferencePage_Select_a_package_for_stack_filter; + public static String JavaStackFramesPreferencePage_Add_type_for_stack_filters; + public static String JavaStackFramesPreferencePage_Select_a_type_for_stack_filter; + + public static String JavaStepFilterPreferencePage_0; public static String JavaStepFilterPreferencePage_Filter_co_nstructors_19; diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties index e93b81afbd..1b8a526140 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties @@ -47,6 +47,39 @@ JavaHotCodeReplaceListener_0= JavaPrimitivesPreferencePage_0=Primitive Display Options JavaPrimitivesPreferencePage_1=Display options for primitive values +JavaStackFramesPreferencePage_title=Stack Frame Colors +JavaStackFramesPreferencePage_description=Color of the Stack Frames set accordingly to these filters when 'Mark Stack Frames differently' toggle is activated. +JavaStackFramesPreferencePage__Color_stack_frames=Mark Stack Frames differently +JavaStackFramesPreferencePage_Defined_stack_frame_filters_for_platform=Filters to define &Platform methods: +JavaStackFramesPreferencePage_Defined_custom_stack_frame_filters=Filters for High&lighting stack frames: +JavaStackFramesPreferencePage_Appearance_of_stack_frames=&Appearance of the stack frames: +JavaStackFramesPreferencePage_fg_color=&Foreground Color +JavaStackFramesPreferencePage_bg_color=&Background Color +JavaStackFramesPreferencePage_category_custom_filter=Highlighted methods +JavaStackFramesPreferencePage_category_synthetic=Synthetic methods +JavaStackFramesPreferencePage_category_platform=Platform methods +JavaStackFramesPreferencePage_category_test=Your Test code +JavaStackFramesPreferencePage_category_production=Your Source code +JavaStackFramesPreferencePage_category_library=Library code +JavaStackFramesPreferencePage_Filter_custom=Mark &Highlighted stack frames differently +JavaStackFramesPreferencePage_Filter_synthetic=Mark S&ynthetic methods differently +JavaStackFramesPreferencePage_Filter_platform=Mark &Platform methods differently +JavaStackFramesPreferencePage_Filter_test=Mark Your &Test code differently +JavaStackFramesPreferencePage_Filter_production=Mark Your &Source code differently +JavaStackFramesPreferencePage_Filter_library=Mark &Library code differently + +JavaStackFramesPreferencePage_Add__Filter=Add &Filter... +JavaStackFramesPreferencePage_Add__Package=Add &Packages... +JavaStackFramesPreferencePage_Add__Type=Add &Class... +JavaStackFramesPreferencePage_Remove=&Remove +JavaStackFramesPreferencePage_Enable_All=&Enable All +JavaStackFramesPreferencePage_Disable_All=D&isable All + +JavaStackFramesPreferencePage_Add_package_for_stack_filters=Add Packages to Stack Filters +JavaStackFramesPreferencePage_Select_a_package_for_stack_filter=&Select a package for stack filtering: +JavaStackFramesPreferencePage_Add_type_for_stack_filters=Add Class to Stack Filters +JavaStackFramesPreferencePage_Select_a_type_for_stack_filter=&Select a class for stack filtering: + JavaStepFilterPreferencePage_Filter_co_nstructors_19=Filter co&nstructors JavaStepFilterPreferencePage_Filter_s_ynthetic_methods__requires_VM_support__17=Filter s&ynthetic methods (requires VM support) JavaStepFilterPreferencePage_Filter_static__initializers_18=Filter static &initializers diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJDIPreferencesConstants.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJDIPreferencesConstants.java index 6f0af1f7d4..758b6360e7 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJDIPreferencesConstants.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJDIPreferencesConstants.java @@ -80,8 +80,43 @@ public interface IJDIPreferencesConstants { public static final String PREF_STEP_THRU_FILTERS = IJavaDebugUIConstants.PLUGIN_ID + ".step_thru_filters"; //$NON-NLS-1$ /** - * List of active step filters. A String containing a comma - * separated list of fully qualified type names/patterns. + * Boolean preference indicating whether to colorize the stack frames in the debug view. + * + */ + public static final String PREF_COLORIZE_STACK_FRAMES = IJavaDebugUIConstants.PLUGIN_ID + ".colorize_stack_frames"; //$NON-NLS-1$ + public static final String PREF_COLORIZE_PLATFORM_METHODS = IJavaDebugUIConstants.PLUGIN_ID + ".colorize_platform_methods"; //$NON-NLS-1$ + public static final String PREF_COLORIZE_SYNTHETIC_METHODS = IJavaDebugUIConstants.PLUGIN_ID + ".colorize_synthetic_methods"; //$NON-NLS-1$ + public static final String PREF_COLORIZE_LIBRARY_METHODS = IJavaDebugUIConstants.PLUGIN_ID + ".colorize_library_methods"; //$NON-NLS-1$ + public static final String PREF_COLORIZE_TEST_METHODS = IJavaDebugUIConstants.PLUGIN_ID + ".colorize_test_methods"; //$NON-NLS-1$ + public static final String PREF_COLORIZE_PRODUCTION_METHODS = IJavaDebugUIConstants.PLUGIN_ID + ".colorize_production_methods"; //$NON-NLS-1$ + public static final String PREF_COLORIZE_CUSTOM_METHODS = IJavaDebugUIConstants.PLUGIN_ID + ".colorize_custom_methods"; //$NON-NLS-1$ + + /** + * List of active filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type + * names/patterns. + */ + public static final String PREF_ACTIVE_CUSTOM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".active_custom_frames_filters"; //$NON-NLS-1$ + + /** + * List of inactive filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type + * names/patterns. + */ + public static final String PREF_INACTIVE_CUSTOM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".inactive_custom_frames_filters"; //$NON-NLS-1$ + + /** + * List of active filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type + * names/patterns. + */ + public static final String PREF_ACTIVE_PLATFORM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".active_platform_frames_filters"; //$NON-NLS-1$ + + /** + * List of inactive filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type + * names/patterns. + */ + public static final String PREF_INACTIVE_PLATFORM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".inactive_platform_frames_filters"; //$NON-NLS-1$ + + /** + * List of active step filters. A String containing a comma separated list of fully qualified type names/patterns. */ public static final String PREF_ACTIVE_FILTERS_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".active_filters"; //$NON-NLS-1$ @@ -218,6 +253,25 @@ public interface IJDIPreferencesConstants { public static final String PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR= "org.eclipse.jdt.debug.ui.InDeadlockColor"; //$NON-NLS-1$ public static final String PREF_LABELED_OBJECT_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".LabeledObject"; //$NON-NLS-1$ + + public static final String PREF_TEST_STACK_FRAME_FG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".TestStackFrameFgColor"; //$NON-NLS-1$ + public static final String PREF_TEST_STACK_FRAME_BG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".TestStackFrameBgColor"; //$NON-NLS-1$ + + public static final String PREF_LIB_STACK_FRAME_FG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".LibraryStackFrameFgColor"; //$NON-NLS-1$ + public static final String PREF_LIB_STACK_FRAME_BG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".LibraryStackFrameBgColor"; //$NON-NLS-1$ + + public static final String PREF_SYNT_STACK_FRAME_FG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".SyntheticStackFrameFgColor"; //$NON-NLS-1$ + public static final String PREF_SYNT_STACK_FRAME_BG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".SyntheticStackFrameBgColor"; //$NON-NLS-1$ + + public static final String PREF_PLATFORM_STACK_FRAME_FG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".PlatformStackFrameFgColor"; //$NON-NLS-1$ + public static final String PREF_PLATFORM_STACK_FRAME_BG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".PlatformStackFrameBgColor"; //$NON-NLS-1$ + + public static final String PREF_PRODUCTION_STACK_FRAME_FG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".ProductionStackFrameFgColor"; //$NON-NLS-1$ + public static final String PREF_PRODUCTION_STACK_FRAME_BG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".ProductionStackFrameBgColor"; //$NON-NLS-1$ + + public static final String PREF_CUSTOM_FILTERED_STACK_FRAME_FG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".CustomFilteredStackFrameFgColor"; //$NON-NLS-1$ + public static final String PREF_CUSTOM_FILTERED_STACK_FRAME_BG_COLOR = IJavaDebugUIConstants.PLUGIN_ID + ".CustomFilteredStackFrameBgColor"; //$NON-NLS-1$ + /** * @since 3.2 */ diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJavaDebugHelpContextIds.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJavaDebugHelpContextIds.java index 33b90bcef7..9e17bc4656 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJavaDebugHelpContextIds.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJavaDebugHelpContextIds.java @@ -52,6 +52,7 @@ public interface IJavaDebugHelpContextIds { public static final String LAUNCH_JRE_PROPERTY_PAGE= PREFIX + "launch_jre_property_page_context"; //$NON-NLS-1$ public static final String JAVA_DEBUG_PREFERENCE_PAGE= PREFIX + "java_debug_preference_page_context"; //$NON-NLS-1$ public static final String JAVA_STEP_FILTER_PREFERENCE_PAGE= PREFIX + "java_step_filter_preference_page_context"; //$NON-NLS-1$ + public static final String JAVA_STACK_FRAMES_PREFERENCE_PAGE = PREFIX + "java_stack_frames_preference_page_context"; //$NON-NLS-1$ public static final String JAVA_BREAKPOINT_PREFERENCE_PAGE= PREFIX + "java_breakpoint_preference_page_context"; //$NON-NLS-1$ public static final String JAVA_DETAIL_FORMATTER_PREFERENCE_PAGE= PREFIX + "java_detail_formatter_preference_page_context"; //$NON-NLS-1$ public static final String JAVA_PRIMITIVES_PREFERENCE_PAGE= PREFIX + "java_primitives_preference_page_context"; //$NON-NLS-1$ diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java index b5963e4aa9..73248d2bd1 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java @@ -46,6 +46,19 @@ public void initializeDefaultPreferences() { store.setDefault(IJDIPreferencesConstants.PREF_INACTIVE_FILTERS_LIST, "com.ibm.*,com.sun.*,java.*,javax.*,jdk.*,jrockit.*,org.omg.*,sun.*,sunw.*"); //$NON-NLS-1$ store.setDefault(IJDIPreferencesConstants.PREF_STEP_THRU_FILTERS, true); + // JavaStackFramePreferencePage + store.setDefault(IJDIPreferencesConstants.PREF_ACTIVE_PLATFORM_FRAME_FILTER_LIST, "java.*,javax.*,jdk.*,sun.*,sunw.*,org.junit.*,org.eclipse.jdt.internal.*"); //$NON-NLS-1$ + store.setDefault(IJDIPreferencesConstants.PREF_INACTIVE_PLATFORM_FRAME_FILTER_LIST, ""); //$NON-NLS-1$ + store.setDefault(IJDIPreferencesConstants.PREF_ACTIVE_CUSTOM_FRAME_FILTER_LIST, ""); //$NON-NLS-1$ + store.setDefault(IJDIPreferencesConstants.PREF_INACTIVE_CUSTOM_FRAME_FILTER_LIST, ""); //$NON-NLS-1$ + store.setDefault(IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES, true); + store.setDefault(IJDIPreferencesConstants.PREF_COLORIZE_PLATFORM_METHODS, true); + store.setDefault(IJDIPreferencesConstants.PREF_COLORIZE_SYNTHETIC_METHODS, true); + store.setDefault(IJDIPreferencesConstants.PREF_COLORIZE_LIBRARY_METHODS, true); + store.setDefault(IJDIPreferencesConstants.PREF_COLORIZE_TEST_METHODS, true); + store.setDefault(IJDIPreferencesConstants.PREF_COLORIZE_PRODUCTION_METHODS, true); + store.setDefault(IJDIPreferencesConstants.PREF_COLORIZE_CUSTOM_METHODS, true); + store.setDefault(IDebugUIConstants.ID_VARIABLE_VIEW + "." + IJDIPreferencesConstants.PREF_SHOW_CONSTANTS, false); //$NON-NLS-1$ store.setDefault(IDebugUIConstants.ID_EXPRESSION_VIEW + "." + IJDIPreferencesConstants.PREF_SHOW_CONSTANTS, false); //$NON-NLS-1$ store.setDefault(IDebugUIConstants.ID_VARIABLE_VIEW + "." + IJDIPreferencesConstants.PREF_SHOW_STATIC_VARIABLES, false); //$NON-NLS-1$ diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java index 3237a571ca..f964f6bb28 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java @@ -150,6 +150,8 @@ public class JDIModelPresentation extends LabelProvider implements IDebugModelPr private JavaElementLabelProvider fJavaLabelProvider; + private StackFramePresentationProvider fStackFrameProvider; + public JDIModelPresentation() { super(); } @@ -164,6 +166,9 @@ public void dispose() { fJavaLabelProvider.dispose(); } fAttributes.clear(); + if (fStackFrameProvider != null) { + fStackFrameProvider.close(); + } } /** @@ -736,7 +741,10 @@ public Image getImage(Object item) { return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING); } } - if (item instanceof IJavaStackFrame || item instanceof IJavaThread || item instanceof IJavaDebugTarget) { + if (item instanceof IJavaStackFrame) { + return getStackFrameImage((IJavaStackFrame) item); + } + if (item instanceof IJavaThread || item instanceof IJavaDebugTarget) { return getDebugElementImage(item); } if (item instanceof IJavaValue) { @@ -936,6 +944,25 @@ protected Image getDebugElementImage(Object element) { return getDebugImage(image, flags); } + private Image getStackFrameImage(IJavaStackFrame stackFrame) { + var image = getStackFrameProvider().getStackFrameImage(stackFrame); + if (image == null) { + image = DebugUITools.getDefaultImageDescriptor(stackFrame); + } + + int flags = 0; + try { + if (stackFrame.isOutOfSynch()) { + flags = JDIImageDescriptor.IS_OUT_OF_SYNCH; + } else if (!stackFrame.isObsolete() && stackFrame.isSynchronized()) { + flags = JDIImageDescriptor.SYNCHRONIZED; + } + } catch (DebugException e) { + // no need to log errors - elements may no longer exist by the time we render them + } + + return getDebugImage(image, flags); + } /** * Returns the image associated with the given element or null * if none is defined. @@ -964,15 +991,6 @@ protected Image getExpressionImage(Object expression) { */ private int computeJDIAdornmentFlags(Object element) { try { - if (element instanceof IJavaStackFrame) { - IJavaStackFrame javaStackFrame = ((IJavaStackFrame)element); - if (javaStackFrame.isOutOfSynch()) { - return JDIImageDescriptor.IS_OUT_OF_SYNCH; - } - if (!javaStackFrame.isObsolete() && javaStackFrame.isSynchronized()) { - return JDIImageDescriptor.SYNCHRONIZED; - } - } if (element instanceof IJavaThread) { int flag= 0; IJavaThread javaThread = ((IJavaThread)element); @@ -1205,6 +1223,10 @@ protected boolean isShowVariableTypeNames() { } } + protected boolean isColorizeStackFrames() { + return getStackFrameProvider().isColorizeStackFrames(); + } + protected boolean isShowHexValues() { return JDIDebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IJDIPreferencesConstants.PREF_SHOW_HEX); } @@ -2048,6 +2070,12 @@ protected JavaElementLabelProvider getJavaLabelProvider() { return fJavaLabelProvider; } + private StackFramePresentationProvider getStackFrameProvider() { + if (fStackFrameProvider == null) { + fStackFrameProvider = new StackFramePresentationProvider(); + } + return fStackFrameProvider; + } /** * Returns whether the given field variable has the same name as any variables */ @@ -2114,6 +2142,9 @@ public Color getForeground(Object element) { if (element instanceof IJavaThread javaThread && ThreadMonitorManager.getDefault().isInDeadlock(javaThread)) { return getColorFromRegistry(IJDIPreferencesConstants.PREF_THREAD_MONITOR_IN_DEADLOCK_COLOR); } + if (element instanceof IJavaStackFrame frame && isColorizeStackFrames()) { + return getStackFrameProvider().getForeground(frame); + } return null; } @@ -2129,6 +2160,9 @@ protected Color getColorFromRegistry(String symbolicName) { */ @Override public Color getBackground(Object element) { + if (element instanceof IJavaStackFrame frame && isColorizeStackFrames()) { + return getStackFrameProvider().getBackground(frame); + } return null; } diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java new file mode 100644 index 0000000000..521843028a --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java @@ -0,0 +1,429 @@ +/******************************************************************************* + * Copyright (c) 2022 Zsombor Gegesy and others. + * + * 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: + * Zsombor Gegesy - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.ui; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.eclipse.debug.internal.ui.SWTFactory; +import org.eclipse.jdt.debug.core.IJavaStackFrame; +import org.eclipse.jdt.debug.core.IJavaStackFrame.Category; +import org.eclipse.jdt.internal.ui.filtertable.FilterManager; +import org.eclipse.jdt.internal.ui.filtertable.JavaFilterTable; +import org.eclipse.jdt.internal.ui.filtertable.JavaFilterTable.ButtonLabel; +import org.eclipse.jdt.internal.ui.filtertable.JavaFilterTable.DialogLabels; +import org.eclipse.jdt.internal.ui.filtertable.JavaFilterTable.FilterTableConfig; +import org.eclipse.jface.preference.ColorSelector; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PlatformUI; + +/** + * The preference page for Java stack frame categorization, located at the node Java > Debug > Stack Frames + * + * @since 3.19 + */ +public class JavaStackFramePreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + private static class CategoryColors { + final IJavaStackFrame.Category category; + final String key; + Color fgColor; + Color bgColor; + + CategoryColors(Category category, String key) { + this.category = category; + this.key = key; + } + + Color getForegroundColor(StackFramePresentationProvider stackFramePresentationProvider) { + if (fgColor != null) { + return fgColor; + } + return stackFramePresentationProvider.getForegroundColor(category); + } + + Color getBackgroundColor(StackFramePresentationProvider stackFramePresentationProvider) { + if (bgColor != null) { + return bgColor; + } + return stackFramePresentationProvider.getBackgroundColor(category); + } + + void setToDefault() { + this.fgColor = null; + this.bgColor = null; + } + } + + private class StackFrameCategoryLabelProvider extends LabelProvider implements IColorProvider { + + @Override + public String getText(Object element) { + return ((CategoryColors) element).key; + } + + @Override + public Color getForeground(Object element) { + return ((CategoryColors) element).getForegroundColor(stackFramePresentationProvider); + } + + @Override + public Color getBackground(Object element) { + return ((CategoryColors) element).getBackgroundColor(stackFramePresentationProvider); + } + } + + private static class StackFrameCategoryContentProvider implements IStructuredContentProvider { + @Override + public Object[] getElements(Object inputElement) { + return ((java.util.List) inputElement).toArray(); + } + } + + private static class PreferenceButton { + private final Button button; + private final String flag; + + PreferenceButton(Composite container, String label, IPreferenceStore store, String flag) { + this.button = SWTFactory.createCheckButton(container, label, null, store.getBoolean(flag), 2); + this.flag = flag; + } + + boolean performDefault(IPreferenceStore store) { + var defValue = store.getBoolean(flag); + button.setSelection(defValue); + return defValue; + } + + void performOk(IPreferenceStore store) { + store.setValue(flag, button.getSelection()); + } + + boolean isSelected() { + return button.getSelection(); + } + + void setEnabled(boolean enabled) { + this.button.setEnabled(enabled); + } + + PreferenceButton widgetSelected(Consumer consumer) { + this.button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + consumer.accept(button.getSelection()); + } + }); + return this; + } + } + + public static final String PAGE_ID = "org.eclipse.jdt.debug.ui.JavaStackFramePreferencePage"; //$NON-NLS-1$ + final static FilterManager PLATFORM_STACK_FRAMES = new FilterManager(IJDIPreferencesConstants.PREF_ACTIVE_PLATFORM_FRAME_FILTER_LIST, IJDIPreferencesConstants.PREF_INACTIVE_PLATFORM_FRAME_FILTER_LIST); + final static FilterManager CUSTOM_STACK_FRAMES = new FilterManager(IJDIPreferencesConstants.PREF_ACTIVE_CUSTOM_FRAME_FILTER_LIST, IJDIPreferencesConstants.PREF_INACTIVE_CUSTOM_FRAME_FILTER_LIST); + + //widgets + private PreferenceButton fColorizeStackFrames; + private List categoryButtons; + private PreferenceButton fEnablePlatformButton; + private PreferenceButton fEnableCustomButton; + private JavaFilterTable fPlatformStackFilterTable; + private JavaFilterTable fCustomStackFilterTable; + private TableViewer fAppearanceList; + private List colors; + private ColorSelector fFgColorSelector; + private ColorSelector fBgColorSelector; + private StackFramePresentationProvider stackFramePresentationProvider; + + /** + * Constructor + */ + public JavaStackFramePreferencePage() { + super(); + setPreferenceStore(JDIDebugUIPlugin.getDefault().getPreferenceStore()); + setTitle(DebugUIMessages.JavaStackFramesPreferencePage_title); + setDescription(DebugUIMessages.JavaStackFramesPreferencePage_description); + stackFramePresentationProvider = new StackFramePresentationProvider(getPreferenceStore()); + categoryButtons = new ArrayList<>(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createContents(Composite parent) { + PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IJavaDebugHelpContextIds.JAVA_STACK_FRAMES_PREFERENCE_PAGE); + // The main composite + Composite composite = SWTFactory.createComposite(parent, parent.getFont(), 1, 1, GridData.FILL_BOTH, 0, 0); + createStepFilterPreferences(composite); + return composite; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + @Override + public void init(IWorkbench workbench) {} + + /** + * Create a group to contain the step filter related widgetry + */ + private void createStepFilterPreferences(Composite parent) { + var store = getPreferenceStore(); + Composite container = SWTFactory.createComposite(parent, parent.getFont(), 2, 1, GridData.FILL_BOTH, 0, 0); + fColorizeStackFrames = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage__Color_stack_frames, store, IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES); + fColorizeStackFrames.widgetSelected(this::setPageEnablement); + initializeDialogUnits(container); + + this.fEnableCustomButton = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_custom, store, IJDIPreferencesConstants.PREF_COLORIZE_CUSTOM_METHODS).widgetSelected(selected -> fCustomStackFilterTable.setEnabled(selected + && fColorizeStackFrames.isSelected())); + categoryButtons.add(this.fEnableCustomButton); + + fCustomStackFilterTable = new JavaFilterTable(this, CUSTOM_STACK_FRAMES, + new FilterTableConfig() // configuration for the table + .setAddFilter(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Add__Filter)) + .setAddPackage(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Add__Package)) + .setAddType(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Add__Type)) + .setRemove(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Remove)) + .setSelectAll(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Enable_All)) + .setDeselectAll(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Disable_All)) + .setAddPackageDialog(new DialogLabels(DebugUIMessages.JavaStackFramesPreferencePage_Add_package_for_stack_filters, DebugUIMessages.JavaStackFramesPreferencePage_Select_a_package_for_stack_filter)) + .setAddTypeDialog(new DialogLabels(DebugUIMessages.JavaStackFramesPreferencePage_Add_type_for_stack_filters, DebugUIMessages.JavaStackFramesPreferencePage_Select_a_package_for_stack_filter)) + .setLabelText(DebugUIMessages.JavaStackFramesPreferencePage_Defined_custom_stack_frame_filters) + .setHelpContextId(IJavaDebugHelpContextIds.JAVA_STACK_FRAMES_PREFERENCE_PAGE)); + fCustomStackFilterTable.createTable(container); + + categoryButtons.add(new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_synthetic, store, IJDIPreferencesConstants.PREF_COLORIZE_SYNTHETIC_METHODS)); + + this.fEnablePlatformButton = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_platform, store, IJDIPreferencesConstants.PREF_COLORIZE_PLATFORM_METHODS).widgetSelected(selected -> fPlatformStackFilterTable.setEnabled(selected + && fColorizeStackFrames.isSelected())); + categoryButtons.add(fEnablePlatformButton); + + fPlatformStackFilterTable = new JavaFilterTable(this, PLATFORM_STACK_FRAMES, + new FilterTableConfig() + .setAddFilter(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Add__Filter)) + .setAddPackage(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Add__Package)) + .setAddType(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Add__Type)) + .setRemove(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Remove)) + .setSelectAll(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Enable_All)) + .setDeselectAll(new ButtonLabel(DebugUIMessages.JavaStackFramesPreferencePage_Disable_All)) + .setAddPackageDialog(new DialogLabels(DebugUIMessages.JavaStackFramesPreferencePage_Add_package_for_stack_filters, + DebugUIMessages.JavaStackFramesPreferencePage_Select_a_package_for_stack_filter)) + .setAddTypeDialog(new DialogLabels(DebugUIMessages.JavaStackFramesPreferencePage_Add_type_for_stack_filters, + DebugUIMessages.JavaStackFramesPreferencePage_Select_a_package_for_stack_filter)) + .setLabelText(DebugUIMessages.JavaStackFramesPreferencePage_Defined_stack_frame_filters_for_platform) + .setHelpContextId(IJavaDebugHelpContextIds.JAVA_STACK_FRAMES_PREFERENCE_PAGE) + ); + fPlatformStackFilterTable.createTable(container); + + categoryButtons.add(new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_test, store, IJDIPreferencesConstants.PREF_COLORIZE_TEST_METHODS)); + categoryButtons.add(new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_production, store, IJDIPreferencesConstants.PREF_COLORIZE_PRODUCTION_METHODS)); + categoryButtons.add(new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_library, store, IJDIPreferencesConstants.PREF_COLORIZE_LIBRARY_METHODS)); + + createAppearanceList(container); + + setPageEnablement(fColorizeStackFrames.isSelected()); + initList(); + } + + private void createAppearanceList(Composite container) { + + SWTFactory.createLabel(container, DebugUIMessages.JavaStackFramesPreferencePage_Appearance_of_stack_frames, 2); + + var editorComposite = new Composite(container, SWT.NONE); + var layout = new GridLayout(); + layout.numColumns = 2; + layout.marginHeight = 0; + layout.marginWidth = 0; + editorComposite.setLayout(layout); + var gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_VERTICAL); + gd.horizontalSpan = 2; + editorComposite.setLayoutData(gd); + + fAppearanceList = new TableViewer(editorComposite, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION); + fAppearanceList.setLabelProvider(new StackFrameCategoryLabelProvider()); + fAppearanceList.setContentProvider(new StackFrameCategoryContentProvider()); + + gd = new GridData(SWT.BEGINNING, SWT.FILL, false, true); + gd.heightHint = convertHeightInCharsToPixels(8); + fAppearanceList.getControl().setLayoutData(gd); + fAppearanceList.addSelectionChangedListener(event -> { + var selection = event.getStructuredSelection(); + var valid = !selection.isEmpty(); + fFgColorSelector.getButton().setEnabled(valid); + fBgColorSelector.getButton().setEnabled(valid); + if (valid) { + CategoryColors category = (CategoryColors) selection.getFirstElement(); + var color = category.getForegroundColor(stackFramePresentationProvider); + if (color != null) { + fFgColorSelector.setColorValue(color.getRGB()); + } + var bgColor = category.getBackgroundColor(stackFramePresentationProvider); + if (bgColor != null) { + fBgColorSelector.setColorValue(bgColor.getRGB()); + } + } + }); + + Composite stylesComposite = new Composite(editorComposite, SWT.NONE); + layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.numColumns = 2; + stylesComposite.setLayout(layout); + stylesComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); + + SWTFactory.createLabel(stylesComposite, DebugUIMessages.JavaStackFramesPreferencePage_fg_color, 1); + + fFgColorSelector = createColorSelector(stylesComposite); + fFgColorSelector.addListener(event -> { + var selection = getSelected(); + if (selection != null) { + selection.fgColor = toColor(event); + fAppearanceList.update(selection, null); + } + }); + + SWTFactory.createLabel(stylesComposite, DebugUIMessages.JavaStackFramesPreferencePage_bg_color, 1); + + fBgColorSelector = createColorSelector(stylesComposite); + fBgColorSelector.addListener(event -> { + var selection = getSelected(); + if (selection != null) { + selection.bgColor = toColor(event); + fAppearanceList.update(selection, null); + } + }); + } + + private CategoryColors getSelected() { + var selection = fAppearanceList.getStructuredSelection().getFirstElement(); + if (selection instanceof CategoryColors) { + return (CategoryColors) selection; + } + return null; + } + + private Color toColor(PropertyChangeEvent event) { + return new Color((RGB) event.getNewValue()); + } + + private ColorSelector createColorSelector(Composite stylesComposite) { + var colorSelector = new ColorSelector(stylesComposite); + Button button = colorSelector.getButton(); + var gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalAlignment = GridData.BEGINNING; + button.setLayoutData(gd); + return colorSelector; + } + + private void initList() { + colors = List.of( + new CategoryColors(IJavaStackFrame.Category.CUSTOM_FILTERED, DebugUIMessages.JavaStackFramesPreferencePage_category_custom_filter), // + new CategoryColors(IJavaStackFrame.Category.SYNTHETIC, DebugUIMessages.JavaStackFramesPreferencePage_category_synthetic), // + new CategoryColors(IJavaStackFrame.Category.PLATFORM, DebugUIMessages.JavaStackFramesPreferencePage_category_platform), // + new CategoryColors(IJavaStackFrame.Category.TEST, DebugUIMessages.JavaStackFramesPreferencePage_category_test), // + new CategoryColors(IJavaStackFrame.Category.PRODUCTION, DebugUIMessages.JavaStackFramesPreferencePage_category_production), // + new CategoryColors(IJavaStackFrame.Category.LIBRARY, DebugUIMessages.JavaStackFramesPreferencePage_category_library) // + ); + fAppearanceList.setInput(colors); + fAppearanceList.setSelection(new StructuredSelection(colors.get(0))); + } + + /** + * Enables or disables the widgets on the page, with the + * exception of fUseStepFiltersButton according + * to the passed boolean + * @param enabled the new enablement status of the page's widgets + * @since 3.2 + */ + protected void setPageEnablement(boolean enabled) { + fPlatformStackFilterTable.setEnabled(enabled && fEnablePlatformButton.isSelected()); + fCustomStackFilterTable.setEnabled(enabled && fEnableCustomButton.isSelected()); + for (var categoryButton : categoryButtons) { + categoryButton.setEnabled(enabled); + } + fAppearanceList.getControl().setEnabled(enabled); + fFgColorSelector.getButton().setEnabled(enabled); + fBgColorSelector.getButton().setEnabled(enabled); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#performOk() + */ + @Override + public boolean performOk() { + IPreferenceStore store = getPreferenceStore(); + fPlatformStackFilterTable.performOk(store); + fCustomStackFilterTable.performOk(store); + fColorizeStackFrames.performOk(store); + for (var categoryButton : categoryButtons) { + categoryButton.performOk(store); + } + for (var color : colors) { + if (color.fgColor != null) { + stackFramePresentationProvider.setForegroundColor(color.category, color.fgColor.getRGB()); + } + if (color.bgColor != null) { + stackFramePresentationProvider.setBackgroundColor(color.category, color.bgColor.getRGB()); + } + } + return super.performOk(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#performDefaults() + */ + @Override + protected void performDefaults() { + var store = getPreferenceStore(); + boolean enabled = fColorizeStackFrames.performDefault(store); + + for (var categoryButton : categoryButtons) { + categoryButton.performDefault(store); + } + for (var color : colors) { + color.setToDefault(); + } + fAppearanceList.update(colors.toArray(), null); + fAppearanceList.setSelection(new StructuredSelection(colors.get(0))); + + setPageEnablement(enabled); + + fPlatformStackFilterTable.performDefaults(); + fCustomStackFilterTable.performDefaults(); + super.performDefaults(); + } + +} diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java new file mode 100644 index 0000000000..8408f57604 --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java @@ -0,0 +1,290 @@ +/******************************************************************************* + * Copyright (c) 2021 Zsombor Gegesy. + * + * 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: + * Zsombor Gegesy - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.ui; + +import java.util.EnumMap; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.debug.core.DebugException; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.debug.core.IJavaStackFrame; +import org.eclipse.jdt.debug.core.IJavaStackFrame.Category; +import org.eclipse.jdt.internal.ui.JavaPluginImages; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.ColorRegistry; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.PlatformUI; + +/** + * Provides foreground and background colors for stack frames in a debug view. After usage, it needs to be closed, so it could unregister itself from + * preference storage. + * + */ +public class StackFramePresentationProvider implements IPropertyChangeListener, AutoCloseable { + + private final static EnumMap fgKeyMap = new EnumMap<>(Map.of( + Category.CUSTOM_FILTERED, IJDIPreferencesConstants.PREF_CUSTOM_FILTERED_STACK_FRAME_FG_COLOR, + Category.SYNTHETIC, IJDIPreferencesConstants.PREF_SYNT_STACK_FRAME_FG_COLOR, + Category.PLATFORM, IJDIPreferencesConstants.PREF_PLATFORM_STACK_FRAME_FG_COLOR, + Category.TEST, IJDIPreferencesConstants.PREF_TEST_STACK_FRAME_FG_COLOR, + Category.PRODUCTION, IJDIPreferencesConstants.PREF_PRODUCTION_STACK_FRAME_FG_COLOR, + Category.LIBRARY, IJDIPreferencesConstants.PREF_LIB_STACK_FRAME_FG_COLOR + )); + + private final static EnumMap bgKeyMap = new EnumMap<>(Map.of( + Category.CUSTOM_FILTERED, IJDIPreferencesConstants.PREF_CUSTOM_FILTERED_STACK_FRAME_BG_COLOR, // + Category.SYNTHETIC, IJDIPreferencesConstants.PREF_SYNT_STACK_FRAME_BG_COLOR, + Category.PLATFORM, IJDIPreferencesConstants.PREF_PLATFORM_STACK_FRAME_BG_COLOR, + Category.TEST, IJDIPreferencesConstants.PREF_TEST_STACK_FRAME_BG_COLOR, + Category.PRODUCTION, IJDIPreferencesConstants.PREF_PRODUCTION_STACK_FRAME_BG_COLOR, + Category.LIBRARY, IJDIPreferencesConstants.PREF_LIB_STACK_FRAME_BG_COLOR + )); + + private final static Map prefConst = Map.of( + IJDIPreferencesConstants.PREF_COLORIZE_CUSTOM_METHODS, Category.CUSTOM_FILTERED, + IJDIPreferencesConstants.PREF_COLORIZE_SYNTHETIC_METHODS, Category.SYNTHETIC, + IJDIPreferencesConstants.PREF_COLORIZE_PLATFORM_METHODS, Category.PLATFORM, + IJDIPreferencesConstants.PREF_COLORIZE_TEST_METHODS, Category.TEST, // + IJDIPreferencesConstants.PREF_COLORIZE_PRODUCTION_METHODS, Category.PRODUCTION, + IJDIPreferencesConstants.PREF_COLORIZE_LIBRARY_METHODS, Category.LIBRARY + ); + + class Filters { + private final String[] filters; + + Filters(String[] filters) { + this.filters = filters; + } + + public boolean match(String fqcName) { + for (String filter : filters) { + if (filter.endsWith("*")) { //$NON-NLS-1$ + if (fqcName.startsWith(filter.substring(0, filter.length() - 1))) { + return true; + } + } else { + if (filter.equals(fqcName)) { + return true; + } + } + } + return false; + } + } + + private Filters platform; + private Filters custom; + private final IPreferenceStore store; + private final EnumMap enabledCategories; + private boolean colorizeStackFrames; + + public StackFramePresentationProvider(IPreferenceStore store) { + this.store = store; + platform = getActivePlatformFilters(store); + custom = getActiveCustomFilters(store); + store.addPropertyChangeListener(this); + enabledCategories = new EnumMap<>(Category.class); + for (var kv : prefConst.entrySet()) { + boolean enabled = store.getBoolean(kv.getKey()); + enabledCategories.put(kv.getValue(), enabled); + } + colorizeStackFrames = store.getBoolean(IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES); + } + + private Filters getActivePlatformFilters(IPreferenceStore store) { + return new Filters(JavaStackFramePreferencePage.PLATFORM_STACK_FRAMES.getActiveList(store)); + } + + private Filters getActiveCustomFilters(IPreferenceStore store) { + return new Filters(JavaStackFramePreferencePage.CUSTOM_STACK_FRAMES.getActiveList(store)); + } + + public StackFramePresentationProvider() { + this(JDIDebugUIPlugin.getDefault().getPreferenceStore()); + } + + /** + * @return the foreground color of the stack frame's category, or null, if not defined. + */ + public Color getForeground(IJavaStackFrame frame) { + Category cat = getCategory(frame); + if (cat == null) { + return null; + } + return getForegroundColor(cat); + } + + /** + * @return the foreground color of the category, or null, if it's not defined. + */ + Color getForegroundColor(Category cat) { + var key = fgKeyMap.get(cat); + if (key != null) { + return getColorRegistry().get(key); + } + return null; + } + + private ColorRegistry getColorRegistry() { + return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry(); + } + + void setForegroundColor(Category cat, RGB color) { + var key = fgKeyMap.get(cat); + if (key != null) { + getColorRegistry().put(key, color); + } + } + + void setBackgroundColor(Category cat, RGB color) { + var key = bgKeyMap.get(cat); + if (key != null) { + getColorRegistry().put(key, color); + } + } + + /** + * @return the category specific image for the stack frame, or null, if there is no one defined. + */ + public ImageDescriptor getStackFrameImage(IJavaStackFrame frame) { + var category = getCategory(frame); + if (category != null) { + switch (category) { + case LIBRARY: + return JavaPluginImages.DESC_OBJS_JAR; + case TEST: + return JavaPluginImages.DESC_OBJS_TEST_ATTRIB; + case PRODUCTION: + return JavaPluginImages.DESC_OBJS_CFILE; + default: + break; + } + } + return null; + } + + /** + * @return the background color assigned to the stack frame's category, or null, if there is no category matching. + */ + public Color getBackground(IJavaStackFrame frame) { + Category cat = getCategory(frame); + if (cat == null) { + return null; + } + return getBackgroundColor(cat); + } + + Color getBackgroundColor(Category cat) { + var key = bgKeyMap.get(cat); + if (key != null) { + return getColorRegistry().get(key); + } + return null; + } + + private IJavaStackFrame.Category getCategory(IJavaStackFrame frame) { + if (frame.getCategory() != null) { + return frame.getCategory(); + } + IJavaStackFrame.Category result = Category.UNKNOWN; + try { + result = categorize(frame); + } catch (DebugException e) { + JDIDebugUIPlugin.log(e); + } + frame.setCategory(result); + return result; + } + + private boolean isEnabled(Category category) { + return Boolean.TRUE.equals(enabledCategories.get(category)); + } + + /** + * Visible for testing! + */ + public IJavaStackFrame.Category categorize(IJavaStackFrame frame) throws DebugException { + var refTypeName = frame.getReferenceType().getName(); + if (isEnabled(Category.CUSTOM_FILTERED) && custom.match(refTypeName)) { + return Category.CUSTOM_FILTERED; + } + if (isEnabled(Category.SYNTHETIC) && frame.isSynthetic()) { + return Category.SYNTHETIC; + } + if (isEnabled(Category.PLATFORM) && platform.match(refTypeName)) { + return Category.PLATFORM; + } + + return categorizeSourceElement(frame); + } + + private Category categorizeSourceElement(IJavaStackFrame frame) { + var sourceLocator = frame.getLaunch().getSourceLocator(); + if (sourceLocator == null) { + return Category.UNKNOWN; + } + var source = sourceLocator.getSourceElement(frame); + if (source == null) { + return Category.UNKNOWN; + } + if (source instanceof IFile) { + if (isEnabled(Category.TEST)) { + var file = (IFile) source; + var jproj = JavaCore.create(file.getProject()); + var cp = jproj.findContainingClasspathEntry(file); + if (cp != null && cp.isTest()) { + return Category.TEST; + } + } + if (isEnabled(Category.PRODUCTION)) { + return Category.PRODUCTION; + } + } else if (source instanceof IClassFile && isEnabled(Category.LIBRARY)) { + return Category.LIBRARY; + } + return Category.UNKNOWN; + } + + @Override + public void close() { + store.removePropertyChangeListener(this); + } + + @Override + public void propertyChange(PropertyChangeEvent event) { + String prop = event.getProperty(); + if (IJDIPreferencesConstants.PREF_ACTIVE_PLATFORM_FRAME_FILTER_LIST.equals(prop)) { + platform = getActivePlatformFilters(store); + } else if (IJDIPreferencesConstants.PREF_ACTIVE_CUSTOM_FRAME_FILTER_LIST.equals(prop)) { + custom = getActiveCustomFilters(store); + } else if (IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES.equals(prop)) { + colorizeStackFrames = (Boolean) event.getNewValue(); + } else { + var category = prefConst.get(prop); + if (category != null) { + enabledCategories.put(category, (Boolean) event.getNewValue()); + } + } + } + + public boolean isColorizeStackFrames() { + return colorizeStackFrames; + } +} diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ColorizeStackFramesAction.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ColorizeStackFramesAction.java new file mode 100644 index 0000000000..792b0f520c --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ColorizeStackFramesAction.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2022 Zsombor Gegesy and others. + * + * 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: + * Zsombor Gegesy - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.ui.actions; + +import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants; + +/** + * Enable/disable the coloring of the stack frames in debug views. + * + */ +public class ColorizeStackFramesAction extends ToggleBooleanPreferenceAction { + + /* + * (non-Javadoc) + * + * @see org.eclipse.jdt.internal.debug.ui.actions.ViewFilterAction#getPreferenceKey() + */ + @Override + protected String getPreferenceKey() { + return IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jdt.internal.debug.ui.actions.ViewFilterAction#getCompositeKey() + */ + @Override + protected String getCompositeKey() { + return getPreferenceKey(); + } + +} diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java index a77f2e3ebf..d8258fc228 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java @@ -35,6 +35,16 @@ public interface IJavaStackFrame extends IStackFrame, IJavaModifiers, IFilteredStep, IDropToFrame { + /** + * Categorization of the stack frame. + * + * @since 3.21 + * + */ + enum Category { + CUSTOM_FILTERED, SYNTHETIC, PLATFORM, TEST, PRODUCTION, LIBRARY, UNKNOWN + } + /** * Status code indicating a stack frame is invalid. A stack frame becomes * invalid when the thread containing the stack frame resumes. A stack frame @@ -533,4 +543,19 @@ public IJavaVariable findVariable(String variableName) * @since 3.3 */ public void forceReturn(IJavaValue value) throws DebugException; + + /** + * @since 3.21 + * @param category + * the new category of the stack frame. + */ + public void setCategory(Category category); + + /** + * + * @since 3.21 + * @return the category of the stack frame, null if it's not yet categorized. + */ + public Category getCategory(); + } diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java index d92e27b34a..379270e71c 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java @@ -139,6 +139,8 @@ public class JDIStackFrame extends JDIDebugElement implements IJavaStackFrame { */ private boolean fIsTop; + private Category fCategory; + @SuppressWarnings("restriction") private static final String SYNTHETIC_OUTER_LOCAL_PREFIX = new String(org.eclipse.jdt.internal.compiler.lookup.TypeConstants.SYNTHETIC_OUTER_LOCAL_PREFIX); @@ -1730,4 +1732,14 @@ public void forceReturn(IJavaValue value) throws DebugException { public void setIsTop(boolean isTop) { this.fIsTop = isTop; } + + @Override + public Category getCategory() { + return fCategory; + } + + @Override + public void setCategory(Category fCategory) { + this.fCategory = fCategory; + } } diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JavaSourceLookupDirector.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JavaSourceLookupDirector.java index b38d2968c0..4a5aa4983a 100644 --- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JavaSourceLookupDirector.java +++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JavaSourceLookupDirector.java @@ -32,7 +32,7 @@ */ public class JavaSourceLookupDirector extends AbstractSourceLookupDirector { - private static Set fFilteredTypes; + private static final Set fFilteredTypes; static { fFilteredTypes = new HashSet<>(); From c506df90a02d14695296ce022cefda0822d142a7 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Sat, 2 Jul 2022 18:41:16 +0200 Subject: [PATCH 03/15] Collapsing stack frames, which doesn't relevant, so only test and prod code will be visible --- org.eclipse.jdt.debug.ui/plugin.properties | 3 + org.eclipse.jdt.debug.ui/plugin.xml | 8 ++ .../internal/debug/ui/DebugUIMessages.java | 2 + .../debug/ui/DebugUIMessages.properties | 3 + .../debug/ui/IJDIPreferencesConstants.java | 1 + .../ui/JDIDebugUIPreferenceInitializer.java | 1 + .../debug/ui/JDIModelPresentation.java | 4 + .../ui/JavaStackFramePreferencePage.java | 12 ++- .../ui/StackFramePresentationProvider.java | 14 ++- .../ui/actions/CollapseStackFramesAction.java | 34 ++++++++ .../monitors/JavaThreadContentProvider.java | 86 +++++++++++++++---- .../debug/core/model/GroupedStackFrame.java | 55 ++++++++++++ 12 files changed, 201 insertions(+), 22 deletions(-) create mode 100644 org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/CollapseStackFramesAction.java create mode 100644 org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java diff --git a/org.eclipse.jdt.debug.ui/plugin.properties b/org.eclipse.jdt.debug.ui/plugin.properties index 6354c2f118..9fbcda1a7b 100644 --- a/org.eclipse.jdt.debug.ui/plugin.properties +++ b/org.eclipse.jdt.debug.ui/plugin.properties @@ -183,6 +183,9 @@ showNullEntriesAction.tooltip=Show Null Array Entries colorizeStackFrames.label=Mark Stack Frames with &Colors colorizeStackFrames.tooltip=Based on the function mark the stack frames with different colors +collapseStackFrames.label=Collapse Stack Frames +collapseStackFrames.tooltip=Hide less relevant stack frames + stepIntoSelectionHyperlinkDetector.label=Step Into Selection stepIntoSelectionHyperlinkDetector.description=Performs the step into selection command on demand via a hyperlink diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml index a63457b3ad..6ca16c72e5 100644 --- a/org.eclipse.jdt.debug.ui/plugin.xml +++ b/org.eclipse.jdt.debug.ui/plugin.xml @@ -2216,6 +2216,14 @@ style="toggle" menubarPath="org.eclipse.jdt.debug.ui.LaunchView.javaSubmenu/javaPart" id="org.eclipse.jdt.debug.ui.launchViewActions.ColorizeStackFrames"/> + categoryButtons; private PreferenceButton fEnablePlatformButton; private PreferenceButton fEnableCustomButton; @@ -203,7 +204,10 @@ private void createStepFilterPreferences(Composite parent) { var store = getPreferenceStore(); Composite container = SWTFactory.createComposite(parent, parent.getFont(), 2, 1, GridData.FILL_BOTH, 0, 0); fColorizeStackFrames = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage__Color_stack_frames, store, IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES); - fColorizeStackFrames.widgetSelected(this::setPageEnablement); + fColorizeStackFrames.widgetSelected(this::updateCheckboxes); + + fCollapseStackFrames = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage__Collapse_stack_frames, store, IJDIPreferencesConstants.PREF_COLLAPSE_STACK_FRAMES); + fCollapseStackFrames.widgetSelected(this::updateCheckboxes); initializeDialogUnits(container); this.fEnableCustomButton = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_custom, store, IJDIPreferencesConstants.PREF_COLORIZE_CUSTOM_METHODS).widgetSelected(selected -> fCustomStackFilterTable.setEnabled(selected @@ -361,6 +365,10 @@ private void initList() { fAppearanceList.setSelection(new StructuredSelection(colors.get(0))); } + protected void updateCheckboxes(@SuppressWarnings("unused") boolean flag) { + setPageEnablement(fCollapseStackFrames.isSelected() || fColorizeStackFrames.isSelected()); + } + /** * Enables or disables the widgets on the page, with the * exception of fUseStepFiltersButton according @@ -408,7 +416,7 @@ public boolean performOk() { @Override protected void performDefaults() { var store = getPreferenceStore(); - boolean enabled = fColorizeStackFrames.performDefault(store); + boolean enabled = fColorizeStackFrames.performDefault(store) || fCollapseStackFrames.performDefault(store); for (var categoryButton : categoryButtons) { categoryButton.performDefault(store); diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java index 8408f57604..44cfb559c7 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java @@ -94,6 +94,7 @@ public boolean match(String fqcName) { private final IPreferenceStore store; private final EnumMap enabledCategories; private boolean colorizeStackFrames; + private boolean collapseStackFrames; public StackFramePresentationProvider(IPreferenceStore store) { this.store = store; @@ -106,6 +107,7 @@ public StackFramePresentationProvider(IPreferenceStore store) { enabledCategories.put(kv.getValue(), enabled); } colorizeStackFrames = store.getBoolean(IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES); + collapseStackFrames = store.getBoolean(IJDIPreferencesConstants.PREF_COLLAPSE_STACK_FRAMES); } private Filters getActivePlatformFilters(IPreferenceStore store) { @@ -199,7 +201,10 @@ Color getBackgroundColor(Category cat) { return null; } - private IJavaStackFrame.Category getCategory(IJavaStackFrame frame) { + /** + * @return the {@link IJavaStackFrame.Category} which matches the rules. + */ + public IJavaStackFrame.Category getCategory(IJavaStackFrame frame) { if (frame.getCategory() != null) { return frame.getCategory(); } @@ -276,6 +281,8 @@ public void propertyChange(PropertyChangeEvent event) { custom = getActiveCustomFilters(store); } else if (IJDIPreferencesConstants.PREF_COLORIZE_STACK_FRAMES.equals(prop)) { colorizeStackFrames = (Boolean) event.getNewValue(); + } else if (IJDIPreferencesConstants.PREF_COLLAPSE_STACK_FRAMES.equals(prop)) { + collapseStackFrames = (Boolean) event.getNewValue(); } else { var category = prefConst.get(prop); if (category != null) { @@ -287,4 +294,9 @@ public void propertyChange(PropertyChangeEvent event) { public boolean isColorizeStackFrames() { return colorizeStackFrames; } + + public boolean isCollapseStackFrames() { + return collapseStackFrames; + } + } diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/CollapseStackFramesAction.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/CollapseStackFramesAction.java new file mode 100644 index 0000000000..f517dc25c2 --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/CollapseStackFramesAction.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2022 Zsombor Gegesy and others. + * + * 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: + * Zsombor Gegesy - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.ui.actions; + +import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants; + +/** + * Enable/disable collapsing of the non-relevant stack frames in debug views. + * + */ +public class CollapseStackFramesAction extends ToggleBooleanPreferenceAction { + + @Override + protected String getPreferenceKey() { + return IJDIPreferencesConstants.PREF_COLLAPSE_STACK_FRAMES; + } + + @Override + protected String getCompositeKey() { + return getPreferenceKey(); + } + +} diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java index 69524111c5..6a33f984a6 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java @@ -13,20 +13,27 @@ *******************************************************************************/ package org.eclipse.jdt.internal.debug.ui.monitors; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IDebugElement; -import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.jdt.debug.core.IJavaDebugTarget; +import org.eclipse.jdt.debug.core.IJavaStackFrame.Category; import org.eclipse.jdt.debug.core.IJavaThread; import org.eclipse.jdt.debug.ui.JavaDebugUtils; +import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame; +import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame; import org.eclipse.jdt.internal.debug.core.model.JDIThread; +import org.eclipse.jdt.internal.debug.ui.StackFramePresentationProvider; /** * Java thread presentation adapter. @@ -35,6 +42,8 @@ */ public class JavaThreadContentProvider extends JavaElementContentProvider { + private StackFramePresentationProvider stackFrameProvider; + /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) */ @@ -44,7 +53,7 @@ protected int getChildCount(Object element, IPresentationContext context, IViewe if (!thread.isSuspended()) { return 0; } - int childCount = thread.getFrameCount(); + int childCount = getFrameCount(thread); if (isDisplayMonitors()) { if (((IJavaDebugTarget) thread.getDebugTarget()).supportsMonitorInformation()) { childCount+= thread.getOwnedMonitors().length; @@ -59,6 +68,13 @@ protected int getChildCount(Object element, IPresentationContext context, IViewe return childCount; } + private int getFrameCount(IJavaThread thread) throws DebugException { + if (getStackFrameProvider().isCollapseStackFrames()) { + return getStackFrames(thread).size(); + } + return thread.getFrameCount(); + } + /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, org.eclipse.core.runtime.IProgressMonitor) */ @@ -81,40 +97,66 @@ protected Object[] getChildren(IJavaThread thread) { } } } - IStackFrame[] frames = thread.getStackFrames(); + var frames = getStackFrames(thread); if (!isDisplayMonitors()) { - return frames; + return frames.toArray(); } - Object[] children; - int length = frames.length; if (((IJavaDebugTarget) thread.getDebugTarget()).supportsMonitorInformation()) { IDebugElement[] ownedMonitors = JavaDebugUtils.getOwnedMonitors(thread); IDebugElement contendedMonitor = JavaDebugUtils.getContendedMonitor(thread); - length += ownedMonitors.length; if (contendedMonitor != null) { - length++; + // Insert the contended monitor after the owned monitors + frames.add(0, contendedMonitor); } - children = new Object[length]; if (ownedMonitors.length > 0) { - System.arraycopy(ownedMonitors, 0, children, 0, ownedMonitors.length); - } - if (contendedMonitor != null) { - // Insert the contended monitor after the owned monitors - children[ownedMonitors.length] = contendedMonitor; + frames.addAll(0, Arrays.asList(ownedMonitors)); } } else { - children = new Object[length + 1]; - children[0] = new NoMonitorInformationElement(thread.getDebugTarget()); + frames.add(0, new NoMonitorInformationElement(thread.getDebugTarget())); } - int offset = children.length - frames.length; - System.arraycopy(frames, 0, children, offset, frames.length); - return children; + return frames.toArray(); } catch (DebugException e) { return EMPTY; } } + private List getStackFrames(IJavaThread thread) throws DebugException { + var frames = thread.getStackFrames(); + var stackFrameProvider = getStackFrameProvider(); + var result = new ArrayList<>(frames.length); + if (!stackFrameProvider.isCollapseStackFrames()) { + result.addAll(Arrays.asList(frames)); + return result; + } + GroupedStackFrame lastGroupping = null; + boolean first = true; + for (var frame : frames) { + if (first) { + result.add(frame); + first = false; + } else { + if (frame instanceof JDIStackFrame) { + var javaFrame = (JDIStackFrame) frame; + var category = stackFrameProvider.getCategory(javaFrame); + if (category == null || category == Category.TEST || category == Category.PRODUCTION) { + result.add(javaFrame); + lastGroupping = null; + } else { + if (lastGroupping == null) { + lastGroupping = new GroupedStackFrame(javaFrame.getJavaDebugTarget()); + result.add(lastGroupping); + } + lastGroupping.add(javaFrame); + } + } else { + result.add(frame); + } + } + } + return result; + } + /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#hasChildren(java.lang.Object, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, org.eclipse.core.runtime.IProgressMonitor) */ @@ -174,5 +216,11 @@ private ISchedulingRule getThreadRule(IViewerUpdate[] updates) { return null; } + private synchronized StackFramePresentationProvider getStackFrameProvider() { + if (stackFrameProvider == null) { + stackFrameProvider = new StackFramePresentationProvider(); + } + return stackFrameProvider; + } } diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java new file mode 100644 index 0000000000..bed222c00f --- /dev/null +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2022 Zsombor Gegesy. + * + * 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: + * Zsombor Gegesy - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.core.model; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.debug.core.IJavaStackFrame; + +/** + * Class to collect multiple stack frame. + * + */ +public class GroupedStackFrame extends JDIDebugElement { + private final List stackFrames = new ArrayList<>(); + + public GroupedStackFrame(JDIDebugTarget target) { + super(target); + } + + public void add(IJavaStackFrame frame) { + stackFrames.add(frame); + } + + public int getFrameCount() { + return stackFrames.size(); + } + + public Object[] getFramesAsArray() { + return stackFrames.toArray(); + } + + /** + * Delegates actions, and everything to the top most stack frame. + */ + @Override + public T getAdapter(Class adapterType) { + var adapter = super.getAdapter(adapterType); + if (adapter == null && !stackFrames.isEmpty()) { + adapter = stackFrames.get(0).getAdapter(adapterType); + } + return adapter; + } +} From 5c1d874e1b7e01c40e0b33a7d9e776f9669c98ed Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Tue, 5 Jul 2022 00:21:13 +0200 Subject: [PATCH 04/15] Add ISourceLookup adapter to GroupedStackFrame --- .../jdt/internal/debug/ui/JDIDebugUIPlugin.java | 2 ++ .../JavaDebugShowInAdapterFactory.java | 14 +++++++++++++- .../debug/core/model/GroupedStackFrame.java | 4 ++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java index b95bdd0cdb..792eedc872 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java @@ -57,6 +57,7 @@ import org.eclipse.jdt.debug.core.IJavaVariable; import org.eclipse.jdt.debug.core.JDIDebugModel; import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants; +import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame; import org.eclipse.jdt.internal.debug.ui.breakpoints.ExceptionInspector; import org.eclipse.jdt.internal.debug.ui.breakpoints.JavaBreakpointTypeAdapterFactory; import org.eclipse.jdt.internal.debug.ui.classpath.ClasspathEntryAdapterFactory; @@ -391,6 +392,7 @@ public void start(BundleContext context) throws Exception { IAdapterFactory showInFactory = new JavaDebugShowInAdapterFactory(); manager.registerAdapters(showInFactory, IJavaStackFrame.class); + manager.registerAdapters(showInFactory, GroupedStackFrame.class); IAdapterFactory columnFactory = new ColumnPresentationAdapterFactory(); manager.registerAdapters(columnFactory, IJavaVariable.class); 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 df57f68c4f..69c5fe7738 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 @@ -14,7 +14,10 @@ package org.eclipse.jdt.internal.debug.ui.sourcelookup; import org.eclipse.core.runtime.IAdapterFactory; +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.ui.part.IShowInSource; import org.eclipse.ui.part.IShowInTargetList; @@ -40,6 +43,15 @@ public T getAdapter(Object adaptableObject, Class adapterType) { return (T) new StackFrameShowInTargetListAdapter(); } } + if (adapterType == ISourceDisplay.class) { + if (adaptableObject instanceof GroupedStackFrame) { + return (T) (ISourceDisplay) (element, page, forceSourceLookup) -> { + var frame = ((GroupedStackFrame) element).getTopMostFrame(); + SourceLookupFacility.getDefault().displaySource(frame, page, forceSourceLookup); + }; + } + + } return null; } @@ -48,7 +60,7 @@ public T getAdapter(Object adaptableObject, Class adapterType) { */ @Override public Class[] getAdapterList() { - return new Class[]{IShowInSource.class, IShowInTargetList.class}; + return new Class[] { IShowInSource.class, IShowInTargetList.class, ISourceDisplay.class }; } } diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java index bed222c00f..ca24bc8eff 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java @@ -41,6 +41,10 @@ public Object[] getFramesAsArray() { return stackFrames.toArray(); } + public IJavaStackFrame getTopMostFrame() { + return !stackFrames.isEmpty() ? stackFrames.get(0) : null; + } + /** * Delegates actions, and everything to the top most stack frame. */ From 085408234e3427d29f9ac168c1315866982c8fc1 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Tue, 5 Jul 2022 00:41:10 +0200 Subject: [PATCH 05/15] Fix enabling/disabling checkboxes properly based on the 'collapse..' and 'colorize...' checkbox --- .../debug/ui/JavaStackFramePreferencePage.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java index 7dda85c25e..37b37d8e82 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java @@ -210,8 +210,8 @@ private void createStepFilterPreferences(Composite parent) { fCollapseStackFrames.widgetSelected(this::updateCheckboxes); initializeDialogUnits(container); - this.fEnableCustomButton = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_custom, store, IJDIPreferencesConstants.PREF_COLORIZE_CUSTOM_METHODS).widgetSelected(selected -> fCustomStackFilterTable.setEnabled(selected - && fColorizeStackFrames.isSelected())); + this.fEnableCustomButton = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_custom, store, IJDIPreferencesConstants.PREF_COLORIZE_CUSTOM_METHODS) // + .widgetSelected(selected -> fCustomStackFilterTable.setEnabled(selected && isCategoryHandlingEnabled())); categoryButtons.add(this.fEnableCustomButton); fCustomStackFilterTable = new JavaFilterTable(this, CUSTOM_STACK_FRAMES, @@ -230,8 +230,8 @@ private void createStepFilterPreferences(Composite parent) { categoryButtons.add(new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_synthetic, store, IJDIPreferencesConstants.PREF_COLORIZE_SYNTHETIC_METHODS)); - this.fEnablePlatformButton = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_platform, store, IJDIPreferencesConstants.PREF_COLORIZE_PLATFORM_METHODS).widgetSelected(selected -> fPlatformStackFilterTable.setEnabled(selected - && fColorizeStackFrames.isSelected())); + this.fEnablePlatformButton = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_platform, store, IJDIPreferencesConstants.PREF_COLORIZE_PLATFORM_METHODS) // + .widgetSelected(selected -> fPlatformStackFilterTable.setEnabled(selected && isCategoryHandlingEnabled())); categoryButtons.add(fEnablePlatformButton); fPlatformStackFilterTable = new JavaFilterTable(this, PLATFORM_STACK_FRAMES, @@ -257,7 +257,7 @@ private void createStepFilterPreferences(Composite parent) { createAppearanceList(container); - setPageEnablement(fColorizeStackFrames.isSelected()); + setPageEnablement(isCategoryHandlingEnabled()); initList(); } @@ -366,7 +366,11 @@ private void initList() { } protected void updateCheckboxes(@SuppressWarnings("unused") boolean flag) { - setPageEnablement(fCollapseStackFrames.isSelected() || fColorizeStackFrames.isSelected()); + setPageEnablement(isCategoryHandlingEnabled()); + } + + private boolean isCategoryHandlingEnabled() { + return fCollapseStackFrames.isSelected() || fColorizeStackFrames.isSelected(); } /** From f695920e26c245af8ee59510287f54eaf6284c88 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Tue, 5 Jul 2022 00:45:37 +0200 Subject: [PATCH 06/15] Try to make labels a bit more consistent --- .../debug/ui/DebugUIMessages.properties | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties index c7fb3fe6c4..f7379ba3f3 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties @@ -49,25 +49,25 @@ JavaPrimitivesPreferencePage_1=Display options for primitive values JavaStackFramesPreferencePage_title=Stack Frame Colors JavaStackFramesPreferencePage_description=Color of the Stack Frames set accordingly to these filters when 'Mark Stack Frames differently' toggle is activated. -JavaStackFramesPreferencePage__Color_stack_frames=Mark Stack Frames differently +JavaStackFramesPreferencePage__Color_stack_frames=Mark stack frames differently JavaStackFramesPreferencePage__Collapse_stack_frames=Collapse non relevant stack frames -JavaStackFramesPreferencePage_Defined_stack_frame_filters_for_platform=Filters to define &Platform methods: -JavaStackFramesPreferencePage_Defined_custom_stack_frame_filters=Filters for High&lighting stack frames: +JavaStackFramesPreferencePage_Defined_stack_frame_filters_for_platform=Filters to define &platform methods: +JavaStackFramesPreferencePage_Defined_custom_stack_frame_filters=Filters for highlighted stack frames: JavaStackFramesPreferencePage_Appearance_of_stack_frames=&Appearance of the stack frames: -JavaStackFramesPreferencePage_fg_color=&Foreground Color -JavaStackFramesPreferencePage_bg_color=&Background Color +JavaStackFramesPreferencePage_fg_color=&Foreground color +JavaStackFramesPreferencePage_bg_color=&Background color JavaStackFramesPreferencePage_category_custom_filter=Highlighted methods JavaStackFramesPreferencePage_category_synthetic=Synthetic methods JavaStackFramesPreferencePage_category_platform=Platform methods -JavaStackFramesPreferencePage_category_test=Your Test code -JavaStackFramesPreferencePage_category_production=Your Source code +JavaStackFramesPreferencePage_category_test=Your test code +JavaStackFramesPreferencePage_category_production=Your source code JavaStackFramesPreferencePage_category_library=Library code -JavaStackFramesPreferencePage_Filter_custom=Mark &Highlighted stack frames differently -JavaStackFramesPreferencePage_Filter_synthetic=Mark S&ynthetic methods differently -JavaStackFramesPreferencePage_Filter_platform=Mark &Platform methods differently -JavaStackFramesPreferencePage_Filter_test=Mark Your &Test code differently -JavaStackFramesPreferencePage_Filter_production=Mark Your &Source code differently -JavaStackFramesPreferencePage_Filter_library=Mark &Library code differently +JavaStackFramesPreferencePage_Filter_custom=Mark &highlighted stack frames differently +JavaStackFramesPreferencePage_Filter_synthetic=Mark s&ynthetic methods differently +JavaStackFramesPreferencePage_Filter_platform=Mark &platform methods differently +JavaStackFramesPreferencePage_Filter_test=Mark your &test code differently +JavaStackFramesPreferencePage_Filter_production=Mark your &source code differently +JavaStackFramesPreferencePage_Filter_library=Mark &library code differently JavaStackFramesPreferencePage_Add__Filter=Add &Filter... JavaStackFramesPreferencePage_Add__Package=Add &Packages... From cf83ad513926a5812d3f743096ce8da111d6562d Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Sat, 16 Jul 2022 00:49:57 +0200 Subject: [PATCH 07/15] Show frames as children of GroupedStackFrame --- .../jdt/internal/debug/ui/JDIDebugUIPlugin.java | 1 + .../ui/monitors/JavaThreadContentProvider.java | 13 +++++++++++++ .../debug/ui/monitors/MonitorsAdapterFactory.java | 3 ++- .../debug/core/model/GroupedStackFrame.java | 5 +++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java index 792eedc872..ac2a0b79a2 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java @@ -383,6 +383,7 @@ public void start(BundleContext context) throws Exception { manager.registerAdapters(monitorFactory, JavaOwningThread.class); manager.registerAdapters(monitorFactory, JavaWaitingThread.class); manager.registerAdapters(monitorFactory, IJavaStackFrame.class); + manager.registerAdapters(monitorFactory, GroupedStackFrame.class); IAdapterFactory targetFactory = new TargetAdapterFactory(); manager.registerAdapters(targetFactory, IJavaDebugTarget.class); diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java index 6a33f984a6..b9291962de 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java @@ -49,6 +49,9 @@ public class JavaThreadContentProvider extends JavaElementContentProvider { */ @Override protected int getChildCount(Object element, IPresentationContext context, IViewerUpdate monitor) throws CoreException { + if (element instanceof GroupedStackFrame) { + return ((GroupedStackFrame) element).getFrameCount(); + } IJavaThread thread = (IJavaThread)element; if (!thread.isSuspended()) { return 0; @@ -80,6 +83,9 @@ private int getFrameCount(IJavaThread thread) throws DebugException { */ @Override protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context, IViewerUpdate monitor) throws CoreException { + if (parent instanceof GroupedStackFrame) { + return getChildren((GroupedStackFrame) parent, index, length); + } IJavaThread thread = (IJavaThread)parent; if (!thread.isSuspended()) { return EMPTY; @@ -87,6 +93,10 @@ protected Object[] getChildren(Object parent, int index, int length, IPresentati return getElements(getChildren(thread), index, length); } + private Object[] getChildren(GroupedStackFrame parent, int index, int length) { + return parent.getFramesAsArray(index, length); + } + protected Object[] getChildren(IJavaThread thread) { try { if (thread instanceof JDIThread) { @@ -170,6 +180,9 @@ protected boolean hasChildren(Object element, IPresentationContext context, IVie } } } + if (element instanceof GroupedStackFrame) { + return ((GroupedStackFrame) element).getFrameCount() > 0; + } return ((IJavaThread)element).hasStackFrames() || (isDisplayMonitors() && ((IJavaThread)element).hasOwnedMonitors()); } diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/MonitorsAdapterFactory.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/MonitorsAdapterFactory.java index 81046e2de0..72d977931e 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/MonitorsAdapterFactory.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/MonitorsAdapterFactory.java @@ -17,6 +17,7 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.jdt.debug.core.IJavaStackFrame; import org.eclipse.jdt.debug.core.IJavaThread; +import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame; import org.eclipse.jdt.internal.debug.ui.variables.JavaStackFrameContentProvider; /** @@ -40,7 +41,7 @@ public class MonitorsAdapterFactory implements IAdapterFactory { public T getAdapter(Object adaptableObject, Class adapterType) { if (IElementContentProvider.class.equals(adapterType)) { - if (adaptableObject instanceof IJavaThread) { + if (adaptableObject instanceof IJavaThread || adaptableObject instanceof GroupedStackFrame) { return (T) getThreadPresentation(); } if (adaptableObject instanceof IJavaStackFrame) { diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java index ca24bc8eff..04fe48c4d4 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java @@ -41,6 +41,11 @@ public Object[] getFramesAsArray() { return stackFrames.toArray(); } + public Object[] getFramesAsArray(int index, int length) { + var subList = stackFrames.subList(index, Math.min(index + length, stackFrames.size())); + return subList.isEmpty() ? null : subList.toArray(); + } + public IJavaStackFrame getTopMostFrame() { return !stackFrames.isEmpty() ? stackFrames.get(0) : null; } From a1897d0c0a86408cdec1195de363934dec39033b Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Fri, 12 Aug 2022 00:02:09 +0200 Subject: [PATCH 08/15] Make the default colors a bit darker --- org.eclipse.jdt.debug.ui/plugin.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml index 6ca16c72e5..dc13be1eed 100644 --- a/org.eclipse.jdt.debug.ui/plugin.xml +++ b/org.eclipse.jdt.debug.ui/plugin.xml @@ -3642,7 +3642,7 @@ M4 = Platform-specific fourth key id="org.eclipse.jdt.debug.ui.SyntheticStackFrameFgColor" isEditable="true" label="%SyntheticStackFrameFgColorDefinition.label" - value="202,130,130"> + value="101,65,65"> %SyntheticStackFrameFgColorDefinition.description + value="75,102,78"> %PlatformStackFrameFgColorDefinition.description + value="102,84,126"> %LibraryStackFrameFgColorDefinition.description Date: Sat, 13 Aug 2022 00:49:09 +0200 Subject: [PATCH 09/15] Update preference page in case the colors have been changed in a different page --- .../ui/JavaStackFramePreferencePage.java | 20 ++++++++++++++++++- .../ui/StackFramePresentationProvider.java | 3 +++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java index 37b37d8e82..37677f34cf 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java @@ -28,6 +28,7 @@ import org.eclipse.jface.preference.ColorSelector; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; @@ -53,7 +54,7 @@ * * @since 3.19 */ -public class JavaStackFramePreferencePage extends PreferencePage implements IWorkbenchPreferencePage { +public class JavaStackFramePreferencePage extends PreferencePage implements IWorkbenchPreferencePage, IPropertyChangeListener { private static class CategoryColors { final IJavaStackFrame.Category category; @@ -300,6 +301,7 @@ private void createAppearanceList(Composite container) { } }); + Composite stylesComposite = new Composite(editorComposite, SWT.NONE); layout = new GridLayout(); layout.marginHeight = 0; @@ -329,6 +331,8 @@ private void createAppearanceList(Composite container) { fAppearanceList.update(selection, null); } }); + + PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().addListener(this); } private CategoryColors getSelected() { @@ -438,4 +442,18 @@ protected void performDefaults() { super.performDefaults(); } + @Override + public void propertyChange(PropertyChangeEvent event) { + var propertyName = event.getProperty(); + if (StackFramePresentationProvider.isColorName(propertyName)) { + fAppearanceList.update(colors.toArray(), null); + fAppearanceList.setSelection(new StructuredSelection(colors.get(0))); + } + } + + @Override + public void dispose() { + PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry().removeListener(this); + super.dispose(); + } } diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java index 44cfb559c7..7a8ad5940b 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java @@ -299,4 +299,7 @@ public boolean isCollapseStackFrames() { return collapseStackFrames; } + static boolean isColorName(String propertyName) { + return fgKeyMap.containsValue(propertyName) || bgKeyMap.containsValue(propertyName); + } } From b3276abb8fc4264343f6f6b5de293585c9a48c98 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Sun, 5 Mar 2023 22:43:00 +0100 Subject: [PATCH 10/15] Add an action to highlight the current stack frame for debugging --- org.eclipse.jdt.debug.ui/plugin.properties | 3 + org.eclipse.jdt.debug.ui/plugin.xml | 8 ++ .../ui/JavaStackFramePreferencePage.java | 2 +- .../ui/actions/HighlightStackFrameAction.java | 86 +++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/HighlightStackFrameAction.java diff --git a/org.eclipse.jdt.debug.ui/plugin.properties b/org.eclipse.jdt.debug.ui/plugin.properties index 9fbcda1a7b..f416f95926 100644 --- a/org.eclipse.jdt.debug.ui/plugin.properties +++ b/org.eclipse.jdt.debug.ui/plugin.properties @@ -186,6 +186,9 @@ colorizeStackFrames.tooltip=Based on the function mark the stack frames with dif collapseStackFrames.label=Collapse Stack Frames collapseStackFrames.tooltip=Hide less relevant stack frames +highlightStackFrame.label=Highlight Stack Frame +highlightStackFrame.tooltip=Always mark this stack frame distinctly + stepIntoSelectionHyperlinkDetector.label=Step Into Selection stepIntoSelectionHyperlinkDetector.description=Performs the step into selection command on demand via a hyperlink diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml index dc13be1eed..a359e91543 100644 --- a/org.eclipse.jdt.debug.ui/plugin.xml +++ b/org.eclipse.jdt.debug.ui/plugin.xml @@ -1699,6 +1699,14 @@ enablesFor="+" id="org.eclipse.jdt.debug.ui.actions.EditStepFiltersAction"> + + Date: Sun, 5 Mar 2023 23:21:44 +0100 Subject: [PATCH 11/15] Do not group the custom-filtered selection --- .../internal/debug/ui/monitors/JavaThreadContentProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java index b9291962de..ab5247da51 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java @@ -149,7 +149,7 @@ private List getStackFrames(IJavaThread thread) throws DebugException { if (frame instanceof JDIStackFrame) { var javaFrame = (JDIStackFrame) frame; var category = stackFrameProvider.getCategory(javaFrame); - if (category == null || category == Category.TEST || category == Category.PRODUCTION) { + if (category == null || category == Category.TEST || category == Category.PRODUCTION || category == Category.CUSTOM_FILTERED) { result.add(javaFrame); lastGroupping = null; } else { From 0b7311884461a9194392ed1348786291d618ae0f Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Sat, 26 Oct 2024 12:13:47 +0200 Subject: [PATCH 12/15] StackFramePresentationProvider should not be AutoClosable it just triggers a bunch of warning --- .../jdt/internal/debug/ui/StackFramePresentationProvider.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java index 7a8ad5940b..8277e23aa3 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java @@ -37,7 +37,7 @@ * preference storage. * */ -public class StackFramePresentationProvider implements IPropertyChangeListener, AutoCloseable { +public final class StackFramePresentationProvider implements IPropertyChangeListener { private final static EnumMap fgKeyMap = new EnumMap<>(Map.of( Category.CUSTOM_FILTERED, IJDIPreferencesConstants.PREF_CUSTOM_FILTERED_STACK_FRAME_FG_COLOR, @@ -267,7 +267,6 @@ private Category categorizeSourceElement(IJavaStackFrame frame) { return Category.UNKNOWN; } - @Override public void close() { store.removePropertyChangeListener(this); } From 873702d297e816090fd73b2314bb1444b26f2dd6 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Tue, 10 Dec 2024 19:16:24 +0200 Subject: [PATCH 13/15] Fix version numbers to make it build --- org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF | 2 +- org.eclipse.jdt.debug.tests/pom.xml | 2 +- org.eclipse.jdt.debug/META-INF/MANIFEST.MF | 2 +- .../model/org/eclipse/jdt/debug/core/IJavaStackFrame.java | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF index 116932fe89..9c5ad6443e 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.600.qualifier +Bundle-Version: 3.12.700.qualifier Bundle-ClassPath: javadebugtests.jar Bundle-Activator: org.eclipse.jdt.debug.testplugin.JavaTestPlugin Bundle-Vendor: %providerName diff --git a/org.eclipse.jdt.debug.tests/pom.xml b/org.eclipse.jdt.debug.tests/pom.xml index b23765e6c3..721c143c8d 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.600-SNAPSHOT + 3.12.700-SNAPSHOT eclipse-test-plugin ${project.artifactId} diff --git a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF index 84d4ebd713..c7e05af5e1 100644 --- a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.debug; singleton:=true -Bundle-Version: 3.21.700.qualifier +Bundle-Version: 3.22.0.qualifier Bundle-ClassPath: jdimodel.jar Bundle-Activator: org.eclipse.jdt.internal.debug.core.JDIDebugPlugin Bundle-Vendor: %providerName diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java index d8258fc228..1ba486726e 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaStackFrame.java @@ -38,7 +38,7 @@ public interface IJavaStackFrame extends IStackFrame, IJavaModifiers, /** * Categorization of the stack frame. * - * @since 3.21 + * @since 3.22 * */ enum Category { @@ -545,7 +545,7 @@ public IJavaVariable findVariable(String variableName) public void forceReturn(IJavaValue value) throws DebugException; /** - * @since 3.21 + * @since 3.22 * @param category * the new category of the stack frame. */ @@ -553,7 +553,7 @@ public IJavaVariable findVariable(String variableName) /** * - * @since 3.21 + * @since 3.22 * @return the category of the stack frame, null if it's not yet categorized. */ public Category getCategory(); From 86af524b3f6b478a9b08cee2c39999bfd1704485 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Tue, 10 Dec 2024 20:45:55 +0200 Subject: [PATCH 14/15] Couple of code cleanups around the GroupedStackFrames and other classes --- .../jdt/internal/debug/ui/JDIDebugUIPlugin.java | 6 +++--- .../ui/JDIDebugUIPreferenceInitializer.java | 2 +- .../internal/debug/ui/JDIModelPresentation.java | 3 +-- .../ui/StackFramePresentationProvider.java | 3 +-- .../ui/actions/HighlightStackFrameAction.java | 7 +++---- .../ui/monitors/JavaThreadContentProvider.java | 17 ++++++++--------- .../JavaDebugShowInAdapterFactory.java | 4 ++-- .../debug/core/model/GroupedStackFrame.java | 4 ---- 8 files changed, 19 insertions(+), 27 deletions(-) diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java index ac2a0b79a2..1ee5661032 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2022 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 @@ -383,7 +383,7 @@ public void start(BundleContext context) throws Exception { manager.registerAdapters(monitorFactory, JavaOwningThread.class); manager.registerAdapters(monitorFactory, JavaWaitingThread.class); manager.registerAdapters(monitorFactory, IJavaStackFrame.class); - manager.registerAdapters(monitorFactory, GroupedStackFrame.class); + manager.registerAdapters(monitorFactory, GroupedStackFrame.class); IAdapterFactory targetFactory = new TargetAdapterFactory(); manager.registerAdapters(targetFactory, IJavaDebugTarget.class); @@ -393,7 +393,7 @@ public void start(BundleContext context) throws Exception { IAdapterFactory showInFactory = new JavaDebugShowInAdapterFactory(); manager.registerAdapters(showInFactory, IJavaStackFrame.class); - manager.registerAdapters(showInFactory, GroupedStackFrame.class); + manager.registerAdapters(showInFactory, GroupedStackFrame.class); IAdapterFactory columnFactory = new ColumnPresentationAdapterFactory(); manager.registerAdapters(columnFactory, IJavaVariable.class); diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java index f400e017a2..2d379b99ec 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2020 IBM Corporation and others. + * Copyright (c) 2004, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java index c38e099215..5eac44b0fa 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java @@ -249,8 +249,7 @@ public String getText(Object item) { return getJavaOwningTreadText((JavaOwningThread)item); } else if (item instanceof JavaWaitingThread) { return getJavaWaitingTreadText((JavaWaitingThread)item); - } else if (item instanceof GroupedStackFrame) { - var groupping = (GroupedStackFrame) item; + } else if (item instanceof GroupedStackFrame groupping) { return getFormattedString(DebugUIMessages.JDIModelPresentation_collapsed_frames, String.valueOf(groupping.getFrameCount())); } else if (item instanceof NoMonitorInformationElement) { return DebugUIMessages.JDIModelPresentation_5; diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java index 8277e23aa3..33a5a2d459 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/StackFramePresentationProvider.java @@ -249,9 +249,8 @@ private Category categorizeSourceElement(IJavaStackFrame frame) { if (source == null) { return Category.UNKNOWN; } - if (source instanceof IFile) { + if (source instanceof IFile file) { if (isEnabled(Category.TEST)) { - var file = (IFile) source; var jproj = JavaCore.create(file.getProject()); var cp = jproj.findContainingClasspathEntry(file); if (cp != null && cp.isTest()) { diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/HighlightStackFrameAction.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/HighlightStackFrameAction.java index 97b67ae402..ebd4e2814a 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/HighlightStackFrameAction.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/HighlightStackFrameAction.java @@ -42,16 +42,15 @@ public void run(IAction action) { final var preferenceStore = JDIDebugUIPlugin.getDefault().getPreferenceStore(); final var filterManager = JavaStackFramePreferencePage.CUSTOM_STACK_FRAMES; for (Object selected : selection) { - if (selected instanceof IJavaStackFrame) { - IJavaStackFrame frame = (IJavaStackFrame) selected; + if (selected instanceof IJavaStackFrame frame) { try { addFilter(filterManager, preferenceStore, frame.getDeclaringTypeName()); // Reset all the stack frames, to be evaluated on the next step again. var thread = frame.getThread(); var frames = thread.getStackFrames(); for (var oneFrame : frames) { - if (oneFrame instanceof IJavaStackFrame) { - ((IJavaStackFrame) oneFrame).setCategory(null); + if (oneFrame instanceof IJavaStackFrame currentFrame) { + currentFrame.setCategory(null); } } } catch (DebugException e) { diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java index ab5247da51..470ac94a89 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/monitors/JavaThreadContentProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2012 IBM Corporation and others. + * Copyright (c) 2006, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -49,8 +49,8 @@ public class JavaThreadContentProvider extends JavaElementContentProvider { */ @Override protected int getChildCount(Object element, IPresentationContext context, IViewerUpdate monitor) throws CoreException { - if (element instanceof GroupedStackFrame) { - return ((GroupedStackFrame) element).getFrameCount(); + if (element instanceof GroupedStackFrame groupedFrame) { + return groupedFrame.getFrameCount(); } IJavaThread thread = (IJavaThread)element; if (!thread.isSuspended()) { @@ -83,8 +83,8 @@ private int getFrameCount(IJavaThread thread) throws DebugException { */ @Override protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context, IViewerUpdate monitor) throws CoreException { - if (parent instanceof GroupedStackFrame) { - return getChildren((GroupedStackFrame) parent, index, length); + if (parent instanceof GroupedStackFrame groupedFrame) { + return getChildren(groupedFrame, index, length); } IJavaThread thread = (IJavaThread)parent; if (!thread.isSuspended()) { @@ -146,8 +146,7 @@ private List getStackFrames(IJavaThread thread) throws DebugException { result.add(frame); first = false; } else { - if (frame instanceof JDIStackFrame) { - var javaFrame = (JDIStackFrame) frame; + if (frame instanceof JDIStackFrame javaFrame) { var category = stackFrameProvider.getCategory(javaFrame); if (category == null || category == Category.TEST || category == Category.PRODUCTION || category == Category.CUSTOM_FILTERED) { result.add(javaFrame); @@ -180,8 +179,8 @@ protected boolean hasChildren(Object element, IPresentationContext context, IVie } } } - if (element instanceof GroupedStackFrame) { - return ((GroupedStackFrame) element).getFrameCount() > 0; + if (element instanceof GroupedStackFrame groupedFrame) { + return groupedFrame.getFrameCount() > 0; } return ((IJavaThread)element).hasStackFrames() || (isDisplayMonitors() && ((IJavaThread)element).hasOwnedMonitors()); 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 69c5fe7738..4c7f3855e3 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 @@ -44,9 +44,9 @@ public T getAdapter(Object adaptableObject, Class adapterType) { } } if (adapterType == ISourceDisplay.class) { - if (adaptableObject instanceof GroupedStackFrame) { + if (adaptableObject instanceof GroupedStackFrame groupedFrames) { return (T) (ISourceDisplay) (element, page, forceSourceLookup) -> { - var frame = ((GroupedStackFrame) element).getTopMostFrame(); + var frame = groupedFrames.getTopMostFrame(); SourceLookupFacility.getDefault().displaySource(frame, page, forceSourceLookup); }; } diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java index 04fe48c4d4..bb80f93bff 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/GroupedStackFrame.java @@ -37,10 +37,6 @@ public int getFrameCount() { return stackFrames.size(); } - public Object[] getFramesAsArray() { - return stackFrames.toArray(); - } - public Object[] getFramesAsArray(int index, int length) { var subList = stackFrames.subList(index, Math.min(index + length, stackFrames.size())); return subList.isEmpty() ? null : subList.toArray(); From cf644131aa7f8d9844965379f544a8404a1e2b52 Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Thu, 12 Dec 2024 00:44:06 +0100 Subject: [PATCH 15/15] Add dark style --- .../css/e4-dark_jdi_debug_prefstyle.css | 31 +++++++++++++++++++ org.eclipse.jdt.debug.ui/plugin.xml | 5 +++ 2 files changed, 36 insertions(+) create mode 100644 org.eclipse.jdt.debug.ui/css/e4-dark_jdi_debug_prefstyle.css diff --git a/org.eclipse.jdt.debug.ui/css/e4-dark_jdi_debug_prefstyle.css b/org.eclipse.jdt.debug.ui/css/e4-dark_jdi_debug_prefstyle.css new file mode 100644 index 0000000000..73dd2ceaa2 --- /dev/null +++ b/org.eclipse.jdt.debug.ui/css/e4-dark_jdi_debug_prefstyle.css @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2024 Zsombor Gegesy and others. + * + * 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: + * Zsombor Gegesy - initial API and implementation + *******************************************************************************/ + +/* ############################## Debug preferences ############################## */ + +IEclipsePreferences#org-eclipse-ui-workbench:org-eclipse-jdt-debug-ui { + preferences: + 'org.eclipse.jdt.debug.ui.CustomFilteredStackFrameFgColor=252,226,186' + 'org.eclipse.jdt.debug.ui.CustomFilteredStackFrameBgColor=53,53,53' + 'org.eclipse.jdt.debug.ui.ProductionStackFrameFgColor=238,238,255' + 'org.eclipse.jdt.debug.ui.ProductionStackFrameBgColor=53,53,53' + 'org.eclipse.jdt.debug.ui.TestStackFrameFgColor=238,255,238' + 'org.eclipse.jdt.debug.ui.TestStackFrameBgColor=53,53,53' + 'org.eclipse.jdt.debug.ui.SyntheticStackFrameFgColor=101,101,65' + 'org.eclipse.jdt.debug.ui.SyntheticStackFrameBgColor=53,53,53' + 'org.eclipse.jdt.debug.ui.LibraryStackFrameFgColor=182,164,206' + 'org.eclipse.jdt.debug.ui.LibraryStackFrameBgColor=53,53,53' + 'org.eclipse.jdt.debug.ui.PlatformStackFrameFgColor=108,132,108' + 'org.eclipse.jdt.debug.ui.PlatformStackFrameBgColor=53,53,53' +} diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml index a359e91543..0b957acd1b 100644 --- a/org.eclipse.jdt.debug.ui/plugin.xml +++ b/org.eclipse.jdt.debug.ui/plugin.xml @@ -4024,4 +4024,9 @@ M4 = Platform-specific fourth key + + + + +