diff --git a/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF b/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF index 7ac73965a..dacd4fd52 100644 --- a/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Tests for language server bundle (Incubation) Bundle-SymbolicName: org.eclipse.lsp4e.test;singleton:=true -Bundle-Version: 0.15.29.qualifier +Bundle-Version: 0.15.30.qualifier Fragment-Host: org.eclipse.lsp4e Bundle-Vendor: Eclipse LSP4E Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/org.eclipse.lsp4e.test/pom.xml b/org.eclipse.lsp4e.test/pom.xml index 4f1415c95..5e4998f17 100644 --- a/org.eclipse.lsp4e.test/pom.xml +++ b/org.eclipse.lsp4e.test/pom.xml @@ -8,7 +8,7 @@ org.eclipse.lsp4e.test eclipse-test-plugin - 0.15.29-SNAPSHOT + 0.15.30-SNAPSHOT diff --git a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/folding/FoldingCommandsTest.java b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/folding/FoldingCommandsTest.java new file mode 100644 index 000000000..42003420b --- /dev/null +++ b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/folding/FoldingCommandsTest.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2025 Vegard IT GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Sebastian Thomschke (Vegard IT GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.lsp4e.test.folding; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.ITextViewer; +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.jface.text.source.projection.ProjectionViewer; +import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4e.LanguageServerPlugin; +import org.eclipse.lsp4e.test.utils.AbstractTest; +import org.eclipse.lsp4e.test.utils.TestUtils; +import org.eclipse.lsp4e.tests.mock.MockLanguageServer; +import org.eclipse.lsp4e.ui.FoldingPreferencePage; +import org.eclipse.lsp4j.FoldingRange; +import org.eclipse.lsp4j.FoldingRangeKind; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.IHandlerService; +import org.junit.Test; + +public class FoldingCommandsTest extends AbstractTest { + + private static final int MAX_WAIT_MS = 5_000; + + private static final String CONTENT = """ + /** + * SPDX-License-Identifier: EPL-2.0 + */ + import + import + import + /** + * Some comment + */ + visible + """; + + @Test + public void foldAndUnfoldAllCommands() throws Exception { + // Ensure no auto-folding interferes with the command behavior + configureAutoFolding(false); + + // Provide folding ranges from the Mock LS: license header and imports + final var foldingRangeLicense = new FoldingRange(0, 2); + foldingRangeLicense.setKind(FoldingRangeKind.Comment); + final var foldingRangeImport = new FoldingRange(3, 5); + foldingRangeImport.setKind(FoldingRangeKind.Imports); + MockLanguageServer.INSTANCE.setFoldingRanges(List.of(foldingRangeLicense, foldingRangeImport)); + + // Open editor and wait until folding annotations are present + final var editor = TestUtils.openEditor(TestUtils.createUniqueTestFile(null, CONTENT)); + final ITextViewer viewer = LSPEclipseUtils.getTextViewer(editor); + assertTrue(viewer instanceof ProjectionViewer); + + final var pViewer = (ProjectionViewer) viewer; + TestUtils.waitForAndAssertCondition(MAX_WAIT_MS, () -> getAnnotationModel(pViewer) != null); + final ProjectionAnnotationModel model = getAnnotationModel(pViewer); + + // Ensure folding annotations are populated after projection model exists + // by toggling the folding-enabled preference to trigger a reconcile. + IPreferenceStore store = LanguageServerPlugin.getDefault().getPreferenceStore(); + store.setValue(FoldingPreferencePage.PREF_FOLDING_ENABLED, false); + store.setValue(FoldingPreferencePage.PREF_FOLDING_ENABLED, true); + + TestUtils.waitForAndAssertCondition(MAX_WAIT_MS, () -> countAnnotations(model) == 2); + assertEquals(2, countAnnotations(model)); + + // Execute "Fold All" + IHandlerService handlerService = PlatformUI.getWorkbench().getService(IHandlerService.class); + handlerService.executeCommand("org.eclipse.lsp4e.folding.collapseAll", null); + + TestUtils.waitForAndAssertCondition(MAX_WAIT_MS, () -> countCollapsed(model) == 2); + assertEquals(2, countCollapsed(model)); + + // Execute "Unfold All" + handlerService.executeCommand("org.eclipse.lsp4e.folding.expandAll", null); + TestUtils.waitForAndAssertCondition(MAX_WAIT_MS, () -> countCollapsed(model) == 0); + assertEquals(0, countCollapsed(model)); + } + + private static ProjectionAnnotationModel getAnnotationModel(ProjectionViewer viewer) { + return viewer.getProjectionAnnotationModel(); + } + + private static int countAnnotations(ProjectionAnnotationModel model) { + int count = 0; + for (var it = model.getAnnotationIterator(); it != null && it.hasNext();) { + if (it.next() instanceof ProjectionAnnotation) { + count++; + } + } + return count; + } + + private static int countCollapsed(ProjectionAnnotationModel model) { + int count = 0; + for (var it = model.getAnnotationIterator(); it != null && it.hasNext();) { + Annotation a = it.next(); + if (a instanceof ProjectionAnnotation pa && pa.isCollapsed()) { + count++; + } + } + return count; + } + + private static void configureAutoFolding(boolean enabled) { + IPreferenceStore store = LanguageServerPlugin.getDefault().getPreferenceStore(); + store.setValue(FoldingPreferencePage.PREF_FOLDING_ENABLED, true); + store.setValue(FoldingPreferencePage.PREF_AUTOFOLD_COMMENTS, enabled); + store.setValue(FoldingPreferencePage.PREF_AUTOFOLD_LICENSE_HEADERS_COMMENTS, enabled); + store.setValue(FoldingPreferencePage.PREF_AUTOFOLD_REGIONS, enabled); + store.setValue(FoldingPreferencePage.PREF_AUTOFOLD_IMPORT_STATEMENTS, enabled); + } +} diff --git a/org.eclipse.lsp4e/META-INF/MANIFEST.MF b/org.eclipse.lsp4e/META-INF/MANIFEST.MF index 6ad4bc5b5..2487e8291 100644 --- a/org.eclipse.lsp4e/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Language Server Protocol client for Eclipse IDE (Incubation) Bundle-SymbolicName: org.eclipse.lsp4e;singleton:=true -Bundle-Version: 0.18.30.qualifier +Bundle-Version: 0.18.31.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.12.0", org.eclipse.equinox.common;bundle-version="3.8.0", diff --git a/org.eclipse.lsp4e/plugin.properties b/org.eclipse.lsp4e/plugin.properties index 7046593a7..114aac434 100644 --- a/org.eclipse.lsp4e/plugin.properties +++ b/org.eclipse.lsp4e/plugin.properties @@ -47,4 +47,9 @@ command.open.quick.type.hierarchy.description = Open Quick Call Hierarchy for th view.callHierarchy.name = Call Hierarchy view.typeHierarchy.name = Type Hierarchy view.languageServers.name = Language Servers -viewsCategory.name = Language Servers \ No newline at end of file +viewsCategory.name = Language Servers + +# Folding commands +command.folding.foldAll.name = Fold All +command.folding.unfoldAll.name = Unfold All +menu.folding.name = Folding diff --git a/org.eclipse.lsp4e/plugin.xml b/org.eclipse.lsp4e/plugin.xml index e4506139c..9e1f2dbf6 100644 --- a/org.eclipse.lsp4e/plugin.xml +++ b/org.eclipse.lsp4e/plugin.xml @@ -154,6 +154,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -464,7 +564,7 @@ contentType="org.eclipse.core.runtime.text"> - + diff --git a/org.eclipse.lsp4e/pom.xml b/org.eclipse.lsp4e/pom.xml index f8a9f9958..d2a46aab4 100644 --- a/org.eclipse.lsp4e/pom.xml +++ b/org.eclipse.lsp4e/pom.xml @@ -10,7 +10,7 @@ org.eclipse.lsp4e eclipse-plugin - 0.18.30-SNAPSHOT + 0.18.31-SNAPSHOT diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/FoldAllHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/FoldAllHandler.java new file mode 100644 index 000000000..b45cbcde9 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/FoldAllHandler.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2025 Vegard IT GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Sebastian Thomschke (Vegard IT GmbH) - initial implementation. + *******************************************************************************/ +package org.eclipse.lsp4e.operations.folding; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jface.text.source.projection.ProjectionViewer; +import org.eclipse.lsp4e.ui.UI; + +public class FoldAllHandler extends AbstractHandler { + + @Override + public @Nullable Object execute(final ExecutionEvent event) { + if (UI.getActiveTextViewer() instanceof final ProjectionViewer viewer && viewer.isProjectionMode()) { + UI.runOnUIThread(() -> viewer.doOperation(ProjectionViewer.COLLAPSE_ALL)); + } + return null; + } +} diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/UnfoldAllHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/UnfoldAllHandler.java new file mode 100644 index 000000000..906f9862a --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/UnfoldAllHandler.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2025 Vegard IT GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Sebastian Thomschke (Vegard IT GmbH) - initial implementation. + *******************************************************************************/ +package org.eclipse.lsp4e.operations.folding; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jface.text.source.projection.ProjectionViewer; +import org.eclipse.lsp4e.ui.UI; + +public class UnfoldAllHandler extends AbstractHandler { + + @Override + public @Nullable Object execute(final ExecutionEvent event) { + if (UI.getActiveTextViewer() instanceof final ProjectionViewer viewer && viewer.isProjectionMode()) { + UI.runOnUIThread(() -> viewer.doOperation(ProjectionViewer.EXPAND_ALL)); + } + return null; + } +}