From a240416dc731ee479da473909fbbd376bb872575 Mon Sep 17 00:00:00 2001
From: Dietrich Travkin <10887297+travkin79@users.noreply.github.com>
Date: Tue, 13 Aug 2024 19:53:00 +0200
Subject: [PATCH] Add content filtering to outline view based on SymbolKind
(fix issue #254) (#1049)
---
org.eclipse.lsp4e/plugin.xml | 21 +++++
.../eclipse/lsp4e/outline/CNFOutlinePage.java | 1 +
.../outline/LSSymbolsContentProvider.java | 81 ++++++++++++++---
...lineViewHideSymbolKindMenuContributor.java | 87 +++++++++++++++++++
4 files changed, 180 insertions(+), 10 deletions(-)
create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineViewHideSymbolKindMenuContributor.java
diff --git a/org.eclipse.lsp4e/plugin.xml b/org.eclipse.lsp4e/plugin.xml
index 82d103b45..129b22dbe 100644
--- a/org.eclipse.lsp4e/plugin.xml
+++ b/org.eclipse.lsp4e/plugin.xml
@@ -282,6 +282,27 @@
+
+
+
+
+
+
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java
index 067aa5cd4..38bfd33ac 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java
@@ -63,6 +63,7 @@ public class CNFOutlinePage implements IContentOutlinePage, ILabelProviderListen
public static final String LINK_WITH_EDITOR_PREFERENCE = ID + ".linkWithEditor"; //$NON-NLS-1$
public static final String SHOW_KIND_PREFERENCE = ID + ".showKind"; //$NON-NLS-1$
public static final String SORT_OUTLINE_PREFERENCE = ID + ".sortOutline"; //$NON-NLS-1$
+ public static final String HIDE_DOCUMENT_SYMBOL_KIND_PREFERENCE_PREFIX = ID + ".hide"; //$NON-NLS-1$
private CommonViewer outlineViewer = lateNonNull();
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java
index 4648166c6..cab53be9f 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java
@@ -7,9 +7,10 @@
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
- * Mickael Istria (Red Hat Inc.) - initial implementation
- * Lucas Bullen (Red Hat Inc.) - Bug 508472 - Outline to provide "Link with Editor"
- * - Bug 517428 - Requests sent before initialization
+ * Mickael Istria (Red Hat Inc.) - initial implementation
+ * Lucas Bullen (Red Hat Inc.) - Bug 508472 - Outline to provide "Link with Editor"
+ * - Bug 517428 - Requests sent before initialization
+ * Dietrich Travkin (Solunar GmbH) - Issue 254 - Add outline view contents filtering
*******************************************************************************/
package org.eclipse.lsp4e.outline;
@@ -31,6 +32,9 @@
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -53,10 +57,12 @@
import org.eclipse.lsp4e.LanguageServerPlugin;
import org.eclipse.lsp4e.LanguageServerWrapper;
import org.eclipse.lsp4e.internal.CancellationUtil;
+import org.eclipse.lsp4e.outline.SymbolsModel.DocumentSymbolWithURI;
import org.eclipse.lsp4e.ui.UI;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.ui.IMemento;
@@ -139,6 +145,34 @@ public void documentAboutToBeChanged(DocumentEvent event) {
public void documentChanged(DocumentEvent event) {
refreshTreeContentFromLS();
}
+
+ }
+
+ private final class PreferencesChangedOutlineUpdater implements IPreferenceChangeListener, IOutlineUpdater {
+
+ @Override
+ public void install() {
+ IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID);
+ preferences.addPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void uninstall() {
+ IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID);
+ preferences.removePreferenceChangeListener(this);
+ }
+
+ @Override
+ public void preferenceChange(PreferenceChangeEvent event) {
+ if (viewer != null
+ && event.getKey().startsWith(CNFOutlinePage.HIDE_DOCUMENT_SYMBOL_KIND_PREFERENCE_PREFIX)) {
+ viewer.getControl().getDisplay().asyncExec(() -> {
+ if(!viewer.getTree().isDisposed()) {
+ viewer.refresh();
+ }
+ });
+ }
+ }
}
private final class ReconcilerOutlineUpdater extends AbstractReconciler implements IOutlineUpdater {
@@ -225,6 +259,7 @@ public void resourceChanged(IResourceChangeEvent event) {
private final boolean refreshOnResourceChanged;
private boolean isQuickOutline;
private @Nullable IOutlineUpdater outlineUpdater;
+ private IOutlineUpdater preferencesDependantOutlineUpdater;
public LSSymbolsContentProvider() {
this(false);
@@ -232,10 +267,12 @@ public LSSymbolsContentProvider() {
public LSSymbolsContentProvider(boolean refreshOnResourceChanged) {
this.refreshOnResourceChanged = refreshOnResourceChanged;
+ preferencesDependantOutlineUpdater = new PreferencesChangedOutlineUpdater();
}
@Override
public void init(@NonNullByDefault({}) ICommonContentExtensionSite aConfig) {
+ preferencesDependantOutlineUpdater.install();
}
@Override
@@ -281,18 +318,39 @@ private IOutlineUpdater createOutlineUpdater() {
@Override
public Object[] getElements(@Nullable Object inputElement) {
- if (this.symbols != null && !this.symbols.isDone()) {
+ if (symbols != null && !symbols.isDone()) {
return new Object[] { new PendingUpdateAdapter() };
}
- if (this.lastError != null && symbolsModel.getElements().length == 0) {
+ if (lastError != null && symbolsModel.getElements().length == 0) {
return new Object[] { "An error occured, see log for details" }; //$NON-NLS-1$
}
- return symbolsModel.getElements();
+ return Arrays.stream(symbolsModel.getElements())
+ .filter(element -> !hideElement(element))
+ .toArray(Object[]::new);
}
@Override
public Object[] getChildren(Object parentElement) {
- return symbolsModel.getChildren(parentElement);
+ return Arrays.stream(symbolsModel.getChildren(parentElement))
+ .filter(element -> !hideElement(element))
+ .toArray(Object[]::new);
+ }
+
+ private boolean hideElement(Object element) {
+ SymbolKind kind = null;
+
+ if (element instanceof DocumentSymbol documentSymbol) {
+ kind = documentSymbol.getKind();
+ } else if (element instanceof DocumentSymbolWithURI documentSymbolWithURI) {
+ kind = documentSymbolWithURI.symbol.getKind();
+ } else if (element instanceof SymbolInformation symbolInformation) {
+ kind = symbolInformation.getKind();
+ }
+
+ if (kind != null) {
+ return OutlineViewHideSymbolKindMenuContributor.isHideSymbolKind(kind);
+ }
+ return false;
}
@Override
@@ -342,10 +400,12 @@ protected void refreshTreeContentFromLS() {
TreePath[] initialSelection = ((ITreeSelection) viewer.getSelection()).getPaths();
viewer.refresh();
if (expandedElements.length > 0) {
- viewer.setExpandedTreePaths(Arrays.stream(expandedElements).map(symbolsModel::toUpdatedSymbol)
- .filter(Objects::nonNull).toArray(TreePath[]::new));
+ viewer.setExpandedTreePaths(Arrays.stream(expandedElements)
+ .map(symbolsModel::toUpdatedSymbol)
+ .filter(Objects::nonNull).toArray(TreePath[]::new));
viewer.setSelection(new TreeSelection(Arrays.stream(initialSelection)
- .map(symbolsModel::toUpdatedSymbol).filter(Objects::nonNull).toArray(TreePath[]::new)));
+ .map(symbolsModel::toUpdatedSymbol)
+ .filter(Objects::nonNull).toArray(TreePath[]::new)));
} else {
viewer.expandToLevel(EXPAND_ROOT_LEVEL);
}
@@ -376,6 +436,7 @@ public void dispose() {
if (outlineUpdater != null) {
outlineUpdater.uninstall();
}
+ preferencesDependantOutlineUpdater.uninstall();
}
@Override
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineViewHideSymbolKindMenuContributor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineViewHideSymbolKindMenuContributor.java
new file mode 100644
index 000000000..d7e543b36
--- /dev/null
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineViewHideSymbolKindMenuContributor.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Advantest Europe GmbH. All rights reserved.
+ * 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:
+ * Dietrich Travkin (Solunar GmbH) - initial implementation of outline contents filtering (issue #254)
+ *******************************************************************************/
+package org.eclipse.lsp4e.outline;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.lsp4e.LanguageServerPlugin;
+import org.eclipse.lsp4e.ui.LSPImages;
+import org.eclipse.lsp4j.SymbolKind;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.actions.CompoundContributionItem;
+
+public class OutlineViewHideSymbolKindMenuContributor extends CompoundContributionItem {
+
+ @Override
+ protected IContributionItem[] getContributionItems() {
+ return Arrays.stream(SymbolKind.values())
+ .sorted(new Comparator() {
+
+ @Override
+ public int compare(SymbolKind sk1, SymbolKind sk2) {
+ return sk1.name().compareTo(sk2.name());
+ }
+
+ })
+ .map(kind -> createHideSymbolKindContributionItem(kind))
+ .toArray(IContributionItem[]::new);
+ }
+
+ private IContributionItem createHideSymbolKindContributionItem(SymbolKind kind) {
+ return new ActionContributionItem(new HideSymbolKindAction(kind));
+ }
+
+ static boolean isHideSymbolKind(SymbolKind kind) {
+ IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID);
+ return preferences.getBoolean(CNFOutlinePage.HIDE_DOCUMENT_SYMBOL_KIND_PREFERENCE_PREFIX + kind.name(), false);
+ }
+
+ static boolean toggleHideSymbolKind(SymbolKind kind) {
+ IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID);
+ boolean oldValue = isHideSymbolKind(kind);
+
+ preferences.putBoolean(CNFOutlinePage.HIDE_DOCUMENT_SYMBOL_KIND_PREFERENCE_PREFIX + kind.name(), !oldValue);
+
+ return !oldValue;
+ }
+
+ private static class HideSymbolKindAction extends Action {
+ private final SymbolKind kind;
+
+ HideSymbolKindAction(SymbolKind kind) {
+ super(kind.name(), IAction.AS_CHECK_BOX);
+ this.kind = kind;
+ setChecked(isHideSymbolKind(kind));
+
+ Image img = LSPImages.imageFromSymbolKind(kind);
+ if (img != null) {
+ setImageDescriptor(ImageDescriptor.createFromImage(img));
+ }
+ }
+
+ @Override
+ public void run() {
+ boolean checkedState = toggleHideSymbolKind(kind);
+ setChecked(checkedState);
+ }
+
+ }
+
+}