Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add content filtering to outline view based on SymbolKind (fix issue #254) #1049

Merged
merged 9 commits into from
Aug 13, 2024
Merged
21 changes: 21 additions & 0 deletions org.eclipse.lsp4e/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,27 @@
</visibleWhen>
</command>
</menuContribution>
<menuContribution
allPopups="false"
locationURI="menu:org.eclipse.ui.views.ContentOutline">
<menu
id="org.eclipse.lsp4e.outline.hideMenu"
label="Hide...">
travkin79 marked this conversation as resolved.
Show resolved Hide resolved
<visibleWhen>
<reference
definitionId="org.eclipse.lsp4e.activePartHasCNFOutlinePage">
</reference>
</visibleWhen>
</menu>
</menuContribution>
<menuContribution
allPopups="false"
locationURI="menu:org.eclipse.lsp4e.outline.hideMenu">
<dynamic
class="org.eclipse.lsp4e.outline.OutlineViewHideSymbolKindMenuContributor"
id="org.eclipse.lsp4e.filters.dynamic">
</dynamic>
</menuContribution>
<menuContribution
allPopups="true"
locationURI="popup:org.eclipse.ui.genericeditor.source.menu?after=additions">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -225,17 +259,20 @@ public void resourceChanged(IResourceChangeEvent event) {
private final boolean refreshOnResourceChanged;
private boolean isQuickOutline;
private @Nullable IOutlineUpdater outlineUpdater;
private IOutlineUpdater preferencesDependantOutlineUpdater;

public LSSymbolsContentProvider() {
this(false);
}

public LSSymbolsContentProvider(boolean refreshOnResourceChanged) {
this.refreshOnResourceChanged = refreshOnResourceChanged;
preferencesDependantOutlineUpdater = new PreferencesChangedOutlineUpdater();
}

@Override
public void init(@NonNullByDefault({}) ICommonContentExtensionSite aConfig) {
preferencesDependantOutlineUpdater.install();
}

@Override
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -376,6 +436,7 @@ public void dispose() {
if (outlineUpdater != null) {
outlineUpdater.uninstall();
}
preferencesDependantOutlineUpdater.uninstall();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -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<SymbolKind>() {

@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);
}

}

}
Loading