diff --git a/org.eclipse.jdt.debug.ui/plugin.properties b/org.eclipse.jdt.debug.ui/plugin.properties index 4e7fca4bc4..4c06ccb6cc 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. diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml index 4643154b7b..4c0ea6e95f 100644 --- a/org.eclipse.jdt.debug.ui/plugin.xml +++ b/org.eclipse.jdt.debug.ui/plugin.xml @@ -2571,6 +2571,13 @@ id="org.eclipse.jdt.debug.ui.JavaDebugPreferencePage"> + + + JavaPrimitivesPreferencePage_0=Primitive Display Options JavaPrimitivesPreferencePage_1=Display options for primitive values +JavaStackFramesPreferencePage_title=Stack Frame Preferences +JavaStackFramesPreferencePage_description=Displaying of the Stack Frames set accordingly to these filters when 'Mark Stack Frames differently' toggle is activated. +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 highlighted stack frames: +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/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/JavaStackFramePreferencePage.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java new file mode 100644 index 0000000000..0c624457c7 --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaStackFramePreferencePage.java @@ -0,0 +1,291 @@ +/******************************************************************************* + * Copyright (c) 2022, 2025 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.Category; +import org.eclipse.jdt.internal.ui.filtertable.Filter; +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.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +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.15 + */ +public class JavaStackFramePreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + private static class ButtonWrapper { + private final Button button; + protected final A key; + + ButtonWrapper(Composite container, String label, A key, boolean checked, Consumer stateChangeConsumer) { + this.button = SWTFactory.createCheckButton(container, label, null, checked, 2); + this.key = key; + if (stateChangeConsumer != null) { + widgetSelected(stateChangeConsumer); + } + } + + void setChecked(boolean checked) { + button.setSelection(checked); + } + + boolean isChecked() { + return button.getSelection(); + } + + void setEnabled(boolean enabled) { + this.button.setEnabled(enabled); + } + + ButtonWrapper widgetSelected(Consumer consumer) { + this.button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + consumer.accept(button.getSelection()); + } + }); + return this; + } + + } + + private static class PreferenceButton extends ButtonWrapper { + + PreferenceButton(Composite container, String label, IPreferenceStore store, String flag, Consumer stateChangeConsumer) { + super(container, label, flag, store.getBoolean(flag), stateChangeConsumer); + } + + boolean performDefault(IPreferenceStore store) { + var defValue = store.getBoolean(key); + setChecked(defValue); + return defValue; + } + + void performOk(IPreferenceStore store) { + store.setValue(key, isChecked()); + } + } + + private static class CategoryButton extends ButtonWrapper { + + CategoryButton(Composite container, String label, StackFrameCategorizer categorizer, Category category, Consumer stateChangeConsumer) { + super(container, label, category, categorizer.isEnabled(category), stateChangeConsumer); + } + + boolean performDefault(StackFrameCategorizer store) { + var defValue = store.isEnabled(key); + setChecked(defValue); + return defValue; + } + + void performOk(StackFrameCategorizer store) { + store.setEnabled(key, isChecked()); + } + + } + public static final String PAGE_ID = "org.eclipse.jdt.debug.ui.JavaStackFramePreferencePage"; //$NON-NLS-1$ + + //widgets + private PreferenceButton fCollapseStackFrames; + private List categoryButtons; + private CategoryButton fEnablePlatformButton; + private CategoryButton fEnableCustomButton; + private JavaFilterTable fPlatformStackFilterTable; + private JavaFilterTable fCustomStackFilterTable; + private StackFrameCategorizer categorizer; + + public JavaStackFramePreferencePage() { + super(); + setPreferenceStore(JDIDebugUIPlugin.getDefault().getPreferenceStore()); + setTitle(DebugUIMessages.JavaStackFramesPreferencePage_title); + setDescription(DebugUIMessages.JavaStackFramesPreferencePage_description); + this.categorizer = JDIDebugUIPlugin.getDefault().getStackFrameCategorizer(); + categoryButtons = new ArrayList<>(); + } + + @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; + } + + @Override + public void init(IWorkbench workbench) {} + + /** + * Create a group to contain the step filter related widgets + */ + private void createStepFilterPreferences(Composite parent) { + var store = getPreferenceStore(); + Composite container = SWTFactory.createComposite(parent, parent.getFont(), 2, 1, GridData.FILL_BOTH, 0, 0); + + fCollapseStackFrames = new PreferenceButton(container, DebugUIMessages.JavaStackFramesPreferencePage__Collapse_stack_frames, store, IJDIPreferencesConstants.PREF_COLLAPSE_STACK_FRAMES, this::updateCheckboxes); + initializeDialogUnits(container); + + this.fEnableCustomButton = new CategoryButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_custom, categorizer, StackFrameCategorizer.CATEGORY_CUSTOM_FILTERED, // + selected -> fCustomStackFilterTable.setEnabled(selected && isCategoryHandlingEnabled())); + categoryButtons.add(this.fEnableCustomButton); + + fCustomStackFilterTable = new JavaFilterTable(new JavaFilterTable.FilterStorage() { + + @Override + public Filter[] getStoredFilters(boolean defaults) { + return combineFilterLists(categorizer.getActiveCustomStackFilter(), categorizer.getInactiveCustomStackFilter()); + } + + @Override + public void setStoredFilters(IPreferenceStore store, Filter[] filters) { + categorizer.setCustomFilters(filters); + } + + }, + 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 CategoryButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_synthetic, categorizer, StackFrameCategorizer.CATEGORY_SYNTHETIC, null)); + + this.fEnablePlatformButton = new CategoryButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_platform, categorizer, StackFrameCategorizer.CATEGORY_PLATFORM, // + selected -> fPlatformStackFilterTable.setEnabled(selected && isCategoryHandlingEnabled())); + categoryButtons.add(fEnablePlatformButton); + + fPlatformStackFilterTable = new JavaFilterTable(new JavaFilterTable.FilterStorage() { + @Override + public Filter[] getStoredFilters(boolean defaults) { + return combineFilterLists(categorizer.getActivePlatformStackFilter(), categorizer.getInactivePlatformStackFilter()); + } + + @Override + public void setStoredFilters(IPreferenceStore store, Filter[] filters) { + categorizer.setPlatformFilters(filters); + } + + }, + 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 CategoryButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_test, categorizer, StackFrameCategorizer.CATEGORY_TEST, null)); + categoryButtons.add(new CategoryButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_production, categorizer, StackFrameCategorizer.CATEGORY_PRODUCTION, null)); + categoryButtons.add(new CategoryButton(container, DebugUIMessages.JavaStackFramesPreferencePage_Filter_library, categorizer, StackFrameCategorizer.CATEGORY_LIBRARY, null)); + + setPageEnablement(isCategoryHandlingEnabled()); + } + + private static Filter[] combineFilterLists(String[] activefilters, String[] inactivefilters) { + Filter[] filters = new Filter[activefilters.length + inactivefilters.length]; + for (int i = 0; i < activefilters.length; i++) { + filters[i] = new Filter(activefilters[i], true); + } + for (int i = 0; i < inactivefilters.length; i++) { + filters[i + activefilters.length] = new Filter(inactivefilters[i], false); + } + return filters; + } + + protected void updateCheckboxes(@SuppressWarnings("unused") boolean flag) { + setPageEnablement(isCategoryHandlingEnabled()); + } + + private boolean isCategoryHandlingEnabled() { + return fCollapseStackFrames.isChecked(); + } + + /** + * Enables or disables the widgets on the page according to the passed boolean + * + * @param enabled + * the new enablement status of the page's widgets + */ + protected void setPageEnablement(boolean enabled) { + fPlatformStackFilterTable.setEnabled(enabled && fEnablePlatformButton.isChecked()); + fCustomStackFilterTable.setEnabled(enabled && fEnableCustomButton.isChecked()); + for (var categoryButton : categoryButtons) { + categoryButton.setEnabled(enabled); + } + } + + @Override + public boolean performOk() { + IPreferenceStore store = getPreferenceStore(); + fCollapseStackFrames.performOk(store); + fPlatformStackFilterTable.performOk(store); + fCustomStackFilterTable.performOk(store); + for (var categoryButton : categoryButtons) { + categoryButton.performOk(categorizer); + } + return super.performOk(); + } + + @Override + protected void performDefaults() { + var store = getPreferenceStore(); + boolean enabled = fCollapseStackFrames.performDefault(store); + + for (var categoryButton : categoryButtons) { + categoryButton.performDefault(categorizer); + } + + setPageEnablement(enabled); + + fPlatformStackFilterTable.performDefaults(); + fCustomStackFilterTable.performDefaults(); + super.performDefaults(); + } + + +}