Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e.test/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e.test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</parent>
<artifactId>org.eclipse.lsp4e.test</artifactId>
<packaging>eclipse-test-plugin</packaging>
<version>0.15.29-SNAPSHOT</version>
<version>0.15.30-SNAPSHOT</version>

<properties>
<os-jvm-flags /> <!-- for the default case -->
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 6 additions & 1 deletion org.eclipse.lsp4e/plugin.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
viewsCategory.name = Language Servers

# Folding commands
command.folding.foldAll.name = Fold All
command.folding.unfoldAll.name = Unfold All
menu.folding.name = Folding
102 changes: 101 additions & 1 deletion org.eclipse.lsp4e/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,106 @@
</foldingReconcilingStrategy>
</extension>

<!-- folding: define commands -->
<extension point="org.eclipse.ui.commands">
<command
categoryId="org.eclipse.lsp4e.category"
id="org.eclipse.lsp4e.folding.collapseAll"
name="%command.folding.foldAll.name" />
<command
categoryId="org.eclipse.lsp4e.category"
id="org.eclipse.lsp4e.folding.expandAll"
name="%command.folding.unfoldAll.name" />
</extension>

<!-- folding: register command handlers -->
<extension point="org.eclipse.ui.handlers">
<handler
class="org.eclipse.lsp4e.operations.folding.FoldAllHandler"
commandId="org.eclipse.lsp4e.folding.collapseAll">
<activeWhen>
<or>
<reference definitionId="org.eclipse.lsp4e.textSelectionHasLanguageServer" />
<reference definitionId="org.eclipse.lsp4e.editorHasLanguageServer" />
</or>
</activeWhen>
</handler>
<handler
class="org.eclipse.lsp4e.operations.folding.UnfoldAllHandler"
commandId="org.eclipse.lsp4e.folding.expandAll">
<activeWhen>
<or>
<reference definitionId="org.eclipse.lsp4e.textSelectionHasLanguageServer" />
<reference definitionId="org.eclipse.lsp4e.editorHasLanguageServer" />
</or>
</activeWhen>
</handler>
</extension>

<!-- folding: register menu entries -->
<extension point="org.eclipse.ui.menus">
<menuContribution
allPopups="true"
locationURI="popup:org.eclipse.ui.genericeditor.source.menu?after=additions">
<separator name="org.eclipse.lsp4e.folding.group.start" visible="true"/>
<command commandId="org.eclipse.lsp4e.folding.collapseAll" label="%command.folding.foldAll.name">
<visibleWhen>
<reference definitionId="org.eclipse.lsp4e.textSelectionHasLanguageServer" />
</visibleWhen>
</command>
<command commandId="org.eclipse.lsp4e.folding.expandAll" label="%command.folding.unfoldAll.name">
<visibleWhen>
<reference definitionId="org.eclipse.lsp4e.textSelectionHasLanguageServer" />
</visibleWhen>
</command>
<separator name="org.eclipse.lsp4e.folding.group.end" visible="true"/>
</menuContribution>
</extension>

<!-- folding: add commands to main Window > Source menu contributed by TM4E -->
<extension point="org.eclipse.ui.menus">
<menuContribution locationURI="menu:org.eclipse.tm4e.source.menu?after=additions">
<separator name="org.eclipse.lsp4e.folding.main.group.start" visible="true"/>
<command commandId="org.eclipse.lsp4e.folding.collapseAll" label="%command.folding.foldAll.name">
<visibleWhen>
<or>
<with variable="activeEditorInput">
<test property="org.eclipse.lsp4e.hasLanguageServer" />
</with>
<with variable="activeEditor">
<test property="org.eclipse.lsp4e.hasLanguageServer" />
</with>
</or>
</visibleWhen>
</command>
<command commandId="org.eclipse.lsp4e.folding.expandAll" label="%command.folding.unfoldAll.name">
<visibleWhen>
<or>
<with variable="activeEditorInput">
<test property="org.eclipse.lsp4e.hasLanguageServer" />
</with>
<with variable="activeEditor">
<test property="org.eclipse.lsp4e.hasLanguageServer" />
</with>
</or>
</visibleWhen>
</command>
<separator name="org.eclipse.lsp4e.folding.main.group.end" visible="true"/>
</menuContribution>
</extension>

<!-- folding: key bindings -->
<extension point="org.eclipse.ui.bindings">
<key sequence="M1+M2+NUMPAD_DIVIDE"
commandId="org.eclipse.lsp4e.folding.collapseAll"
contextId="org.eclipse.ui.genericeditor.genericEditorContext"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration" />
<key sequence="M1+M2+NUMPAD_MULTIPLY"
commandId="org.eclipse.lsp4e.folding.expandAll"
contextId="org.eclipse.ui.genericeditor.genericEditorContext"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration" />
</extension>


<!-- ===================================== -->
<!-- Find References Support -->
Expand Down Expand Up @@ -464,7 +564,7 @@
contentType="org.eclipse.core.runtime.text">
<enabledWhen>
<reference definitionId="org.eclipse.lsp4e.editorHasLanguageServer" />
</enabledWhen>
</enabledWhen>
</reconcilingStrategy>
</extension>

Expand Down
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<artifactId>org.eclipse.lsp4e</artifactId>
<packaging>eclipse-plugin</packaging>
<version>0.18.30-SNAPSHOT</version>
<version>0.18.31-SNAPSHOT</version>

<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading