diff --git a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/JdtTextTestSuite.java b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/JdtTextTestSuite.java index 96bb66735d3..ae6b859828e 100644 --- a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/JdtTextTestSuite.java +++ b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/JdtTextTestSuite.java @@ -20,6 +20,7 @@ import org.eclipse.jdt.text.tests.codemining.CodeMiningTriggerTest; import org.eclipse.jdt.text.tests.codemining.ParameterNamesCodeMiningTest; import org.eclipse.jdt.text.tests.contentassist.ContentAssistTestSuite; +import org.eclipse.jdt.text.tests.folding.FoldingTest; import org.eclipse.jdt.text.tests.semantictokens.SemanticTokensProviderTest; import org.eclipse.jdt.text.tests.spelling.SpellCheckEngineTestCase; import org.eclipse.jdt.text.tests.templates.TemplatesTestSuite; @@ -70,6 +71,7 @@ JavaElementPrefixPatternMatcherTest.class, CodeMiningTriggerTest.class, ParameterNamesCodeMiningTest.class, + FoldingTest.class, }) public class JdtTextTestSuite { } \ No newline at end of file diff --git a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/CustomFoldingRegionTest.java b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/CustomFoldingRegionTest.java new file mode 100644 index 00000000000..47ee3be25f4 --- /dev/null +++ b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/CustomFoldingRegionTest.java @@ -0,0 +1,443 @@ +/******************************************************************************* + * Copyright (c) 2025 Daniel Schmid 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: + * Daniel Schmid - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.text.tests.folding; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.eclipse.jdt.testplugin.JavaProjectHelper; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.preference.IPreferenceStore; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.projection.ProjectionAnnotation; +import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; + +import org.eclipse.ui.PartInitException; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaModelException; + +import org.eclipse.jdt.ui.PreferenceConstants; +import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup; + +import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; +import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; + +public class CustomFoldingRegionTest { + @Rule + public ProjectTestSetup projectSetup= new ProjectTestSetup(); + + private IJavaProject fJProject1; + + private IPackageFragmentRoot fSourceFolder; + + private IPackageFragment fPackageFragment; + + @Before + public void setUp() throws CoreException { + fJProject1= projectSetup.getProject(); + fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + fPackageFragment= fSourceFolder.createPackageFragment("org.example.test", false, null); + } + + + @After + public void tearDown() throws CoreException { + JavaProjectHelper.delete(fJProject1); + IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); + store.setToDefault(PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_START); + store.setToDefault(PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_END); + } + + @Test + public void testNoCustomFoldingRegions() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + public class Test { + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(0, projectionRanges.size()); + } + + @Test + public void testCustomFoldingRegionInsideAndOutsideClass() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + // #region + // something else + // #endregion + public class Test { + // #region + // something else + // #endregion + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(2, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 1, 3); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 5, 7); + } + + @Test + public void testNestedCustomRegions() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + public class Test { + // #region outer + // #region inner + + // #endregion outer + // #endregion inner + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(2, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 3, 7);//outer + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 4, 6);//inner + } + + @Test + public void testNoCustomFoldingRegionsInMethod() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + public class Test { + void a(){ + + } + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(1, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 2, 4); + } + + @Test + public void testCustomFoldingRegionsInMethod() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + public class Test { + void a(){ + // #region + + // #endregion + } + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(2, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 2, 6); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 3, 5); + } + + @Test + public void testNoCustomFoldingRegionsSingleImport() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + import java.util.List; + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(0, projectionRanges.size()); + } + + @Test + public void testCustomFoldingRegionAroundSingleImport() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + // #region imports + import java.util.List; + // #endregion + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(1, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 2, 4); + } + + @Test + public void testCustomFoldingRegionAroundClasses() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + class A { + + } + + // #region + + class B { + + } + + class C { + + } + // #endregion + + class D { + + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(1, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 6, 15); + } + + @Test + public void testCustomFoldingRegionsMultipleLevels() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + // #region outside class + public class Test { + // #endregion should be ignored + // #region outside method + void a(){ + // #endregion should be ignored + // #region inside method + System.out.println("Hello World"); + // #endregion inside method + } + // #endregion outside method + } + // #endregion outside class + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(4, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 1, 13);//outside class + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 4, 11);//outside method + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 5, 10);//void a() + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 7, 9);//inside method + } + + @Test + public void testCustomFoldingRegionsNotEndingTooEarly() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + public class Test { + void a(){ + // #region inside method + } + // #endregion outside method + } + // #endregion outside class + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(1, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 3, 5);//void a() + } + + @Test + public void testCustomFoldingRegionsUsingSpecialCommentTypes() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + public class Test { + void a(){ + /* #region multiline + */ + /** #region javadoc */ + /** #endregion javadoc */ + /* #endregion multiline + */ + } + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(3, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 3, 10);//void a() + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 4, 8);// multiline + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 6, 7);// javadoc + } + + @Test + public void testCustomRegionsWithLocalClass() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + public class Test { + void a(){ + // #region + int i; + + // #endregion + class Inner{ + + } + } + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(3, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 3, 11);//void a() + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 4, 7);//region + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 8, 10);//class Inner + } + + @Test + public void testNoCustomRegionAtDifferentLevelsWithOtherClass() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + public class Test{ + // #region outside + public class A { + public void helloWorld() { + } + // #endregion inside + } + + public class B { + } + + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(3, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 4, 8);//class A + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 5, 6);//void helloWorld() + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 10, 11);//class B + } + + @Test + public void testCustomRegionsAroundFieldAndMethod() throws JavaModelException, PartInitException { + String str= """ + package org.example.test; + + public class Test { + // #region + int a; + + void b(){ + + } + // #endregion + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(2, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 3, 9);//region + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 6, 8);//void b() + } + + @Test + public void testDifferentConfiguration() throws PartInitException, JavaModelException { + IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); + try { + store.setValue(PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_START, "#regstart"); + store.setValue(PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_END, "#regend"); + + + String str= """ + package org.example.test; + public class Test { + // #region should be ignored + // #regstart this is the region + // #regend should end here + // #endregion should be ignored + } + """; + List projectionRanges= getProjectionRangesOfFile(str); + assertEquals(1, projectionRanges.size()); + assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 3, 4); + } finally { + + } + } + + private void assertContainsRegionUsingStartAndEndLine(List projectionRanges, String input, int startLine, int endLine) { + assertTrue(startLine <= endLine, "start line must be smaller or equal to end line"); + int startLineBegin= findLineStartIndex(input, startLine); + + int endLineBegin= findLineStartIndex(input, endLine); + int endLineEnd= findNextLineStart(input, endLineBegin); + endLineEnd= getLengthIfNotFound(input, endLineEnd); + + for (IRegion region : projectionRanges) { + if (region.getOffset() == startLineBegin + 1 && region.getOffset() + region.getLength() == endLineEnd + 1) { + return; + } + } + + fail( + "missing region from line " + startLine + "(index " + (startLineBegin + 1) + ") " + + "to line " + endLine + "(index " + (endLineEnd + 1) + ")" + + ", actual regions: " + projectionRanges + ); + } + + + private int getLengthIfNotFound(String input, int startLineEnd) { + if (startLineEnd == -1) { + startLineEnd= input.length(); + } + return startLineEnd; + } + + + private int findLineStartIndex(String input, int lineNumber) { + int currentInputIndex= 0; + for (int i= 0; i < lineNumber; i++) { + currentInputIndex= findNextLineStart(input, currentInputIndex); + if (currentInputIndex == -1) { + fail("line number is greater than the total number of lines"); + } + } + return currentInputIndex; + } + + + private int findNextLineStart(String input, int currentInputIndex) { + return input.indexOf('\n', currentInputIndex + 1); + } + + private List getProjectionRangesOfFile(String str) throws JavaModelException, PartInitException { + + ICompilationUnit compilationUnit= fPackageFragment.createCompilationUnit("Test.java", str, false, null); + JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(compilationUnit); + ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class); + List regions= new ArrayList<>(); + for (Iterator it= model.getAnnotationIterator(); it.hasNext();) { + Annotation annotation= it.next(); + if (annotation instanceof ProjectionAnnotation projectionAnnotation) { + Position position= model.getPosition(projectionAnnotation); + regions.add(new Region(position.getOffset(), position.getLength())); + } + } + return regions; + } + +} diff --git a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTest.java b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTest.java new file mode 100644 index 00000000000..8844aad8e96 --- /dev/null +++ b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTest.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2025 Daniel Schmid 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: + * Daniel Schmid - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.text.tests.folding; + +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; + +@Suite +@SelectClasses({ + CustomFoldingRegionTest.class +}) +public class FoldingTest { +} diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml index 5c21a366586..07ebbca5751 100644 --- a/org.eclipse.jdt.ui/plugin.xml +++ b/org.eclipse.jdt.ui/plugin.xml @@ -1055,6 +1055,17 @@ + + + + + + + fProviderPreferences; private final Map fProviderControls; + private IScopeContext fContext; - public FoldingConfigurationBlock(OverlayPreferenceStore store) { + private boolean fIsProjectPreferencePage; + + + public FoldingConfigurationBlock(OverlayPreferenceStore store, IScopeContext context, boolean isProjectPreferencePage) { Assert.isNotNull(store); fStore= store; fStore.addKeys(createOverlayStoreKeys()); fProviderDescriptors= createListModel(); fProviderPreferences= new HashMap<>(); fProviderControls= new HashMap<>(); + this.fContext= context; + this.fIsProjectPreferencePage= isProjectPreferencePage; } private Map createListModel() { @@ -289,6 +298,14 @@ void updateListDependencies() { if (prefs == null) { try { prefs= desc.createPreferences(); + if (fIsProjectPreferencePage) { + if (prefs instanceof IScopedJavaFoldingPreferenceBlock scopedPrefs) { + scopedPrefs.setScopeContext(fContext); + } else { + String message= Messages.format(PreferencesMessages.FoldingConfigurationBlock_error_project_prefs_not_supported, desc.getName()); + prefs= new ErrorPreferences(message); + } + } fProviderPreferences.put(id, prefs); } catch (CoreException e) { JavaPlugin.log(e); @@ -369,4 +386,19 @@ private void restoreFromPreferences() { else fProviderViewer.setSelection(new StructuredSelection(provider), true); } + + @Override + public void disableProjectSettings() { + if(fContext != null) { + fContext.getNode(JavaUI.ID_PLUGIN).remove(PreferenceConstants.EDITOR_FOLDING_ENABLED); + } + } + + @Override + public void enableProjectSettings() { + if(fContext != null) { + fContext.getNode(JavaUI.ID_PLUGIN).putBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED, fStore.getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED)); + } + + } } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/FoldingPreferencePage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/FoldingPreferencePage.java index ed17e5a27c9..6b25f623a68 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/FoldingPreferencePage.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/FoldingPreferencePage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -15,8 +15,14 @@ package org.eclipse.jdt.internal.ui.preferences; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; +import org.eclipse.core.runtime.preferences.IScopeContext; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ProjectScope; + +import org.eclipse.ui.preferences.ScopedPreferenceStore; + +import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jdt.internal.ui.IJavaHelpContextIds; import org.eclipse.jdt.internal.ui.JavaPlugin; @@ -24,44 +30,70 @@ /** * The page for setting the editor options. */ -public final class FoldingPreferencePage extends AbstractConfigurationBlockPreferencePage { +public final class FoldingPreferencePage extends AbstractConfigurationBlockPreferenceAndPropertyPage { + + public static final String PROPERTY_PAGE_ID= "org.eclipse.jdt.ui.propertyPages.FoldingPreferencePage"; //$NON-NLS-1$ + public static final String PREFERENCE_PAGE_ID= "org.eclipse.jdt.ui.preferences.FoldingPreferencePage"; //$NON-NLS-1$ + private OverlayPreferenceStore fOverlayStore; + + + public FoldingPreferencePage() { + setDescription(PreferencesMessages.JavaEditorPreferencePage_folding_title); + } /* - * @see org.eclipse.ui.internal.editors.text.AbstractConfigureationBlockPreferencePage#getHelpId() + * @see org.eclipse.jdt.internal.ui.preferences.AbstractConfigurationBlockPreferenceAndPropertyPage#getHelpId() */ @Override protected String getHelpId() { return IJavaHelpContextIds.JAVA_EDITOR_PREFERENCE_PAGE; } - /* - * @see org.eclipse.ui.internal.editors.text.AbstractConfigurationBlockPreferencePage#setDescription() - */ @Override - protected void setDescription() { - String description= PreferencesMessages.JavaEditorPreferencePage_folding_title; - setDescription(description); + protected IPreferenceAndPropertyConfigurationBlock createConfigurationBlock(IScopeContext context) { + ScopedPreferenceStore scopedStore= new ScopedPreferenceStore(context, JavaUI.ID_PLUGIN); + fOverlayStore= new OverlayPreferenceStore( + scopedStore, + new OverlayPreferenceStore.OverlayKey[] {}); + FoldingConfigurationBlock foldingConfigurationBlock= new FoldingConfigurationBlock(fOverlayStore, context, isProjectPreferencePage()); + fOverlayStore.load(); + fOverlayStore.start(); + return foldingConfigurationBlock; } - /* - * @see org.org.eclipse.ui.internal.editors.text.AbstractConfigurationBlockPreferencePage#setPreferenceStore() - */ @Override - protected void setPreferenceStore() { - setPreferenceStore(JavaPlugin.getDefault().getPreferenceStore()); + protected boolean hasProjectSpecificOptions(IProject project) { + return JavaPlugin.getDefault().getFoldingStructureProviderRegistry().hasProjectSpecificOptions(new ProjectScope(project)); } + @Override + protected String getPreferencePageID() { + return PREFERENCE_PAGE_ID; + } @Override - protected Label createDescriptionLabel(Composite parent) { - return null; // no description for new look. + protected String getPropertyPageID() { + return PROPERTY_PAGE_ID; } - /* - * @see org.eclipse.ui.internal.editors.text.AbstractConfigureationBlockPreferencePage#createConfigurationBlock(org.eclipse.ui.internal.editors.text.OverlayPreferenceStore) - */ @Override - protected IPreferenceConfigurationBlock createConfigurationBlock(OverlayPreferenceStore overlayPreferenceStore) { - return new FoldingConfigurationBlock(overlayPreferenceStore); + public boolean performOk() { + boolean result= super.performOk(); + fOverlayStore.propagate(); + return result; } + + @Override + public void performDefaults() { + fOverlayStore.loadDefaults(); + super.performDefaults(); + + } + + @Override + public void dispose() { + super.dispose(); + fOverlayStore.stop(); + } + } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.java index a1886d94f9c..1f26dc5e7d4 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2024 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -773,6 +773,7 @@ private PreferencesMessages() { public static String FoldingConfigurationBlock_combo_caption; public static String FoldingConfigurationBlock_info_no_preferences; public static String FoldingConfigurationBlock_error_not_exist; + public static String FoldingConfigurationBlock_error_project_prefs_not_supported; public static String FoldingConfigurationBlock_warning_providerNotFound_resetToDefault; public static String PropertiesFileEditorPreferencePage_key; public static String PropertiesFileEditorPreferencePage_value; diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.properties index 6c16e9b0575..09cff4be9d4 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.properties +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/PreferencesMessages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2024 IBM Corporation and others. +# Copyright (c) 2000, 2025 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -892,6 +892,7 @@ FoldingConfigurationBlock_enable= Enable f&olding FoldingConfigurationBlock_combo_caption= Select folding to &use: FoldingConfigurationBlock_info_no_preferences= The selected folding provider did not provide a preference control FoldingConfigurationBlock_error_not_exist= The ''{0}'' folding provider does not exist. +FoldingConfigurationBlock_error_project_prefs_not_supported=The ''{0}'' folding provider does not support project specific preferences. FoldingConfigurationBlock_warning_providerNotFound_resetToDefault= The ''{0}'' folding provider could not be found. Resetting to the default folding provider. # Properties File Editor diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/folding/DefaultJavaFoldingPreferenceBlock.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/folding/DefaultJavaFoldingPreferenceBlock.java index b8f64cb15ab..92d49f20cf4 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/folding/DefaultJavaFoldingPreferenceBlock.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/folding/DefaultJavaFoldingPreferenceBlock.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -15,10 +15,10 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; @@ -26,12 +26,19 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.ui.preferences.ScopedPreferenceStore; + +import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jdt.ui.PreferenceConstants; -import org.eclipse.jdt.ui.text.folding.IJavaFoldingPreferenceBlock; +import org.eclipse.jdt.ui.text.folding.IScopedJavaFoldingPreferenceBlock; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.preferences.OverlayPreferenceStore; @@ -43,7 +50,7 @@ * * @since 3.0 */ -public class DefaultJavaFoldingPreferenceBlock implements IJavaFoldingPreferenceBlock { +public class DefaultJavaFoldingPreferenceBlock implements IScopedJavaFoldingPreferenceBlock { private IPreferenceStore fStore; private OverlayPreferenceStore fOverlayStore; @@ -59,6 +66,12 @@ public void widgetSelected(SelectionEvent e) { fOverlayStore.setValue(fCheckBoxes.get(button), button.getSelection()); } }; + private Map fStringInputs= new HashMap<>(); + private ModifyListener fModifyListener = e -> { + Text text = (Text)e.widget; + fOverlayStore.setValue(fStringInputs.get(text), text.getText()); + }; + public DefaultJavaFoldingPreferenceBlock() { @@ -67,6 +80,16 @@ public DefaultJavaFoldingPreferenceBlock() { fOverlayStore= new OverlayPreferenceStore(fStore, fKeys); } + @Override + public void setScopeContext(IScopeContext context) { + if(context == null) { + fStore = JavaPlugin.getDefault().getPreferenceStore(); + } else { + fStore= new ScopedPreferenceStore(context, JavaUI.ID_PLUGIN); + } + fOverlayStore= new OverlayPreferenceStore(fStore, fKeys); + } + private OverlayKey[] createKeys() { ArrayList overlayKeys= new ArrayList<>(); @@ -75,6 +98,9 @@ private OverlayKey[] createKeys() { overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_METHODS)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_IMPORTS)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_HEADERS)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGIONS)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_START)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_END)); return overlayKeys.toArray(new OverlayKey[overlayKeys.size()]); } @@ -87,22 +113,37 @@ public Control createControl(Composite composite) { fOverlayStore.load(); fOverlayStore.start(); - Composite inner= new Composite(composite, SWT.NONE); GridLayout layout= new GridLayout(1, true); layout.verticalSpacing= 3; layout.marginWidth= 0; - inner.setLayout(layout); - - Label label= new Label(inner, SWT.LEFT); - label.setText(FoldingMessages.DefaultJavaFoldingPreferenceBlock_title); - - addCheckBox(inner, FoldingMessages.DefaultJavaFoldingPreferenceBlock_comments, PreferenceConstants.EDITOR_FOLDING_JAVADOC, 0); - addCheckBox(inner, FoldingMessages.DefaultJavaFoldingPreferenceBlock_headers, PreferenceConstants.EDITOR_FOLDING_HEADERS, 0); - addCheckBox(inner, FoldingMessages.DefaultJavaFoldingPreferenceBlock_innerTypes, PreferenceConstants.EDITOR_FOLDING_INNERTYPES, 0); - addCheckBox(inner, FoldingMessages.DefaultJavaFoldingPreferenceBlock_methods, PreferenceConstants.EDITOR_FOLDING_METHODS, 0); - addCheckBox(inner, FoldingMessages.DefaultJavaFoldingPreferenceBlock_imports, PreferenceConstants.EDITOR_FOLDING_IMPORTS, 0); - - return inner; + composite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + + Composite outer= new Composite(composite, SWT.NONE); + outer.setLayout(layout); + outer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + Group initialFoldingGroup= new Group(outer, SWT.NONE); + initialFoldingGroup.setLayout(layout); + initialFoldingGroup.setText(FoldingMessages.DefaultJavaFoldingPreferenceBlock_title); + initialFoldingGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + addCheckBox(initialFoldingGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_comments, PreferenceConstants.EDITOR_FOLDING_JAVADOC, 0); + addCheckBox(initialFoldingGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_headers, PreferenceConstants.EDITOR_FOLDING_HEADERS, 0); + addCheckBox(initialFoldingGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_innerTypes, PreferenceConstants.EDITOR_FOLDING_INNERTYPES, 0); + addCheckBox(initialFoldingGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_methods, PreferenceConstants.EDITOR_FOLDING_METHODS, 0); + addCheckBox(initialFoldingGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_imports, PreferenceConstants.EDITOR_FOLDING_IMPORTS, 0); + addCheckBox(initialFoldingGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_customRegions, PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGIONS, 0); + + Group customRegionGroup= new Group(outer, SWT.NONE); + customRegionGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + GridLayout customRegionLayout= new GridLayout(2, false); + customRegionGroup.setLayout(customRegionLayout); + customRegionGroup.setText(FoldingMessages.DefaultJavaFoldingPreferenceBlock_custom_region_title); + addStringInput(customRegionGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_CustomRegionStart, PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_START); + addStringInput(customRegionGroup, FoldingMessages.DefaultJavaFoldingPreferenceBlock_CustomRegionEnd, PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGION_END); + + return outer; } private Button addCheckBox(Composite parent, String label, String key, int indentation) { @@ -121,13 +162,29 @@ private Button addCheckBox(Composite parent, String label, String key, int inden return checkBox; } + private void addStringInput(Composite parent, String label, String key) { + Label labelElement = new Label(parent, SWT.LEFT); + labelElement.setText(label); + GridData labelGridData= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + labelGridData.horizontalSpan= 1; + labelGridData.grabExcessVerticalSpace= false; + labelElement.setLayoutData(labelGridData); + + Text textInput = new Text(parent, SWT.SINGLE | SWT.BORDER); + textInput.setText(label); + textInput.addModifyListener(fModifyListener); + + GridData textGridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false); + textGridData.horizontalSpan= 1; + textGridData.grabExcessVerticalSpace= true; + textInput.setLayoutData(textGridData); + + fStringInputs.put(textInput, key); + } + private void initializeFields() { - Iterator