Skip to content

Commit

Permalink
feat: support auto folding of comments and add folding preferences page
Browse files Browse the repository at this point in the history
  • Loading branch information
sebthom committed Jan 11, 2025
1 parent e7dda3d commit aa7089b
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 17 deletions.
5 changes: 3 additions & 2 deletions org.eclipse.lsp4e/plugin.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 9 additions & 3 deletions org.eclipse.lsp4e/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,19 @@
<page
class="org.eclipse.lsp4e.ui.LanguageServerPreferencePage"
id="org.eclipse.lsp4e.preferences"
name="%languageservers.preference.page">
name="%languageservers.preferences.page">
</page>
<page
category="org.eclipse.lsp4e.preferences"
class="org.eclipse.lsp4e.ui.LoggingPreferencePage"
id="org.eclipse.lsp4e.logging.preferences"
name="%languageservers.logging.preference.page">
id="org.eclipse.lsp4e.preferences.logging"
name="%languageservers.preferences.logging.page">
</page>
<page
category="org.eclipse.lsp4e.preferences"
class="org.eclipse.lsp4e.ui.FoldingPreferencePage"
id="org.eclipse.lsp4e.preferences.folding"
name="%languageservers.preferences.folding.page">
</page>
</extension>
<extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import org.eclipse.lsp4j.ExecuteCommandCapabilities;
import org.eclipse.lsp4j.FailureHandlingKind;
import org.eclipse.lsp4j.FoldingRangeCapabilities;
import org.eclipse.lsp4j.FoldingRangeKind;
import org.eclipse.lsp4j.FoldingRangeKindSupportCapabilities;
import org.eclipse.lsp4j.FoldingRangeSupportCapabilities;
import org.eclipse.lsp4j.FormattingCapabilities;
import org.eclipse.lsp4j.HoverCapabilities;
import org.eclipse.lsp4j.InlayHintCapabilities;
Expand Down Expand Up @@ -111,7 +114,15 @@ public static TextDocumentClientCapabilities getTextDocumentClientCapabilities()
documentSymbol.setHierarchicalDocumentSymbolSupport(true);
documentSymbol.setSymbolKind(new SymbolKindCapabilities(List.of(SymbolKind.values())));
textDocumentClientCapabilities.setDocumentSymbol(documentSymbol);
textDocumentClientCapabilities.setFoldingRange(new FoldingRangeCapabilities());
final var foldingRangeCapabilities = new FoldingRangeCapabilities();
foldingRangeCapabilities.setLineFoldingOnly(true);
foldingRangeCapabilities.setFoldingRange(new FoldingRangeSupportCapabilities(false));
foldingRangeCapabilities.setFoldingRangeKind(new FoldingRangeKindSupportCapabilities(List.of( //
FoldingRangeKind.Comment,
FoldingRangeKind.Imports,
FoldingRangeKind.Region
)));
textDocumentClientCapabilities.setFoldingRange(foldingRangeCapabilities);
textDocumentClientCapabilities.setFormatting(new FormattingCapabilities(true));
final var hoverCapabilities = new HoverCapabilities();
hoverCapabilities.setContentFormat(List.of( //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*
* Contributors:
* Angelo Zerr <[email protected]> - 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;

Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -66,12 +68,36 @@ public class LSPFoldingReconcilingStrategy
private @Nullable ProjectionViewer viewer;
private List<CompletableFuture<@Nullable List<FoldingRange>>> 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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -165,9 +191,15 @@ private void applyFolding(@Nullable List<FoldingRange> 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
}
Expand Down Expand Up @@ -196,6 +228,7 @@ public void install(ITextViewer viewer) {
this.viewer = projViewer;
projViewer.addProjectionListener(this);
this.projectionAnnotationModel = projViewer.getProjectionAnnotationModel();
prefStore.addPropertyChangeListener(foldingPrefsListener);
}
}

Expand All @@ -205,6 +238,7 @@ public void uninstall() {
if (viewer != null) {
viewer.removeProjectionListener(this);
viewer = null;
prefStore.removePropertyChangeListener(foldingPrefsListener);
}
projectionDisabled();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit aa7089b

Please sign in to comment.