From aa7089bc3bdbca87a412813ab9459b00929b6c7a Mon Sep 17 00:00:00 2001 From: Sebastian Thomschke Date: Sun, 12 Jan 2025 00:00:43 +0100 Subject: [PATCH] feat: support auto folding of comments and add folding preferences page --- org.eclipse.lsp4e/plugin.properties | 5 +- org.eclipse.lsp4e/plugin.xml | 12 ++- .../lsp4e/internal/SupportedFeatures.java | 13 ++- .../LSPFoldingReconcilingStrategy.java | 56 ++++++++++--- .../lsp4e/ui/FoldingPreferencePage.java | 82 +++++++++++++++++++ 5 files changed, 151 insertions(+), 17 deletions(-) create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/FoldingPreferencePage.java diff --git a/org.eclipse.lsp4e/plugin.properties b/org.eclipse.lsp4e/plugin.properties index 6d832ee62..92455f17f 100644 --- a/org.eclipse.lsp4e/plugin.properties +++ b/org.eclipse.lsp4e/plugin.properties @@ -19,8 +19,9 @@ openDeclarationHyperlink_name=Go to declaration openDeclarationHyperlink_description=Get to the location where this element is declared refactorings.menu.label=Refactorings codeactions.menu.label=Code Actions -languageservers.preference.page=Language Servers -languageservers.logging.preference.page=Logs +languageservers.preferences.page=Language Servers +languageservers.preferences.folding.page=Folding +languageservers.preferences.logging.page=Logs notification.category.label = LSP notification.event.label = LSP Notification command.toggle.highlight.label = Toggle Mark Occurrences diff --git a/org.eclipse.lsp4e/plugin.xml b/org.eclipse.lsp4e/plugin.xml index b21492689..941024179 100644 --- a/org.eclipse.lsp4e/plugin.xml +++ b/org.eclipse.lsp4e/plugin.xml @@ -239,13 +239,19 @@ + name="%languageservers.preferences.page"> + id="org.eclipse.lsp4e.preferences.logging" + name="%languageservers.preferences.logging.page"> + + - Add support for 'textDocument/foldingRange' - Bug 537706 + * Sebastian Thomschke (Vegard IT GmbH) - Add comments/region default folding and folding prefs listener */ package org.eclipse.lsp4e.operations.folding; @@ -38,10 +39,13 @@ 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.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.LanguageServers; import org.eclipse.lsp4e.internal.DocumentUtil; +import org.eclipse.lsp4e.ui.FoldingPreferencePage; import org.eclipse.lsp4j.FoldingRange; import org.eclipse.lsp4j.FoldingRangeKind; import org.eclipse.lsp4j.FoldingRangeRequestParams; @@ -52,9 +56,7 @@ import org.eclipse.swt.widgets.Canvas; /** - * LSP folding reconcilinig strategy which consumes the - * `textDocument/foldingRange` command. - * + * LSP folding reconcilinig strategy which consumes the `textDocument/foldingRange` command. */ public class LSPFoldingReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension, IProjectionListener, ITextViewerLifecycle { @@ -66,12 +68,36 @@ public class LSPFoldingReconcilingStrategy private @Nullable ProjectionViewer viewer; private List>> requests = List.of(); private volatile long timestamp = 0; - private final boolean collapseImports; - public LSPFoldingReconcilingStrategy() { - IPreferenceStore store = LanguageServerPlugin.getDefault().getPreferenceStore(); - collapseImports = store.getBoolean("foldingReconcilingStrategy.collapseImports"); //$NON-NLS-1$ - } + private final IPreferenceStore prefStore = LanguageServerPlugin.getDefault().getPreferenceStore(); + private boolean isFoldingEnabled = prefStore.getBoolean(FoldingPreferencePage.PREF_FOLDING_ENABLED); + private boolean collapseComments = prefStore.getBoolean(FoldingPreferencePage.PREF_AUTOFOLD_COMMENTS); + private boolean collapseFoldingRegions = prefStore.getBoolean(FoldingPreferencePage.PREF_AUTOFOLD_REGIONS); + private boolean collapseImports = prefStore.getBoolean(FoldingPreferencePage.PREF_AUTOFOLD_IMPORT_STATEMENTS); + private final IPropertyChangeListener foldingPrefsListener = (final PropertyChangeEvent event) -> { + final var newValue = event.getNewValue(); + if (newValue != null) { + switch (event.getProperty()) { + case FoldingPreferencePage.PREF_FOLDING_ENABLED: + isFoldingEnabled = Boolean.parseBoolean(newValue.toString()); + if(isFoldingEnabled) { + reconcile(null); // requests folding markers from LS + } else { + applyFolding(null); // removes all existing folding markers + } + break; + case FoldingPreferencePage.PREF_AUTOFOLD_COMMENTS: + collapseComments = Boolean.parseBoolean(newValue.toString()); + break; + case FoldingPreferencePage.PREF_AUTOFOLD_REGIONS: + collapseFoldingRegions = Boolean.parseBoolean(newValue.toString()); + break; + case FoldingPreferencePage.PREF_AUTOFOLD_IMPORT_STATEMENTS: + collapseImports = Boolean.parseBoolean(newValue.toString()); + break; + } + } + }; /** * A FoldingAnnotation is a {@link ProjectionAnnotation} it is folding and @@ -132,7 +158,7 @@ public void markCollapsed() { @Override public void reconcile(@Nullable IRegion subRegion) { final var document = this.document; - if (projectionAnnotationModel == null || document == null) { + if (!isFoldingEnabled || projectionAnnotationModel == null || document == null) { return; } @@ -165,9 +191,15 @@ private void applyFolding(@Nullable List ranges) { .sorted(Comparator.comparing(FoldingRange::getEndLine)) // .forEach(foldingRange -> { try { + final var collapsByDefault = foldingRange.getKind() != null + && switch (foldingRange.getKind()) { + case FoldingRangeKind.Comment -> collapseComments; + case FoldingRangeKind.Imports -> collapseImports; + case FoldingRangeKind.Region -> collapseFoldingRegions; + default -> false; + }; updateAnnotation(deletions, existing, additions, foldingRange.getStartLine(), - foldingRange.getEndLine(), - collapseImports && FoldingRangeKind.Imports.equals(foldingRange.getKind())); + foldingRange.getEndLine(), collapsByDefault); } catch (BadLocationException e) { // should never occur } @@ -196,6 +228,7 @@ public void install(ITextViewer viewer) { this.viewer = projViewer; projViewer.addProjectionListener(this); this.projectionAnnotationModel = projViewer.getProjectionAnnotationModel(); + prefStore.addPropertyChangeListener(foldingPrefsListener); } } @@ -205,6 +238,7 @@ public void uninstall() { if (viewer != null) { viewer.removeProjectionListener(this); viewer = null; + prefStore.removePropertyChangeListener(foldingPrefsListener); } projectionDisabled(); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/FoldingPreferencePage.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/FoldingPreferencePage.java new file mode 100644 index 000000000..af3002b36 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/FoldingPreferencePage.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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.ui; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.lsp4e.LanguageServerPlugin; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +public class FoldingPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public static final String PREF_FOLDING_ENABLED = "foldingReconcilingStrategy.enabled"; //$NON-NLS-1$ + public static final String PREF_AUTOFOLD_COMMENTS = "foldingReconcilingStrategy.collapseComments"; //$NON-NLS-1$ + public static final String PREF_AUTOFOLD_REGIONS = "foldingReconcilingStrategy.collapseRegions"; //$NON-NLS-1$ + public static final String PREF_AUTOFOLD_IMPORT_STATEMENTS = "foldingReconcilingStrategy.collapseImports"; //$NON-NLS-1$ + + public static final class PreferenceInitializer extends AbstractPreferenceInitializer { + @Override + public void initializeDefaultPreferences() { + final var store = LanguageServerPlugin.getDefault().getPreferenceStore(); + store.setDefault(PREF_FOLDING_ENABLED, true); + store.setDefault(PREF_AUTOFOLD_COMMENTS, false); + store.setDefault(PREF_AUTOFOLD_REGIONS, false); + store.setDefault(PREF_AUTOFOLD_IMPORT_STATEMENTS, false); + } + } + + public FoldingPreferencePage() { + super(GRID); + setPreferenceStore(LanguageServerPlugin.getDefault().getPreferenceStore()); + } + + @Override + public void createFieldEditors() { + Composite parent = getFieldEditorParent(); + + // Add check boxes + addField(new BooleanFieldEditor( // + PREF_FOLDING_ENABLED, // + "Enable folding", //$NON-NLS-1$ + parent)); + + // Add a label before the field editors + final var label = new Label(parent, SWT.NONE); + label.setText("Initially fold these elements:"); //$NON-NLS-1$ + label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + // Add check boxes + addField(new BooleanFieldEditor( // + PREF_AUTOFOLD_COMMENTS, // + "Comments", //$NON-NLS-1$ + parent)); + addField(new BooleanFieldEditor( // + PREF_AUTOFOLD_COMMENTS, // + "Folding Regions", //$NON-NLS-1$ + parent)); + addField(new BooleanFieldEditor( // + PREF_AUTOFOLD_IMPORT_STATEMENTS, // + "Import statements", //$NON-NLS-1$ + parent)); + } + + @Override + public void init(IWorkbench workbench) { + // Initialization logic if needed + } +} \ No newline at end of file