From 2e77b4ecd94566724a29180181cb59b590ee6ade Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Tue, 14 Apr 2026 11:38:26 +0200 Subject: [PATCH 01/13] Adapt overlay icon calculation for customization by LSP4E clients - Extract method for determining all overlay icons so that clients can override it to customize overlay icons. - Introduce convenience method for determining a symbol's name. - Forward the symbol object to methods for calculating the symbol's icon and overlay icons so that clients could use additional symbol details for customization, e.g. name and details fields. --- .../CallHierarchyLabelProvider.java | 2 +- .../TypeHierarchyItemLabelProvider.java | 2 +- .../lsp4e/outline/SymbolsLabelProvider.java | 2 +- .../src/org/eclipse/lsp4e/ui/LSPImages.java | 4 +- .../eclipse/lsp4e/ui/SymbolIconProvider.java | 95 ++++++++++++++++--- 5 files changed, 88 insertions(+), 17 deletions(-) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java index 809a2ed88..ae6db785a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java @@ -39,7 +39,7 @@ public CallHierarchyLabelProvider(SymbolIconProvider symbolIconProvider) { public @Nullable Image getImage(final @Nullable Object element) { if (element instanceof CallHierarchyViewTreeNode treeNode) { CallHierarchyItem callContainer = treeNode.getCallContainer(); - Image res = symbolIconProvider.getImageFor(callContainer.getKind(), callContainer.getTags()); + Image res = symbolIconProvider.getImageFor(callContainer.getKind(), callContainer.getTags(), element); if (res != null) { return res; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java index 4d426704e..a7c34e535 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java @@ -39,7 +39,7 @@ public String getText(Object element) { @Override public @Nullable Image getImage(@Nullable Object element) { if (element instanceof TypeHierarchyItem item) { - return symbolIconProvider.getImageFor(item.getKind(), item.getTags()); + return symbolIconProvider.getImageFor(item.getKind(), item.getTags(), element); } return element == null ? null : super.getImage(element); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java index e981fc337..5d6df01b9 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java @@ -170,7 +170,7 @@ public void dispose() { } if (actualElement != null && symbolKind != null) { - return symbolIconProvider.getImageFor(symbolKind, symbolTags, getMaxSeverity(actualElement)); + return symbolIconProvider.getImageFor(symbolKind, symbolTags, getMaxSeverity(actualElement), actualElement); } return null; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java index df3106af0..9eecc27de 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java @@ -292,8 +292,8 @@ public static ImageRegistry getImageRegistry() { * * @see #getImage(String)) * @see #getImageDescriptor(String) - * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List) - * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List, int) + * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List, Object) + * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List, int, Object) */ public static @Nullable Image imageFromSymbolKind(@Nullable SymbolKind kind) { if (kind == null) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java index c675253ce..db24e231e 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java @@ -21,8 +21,12 @@ import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.lsp4e.operations.symbols.SymbolsUtil; +import org.eclipse.lsp4e.outline.SymbolsModel.DocumentSymbolWithURI; +import org.eclipse.lsp4j.DocumentSymbol; +import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; import org.eclipse.lsp4j.SymbolTag; +import org.eclipse.lsp4j.WorkspaceSymbol; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.ISharedImages; @@ -33,6 +37,30 @@ */ public class SymbolIconProvider { + /** + * Returns a symbol's name if the given symbol is an instance of + * {@link DocumentSymbol}, or {@link DocumentSymbolWithURI}, + * {@link SymbolInformation}, or {@link WorkspaceSymbol}, + * returns null otherwise. + * + * @param symbol the symbol to get the name for + * @return the symbol's name or null. + */ + protected @Nullable String getName(Object symbol) { + String name = null; + if (symbol instanceof SymbolInformation info) { + name = info.getName(); + } else if (symbol instanceof WorkspaceSymbol wpSymbol) { + name = wpSymbol.getName(); + } else if (symbol instanceof DocumentSymbol docSymbol) { + name = docSymbol.getName(); + } else if (symbol instanceof DocumentSymbolWithURI symbolWithURI) { + name = symbolWithURI.symbol.getName(); + } + + return name; + } + /** * Returns an overlay icon {@link ImageDescriptor} for the given severity. * @@ -154,6 +182,10 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List visibilityTag = getHighestPrecedenceVisibilitySymbolTag(symbolTags); if (visibilityTag.isEmpty()) { + if (kind == SymbolKind.Constructor) { + // we'll add a constructor overlay icon instead in #getOverlaysFor(...), this way, the overlays will be customizable + return LSPImages.IMG_METHOD; + } return LSPImages.imageKeyFromSymbolKind(kind); } @@ -189,12 +221,14 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List symbolTags) { - return getImageFor(symbolKind, symbolTags, -1); + public @Nullable Image getImageFor(@Nullable SymbolKind symbolKind, @Nullable List symbolTags, + Object symbol) { + return getImageFor(symbolKind, symbolTags, -1, symbol); } /** @@ -206,12 +240,13 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List symbolTags, int severity) { + final @Nullable List symbolTags, int severity, Object symbol) { if (symbolKind == null) { return LSPImages.imageFromSymbolKind(symbolKind); @@ -221,10 +256,29 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List symbolTags, int severity, Object symbol) { + ImageDescriptor severityImageDescriptor = getOverlayForMarkerSeverity(severity); - ImageDescriptor deprecatedImageDescriptor = getUnderlayForDeprecation(SymbolsUtil.isDeprecated(finalSymbolTags)); + ImageDescriptor deprecatedImageDescriptor = getUnderlayForDeprecation(SymbolsUtil.isDeprecated(symbolTags)); - List additionalTags = getAdditionalSymbolTagsSorted(finalSymbolTags); + List additionalTags = getAdditionalSymbolTagsSorted(symbolTags); ImageDescriptor topLeftOverlayDescriptor = null; ImageDescriptor topRightOverlayDescriptor = null; @@ -233,8 +287,7 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List we need to add a "C" overlay to show it's a constructor - if (SymbolKind.Constructor == symbolKind - && !baseImageKey.equals(LSPImages.imageKeyFromSymbolKind(symbolKind))) { + if (SymbolKind.Constructor == symbolKind) { topRightOverlayDescriptor = LSPImages.getImageDescriptor(LSPImages.IMG_OVR_CONSTRUCTOR); } @@ -262,11 +315,29 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List Date: Thu, 16 Apr 2026 14:27:04 +0200 Subject: [PATCH 02/13] Introduce symbol icon provider extension point for customizing overlay icons --- org.eclipse.lsp4e/plugin.xml | 1 + .../schema/symbolIconsProvider.exsd | 140 ++++++++++++++++ .../CallHierarchyLabelProvider.java | 14 +- .../internal/SymbolIconProviderRegistry.java | 154 ++++++++++++++++++ .../TypeHierarchyItemLabelProvider.java | 14 +- .../lsp4e/outline/SymbolsLabelProvider.java | 9 +- .../src/org/eclipse/lsp4e/ui/LSPImages.java | 2 +- .../eclipse/lsp4e/ui/SymbolIconProvider.java | 12 +- 8 files changed, 314 insertions(+), 32 deletions(-) create mode 100644 org.eclipse.lsp4e/schema/symbolIconsProvider.exsd create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java diff --git a/org.eclipse.lsp4e/plugin.xml b/org.eclipse.lsp4e/plugin.xml index fc7d6aeee..42c5b03b7 100644 --- a/org.eclipse.lsp4e/plugin.xml +++ b/org.eclipse.lsp4e/plugin.xml @@ -2,6 +2,7 @@ + diff --git a/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd b/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd new file mode 100644 index 000000000..794019a5a --- /dev/null +++ b/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd @@ -0,0 +1,140 @@ + + + + + + + + + This extension point allows for customizing the (document) symbol icons with their overlays, their placing and filtering for certain language servers. This way, clients can use other images, adapt overlay icon placing and decide wich symbol details to show or hide in the outline view, call hierarchy, or type hierarchy. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A string uniquely identifying this symbol icon provider extension. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [Enter the first release in which this extension point appears.] + + + + + + + + + [Enter extension point usage example here.] + + + + + + + + + [Enter API information here.] + + + + + + + + + [Enter information about supplied implementation of this extension point.] + + + + + + + + + Copyright (c) 2024 Advantest 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 + + + + diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java index ae6db785a..313dc93bc 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java @@ -16,6 +16,7 @@ import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; +import org.eclipse.lsp4e.operations.symbols.internal.SymbolIconProviderRegistry; import org.eclipse.lsp4e.ui.SymbolIconProvider; import org.eclipse.lsp4j.CallHierarchyItem; import org.eclipse.swt.graphics.Image; @@ -25,21 +26,12 @@ */ public class CallHierarchyLabelProvider extends LabelProvider implements IStyledLabelProvider { - private final SymbolIconProvider symbolIconProvider; - - public CallHierarchyLabelProvider() { - this(new SymbolIconProvider()); - } - - public CallHierarchyLabelProvider(SymbolIconProvider symbolIconProvider) { - this.symbolIconProvider = symbolIconProvider; - } - @Override public @Nullable Image getImage(final @Nullable Object element) { if (element instanceof CallHierarchyViewTreeNode treeNode) { CallHierarchyItem callContainer = treeNode.getCallContainer(); - Image res = symbolIconProvider.getImageFor(callContainer.getKind(), callContainer.getTags(), element); + SymbolIconProvider symbolIconProvider = SymbolIconProviderRegistry.getSymbolIconProviderFor(callContainer); + Image res = symbolIconProvider.getImageFor(callContainer.getKind(), callContainer.getTags(), callContainer); if (res != null) { return res; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java new file mode 100644 index 000000000..6369dae35 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2024 Advantest 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: + * Dietrich Travkin (Solunar GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.lsp4e.operations.symbols.internal; + +import java.net.URI; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.lsp4e.LanguageServerPlugin; +import org.eclipse.lsp4e.outline.SymbolsModel.DocumentSymbolWithURI; +import org.eclipse.lsp4e.ui.SymbolIconProvider; +import org.eclipse.lsp4j.CallHierarchyItem; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.TypeHierarchyItem; +import org.eclipse.lsp4j.WorkspaceSymbol; +import org.eclipse.lsp4j.WorkspaceSymbolLocation; + +public class SymbolIconProviderRegistry { + + private static final String EXTENSION_POINT_ID = LanguageServerPlugin.PLUGIN_ID + ".symbolIconsProvider"; //$NON-NLS-1$ + + private static SymbolIconProviderRegistry INSTANCE = null; + + // default icon provider from LSP4E + private final SymbolIconProvider defaultIconProvider = new SymbolIconProvider(); + + // symbol icon providers from extensions, cached per content type ID + private final Map cachedIconProviders = new HashMap<>(); + + private SymbolIconProviderRegistry() { + loadExtensions(); + } + + private static SymbolIconProviderRegistry get() { + if (INSTANCE == null) { + INSTANCE = new SymbolIconProviderRegistry(); + } + return INSTANCE; + } + + private void loadExtensions() { + IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(EXTENSION_POINT_ID); + + if (extensionPoint == null) { + Platform.getLog(getClass()).error("No extension point found for ID " + EXTENSION_POINT_ID); //$NON-NLS-1$ + return; + } + + for (IConfigurationElement configurationElement : extensionPoint.getConfigurationElements()) { + if ("iconProvider".equals(configurationElement.getName())) { //$NON-NLS-1$ + String className = configurationElement.getAttribute("class"); //$NON-NLS-1$ + + if (className == null || className.isBlank()) { + continue; + } + + SymbolIconProvider iconProvider; + try { + iconProvider = (SymbolIconProvider) configurationElement.createExecutableExtension("class"); //$NON-NLS-1$ + } catch (CoreException | ClassCastException e) { + Platform.getLog(getClass()).error("Failed instantiating class " + className, e); //$NON-NLS-1$ + continue; + } + + for ( IConfigurationElement contentTypeConfigElement : configurationElement.getChildren("contentType")) { //$NON-NLS-1$ + String contentTypeId = contentTypeConfigElement.getAttribute("contentTypeId"); //$NON-NLS-1$ + + if (contentTypeId == null || contentTypeId.isBlank()) { + continue; + } + + cachedIconProviders.put(contentTypeId, iconProvider); + } + } + } + } + + public static SymbolIconProvider getSymbolIconProviderFor(Object symbol) { + return get().getIconProvider(symbol); + } + + private SymbolIconProvider getIconProvider(Object symbol) { + URI uri = getUri(symbol); + if (uri == null) { + return null; + } + + String fileName = null; + try { + fileName = Path.of(uri.getPath()).getFileName().toString(); + } catch (Exception e) { + Platform.getLog(getClass()).warn("Failed to parse file name from URI " + uri, e); //$NON-NLS-1$ + return null; + } + + IContentType[] contentTypes = Platform.getContentTypeManager().findContentTypesFor(fileName); + for (IContentType contentType : contentTypes) { + IContentType candidate = contentType; + while (candidate != null) { + SymbolIconProvider iconProvider = cachedIconProviders.get(candidate.getId()); + if (iconProvider != null) { + return iconProvider; + } + candidate = candidate.getBaseType(); + } + } + + return defaultIconProvider; + } + + private @Nullable URI getUri(Object symbol) { + if (symbol instanceof SymbolInformation info) + return toUri(info.getLocation().getUri()); + if (symbol instanceof WorkspaceSymbol ws) + return toUri(ws.getLocation().map(Location::getUri, WorkspaceSymbolLocation::getUri)); + if (symbol instanceof DocumentSymbolWithURI s) + return s.uri; + if (symbol instanceof TypeHierarchyItem item) // for subclasses handling TH + return toUri(item.getUri()); + if (symbol instanceof CallHierarchyItem item) // for subclasses handling CH + return toUri(item.getUri()); + return null; // plain DocumentSymbol — no URI + } + + private @Nullable URI toUri(String uri) { + if (uri == null) { + return null; + } + + try { + return URI.create(uri); + } catch (IllegalArgumentException e) { + Platform.getLog(getClass()).warn("Failed to parse URI " + uri, e); //$NON-NLS-1$ + return null; + } + } +} diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java index a7c34e535..06ff3a17f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java @@ -12,22 +12,13 @@ import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; +import org.eclipse.lsp4e.operations.symbols.internal.SymbolIconProviderRegistry; import org.eclipse.lsp4e.ui.SymbolIconProvider; import org.eclipse.lsp4j.TypeHierarchyItem; import org.eclipse.swt.graphics.Image; public class TypeHierarchyItemLabelProvider extends LabelProvider implements IStyledLabelProvider { - private final SymbolIconProvider symbolIconProvider; - - public TypeHierarchyItemLabelProvider() { - this(new SymbolIconProvider()); - } - - public TypeHierarchyItemLabelProvider(SymbolIconProvider symbolIconProvider) { - this.symbolIconProvider = symbolIconProvider; - } - @Override public String getText(Object element) { if (element instanceof TypeHierarchyItem item) { @@ -39,7 +30,8 @@ public String getText(Object element) { @Override public @Nullable Image getImage(@Nullable Object element) { if (element instanceof TypeHierarchyItem item) { - return symbolIconProvider.getImageFor(item.getKind(), item.getTags(), element); + SymbolIconProvider symbolIconProvider = SymbolIconProviderRegistry.getSymbolIconProviderFor(item); + return symbolIconProvider.getImageFor(item.getKind(), item.getTags(), item); } return element == null ? null : super.getImage(element); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java index 5d6df01b9..ca3f204de 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java @@ -45,6 +45,7 @@ import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.internal.StyleUtil; import org.eclipse.lsp4e.operations.symbols.SymbolsUtil; +import org.eclipse.lsp4e.operations.symbols.internal.SymbolIconProviderRegistry; import org.eclipse.lsp4e.outline.SymbolsModel.DocumentSymbolWithURI; import org.eclipse.lsp4e.ui.LSPImages; import org.eclipse.lsp4e.ui.Messages; @@ -72,8 +73,6 @@ public class SymbolsLabelProvider extends LabelProvider implements ICommonLabelProvider, IStyledLabelProvider, IPreferenceChangeListener { - private final SymbolIconProvider symbolIconProvider; - private final Map> severities = new HashMap<>(); private final IResourceChangeListener listener = e -> { try { @@ -103,13 +102,8 @@ public SymbolsLabelProvider() { } public SymbolsLabelProvider(final boolean showLocation, final boolean showKind) { - this(showLocation, showKind, new SymbolIconProvider()); - } - - public SymbolsLabelProvider(final boolean showLocation, final boolean showKind, SymbolIconProvider symbolIconProvider) { this.showLocation = showLocation; this.showKind = showKind; - this.symbolIconProvider = symbolIconProvider; InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID).addPreferenceChangeListener(this); ResourcesPlugin.getWorkspace().addResourceChangeListener(listener); } @@ -170,6 +164,7 @@ public void dispose() { } if (actualElement != null && symbolKind != null) { + SymbolIconProvider symbolIconProvider = SymbolIconProviderRegistry.getSymbolIconProviderFor(actualElement); return symbolIconProvider.getImageFor(symbolKind, symbolTags, getMaxSeverity(actualElement), actualElement); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java index 9eecc27de..a2351f98e 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java @@ -290,7 +290,7 @@ public static ImageRegistry getImageRegistry() { * @param kind a document symbol's kind * @return an image representing the given symbol kind or null * - * @see #getImage(String)) + * @see #getImage(String) * @see #getImageDescriptor(String) * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List, Object) * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List, int, Object) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java index db24e231e..5509b4c1f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java @@ -22,10 +22,12 @@ import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.lsp4e.operations.symbols.SymbolsUtil; import org.eclipse.lsp4e.outline.SymbolsModel.DocumentSymbolWithURI; +import org.eclipse.lsp4j.CallHierarchyItem; import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; import org.eclipse.lsp4j.SymbolTag; +import org.eclipse.lsp4j.TypeHierarchyItem; import org.eclipse.lsp4j.WorkspaceSymbol; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.ISharedImages; @@ -41,6 +43,7 @@ public class SymbolIconProvider { * Returns a symbol's name if the given symbol is an instance of * {@link DocumentSymbol}, or {@link DocumentSymbolWithURI}, * {@link SymbolInformation}, or {@link WorkspaceSymbol}, + * or {@link CallHierarchyItem}, or {@link TypeHierarchyItem}, * returns null otherwise. * * @param symbol the symbol to get the name for @@ -56,6 +59,10 @@ public class SymbolIconProvider { name = docSymbol.getName(); } else if (symbol instanceof DocumentSymbolWithURI symbolWithURI) { name = symbolWithURI.symbol.getName(); + } else if (symbol instanceof CallHierarchyItem callHierItem) { + name = callHierItem.getName(); + } else if (symbol instanceof TypeHierarchyItem typeHierItem) { + name = typeHierItem.getName(); } return name; @@ -88,7 +95,7 @@ public class SymbolIconProvider { return LSPImages.imageDescriptorOverlayFromSymbolTag(SymbolTag.Deprecated); } - private static final List VISIBILITY_PRECEDENCE = List.of( + protected static final List VISIBILITY_PRECEDENCE = List.of( SymbolTag.Public, SymbolTag.Protected, SymbolTag.Package, SymbolTag.Internal, SymbolTag.File, SymbolTag.Private); @@ -104,7 +111,7 @@ protected List getVisibilityPrecedence() { // In order to keep the number of overlay icons rather small in the UI, we do not show the following symbol tags: // SymbolTag.Nullable, SymbolTag.NonNull, SymbolTag.Declaration, SymbolTag.Definition - private static final List ADDITIONAL_TAGS_PRECEDENCE = List.of( + protected static final List ADDITIONAL_TAGS_PRECEDENCE = List.of( SymbolTag.Static, SymbolTag.Final, SymbolTag.Abstract, SymbolTag.Overrides, SymbolTag.Implements, SymbolTag.Virtual, SymbolTag.Sealed, SymbolTag.Synchronized, SymbolTag.Transient, SymbolTag.Volatile, @@ -249,6 +256,7 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List symbolTags, int severity, Object symbol) { if (symbolKind == null) { + // return empty image return LSPImages.imageFromSymbolKind(symbolKind); } From 9b3858ccdacbcb6ff2be22cd6f0111c8d4a16618 Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Thu, 16 Apr 2026 15:05:48 +0200 Subject: [PATCH 03/13] Ensure file image is returned from shared images --- .../src/org/eclipse/lsp4e/ui/LSPImages.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java index a2351f98e..ee5dadc5d 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java @@ -243,6 +243,9 @@ private record ImageWithOverlaysKey(String baseImageKey, * Returns the Image identified by the given key, or null if it does not exist. */ public static @Nullable Image getImage(String key) { + if (ISharedImages.IMG_OBJ_FILE.equals(key)) { + return getSharedImage(key); + } return getImageRegistry().get(key); } @@ -250,6 +253,9 @@ private record ImageWithOverlaysKey(String baseImageKey, * Returns the ImageDescriptor identified by the given key, or null if it does not exist. */ public static @Nullable ImageDescriptor getImageDescriptor(String key) { + if (ISharedImages.IMG_OBJ_FILE.equals(key)) { + return getSharedImageDescriptor(key); + } return getImageRegistry().getDescriptor(key); } @@ -266,7 +272,7 @@ public static ImageRegistry getImageRegistry() { * @return the workbench's shared image for the , or null if not found */ public static @Nullable Image getSharedImage(@Nullable String imageId) { - if(imageId == null) { + if (imageId == null) { return null; } return PlatformUI.getWorkbench().getSharedImages().getImage(imageId); @@ -277,7 +283,7 @@ public static ImageRegistry getImageRegistry() { * @return the workbench's shared image descriptor for the workbench, or null if not found */ public static @Nullable ImageDescriptor getSharedImageDescriptor(@Nullable String imageId) { - if(imageId == null) { + if (imageId == null) { return null; } return PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(imageId); From dc0d05d5afa8375fb3cfb3c5b64bd12b9dbc86fc Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Thu, 16 Apr 2026 16:52:51 +0200 Subject: [PATCH 04/13] Adapt test to changes in LSPImages class --- .../lsp4e/test/utils/LSPImagesTest.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/LSPImagesTest.java b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/LSPImagesTest.java index 369aeaafe..539fa17d1 100644 --- a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/LSPImagesTest.java +++ b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/LSPImagesTest.java @@ -28,21 +28,21 @@ import org.junit.jupiter.params.provider.EnumSource.Mode; public class LSPImagesTest { - + @ParameterizedTest @EnumSource(SymbolKind.class) public void testAllImagesForSymbolKindAvailable(SymbolKind kind) { Image img = LSPImages.imageFromSymbolKind(kind); - + assertNotNull(img); } - + @ParameterizedTest @EnumSource(SymbolTag.class) public void testAllOverlayImagesForSymbolTagAvailable(SymbolTag tag) { ImageDescriptor descriptor = LSPImages.imageDescriptorOverlayFromSymbolTag(tag); Image img = LSPImages.imageOverlayFromSymbolTag(tag); - + assertNotNull(descriptor); assertNotNull(img); } @@ -54,22 +54,23 @@ public void testAllOverlayImagesForSymbolTagAvailable(SymbolTag tag) { public void testVisibilityOverlayImagesForFieldsAndMethodsAvailable(SymbolTag tag) { var symbolTags = List.of(tag); SymbolIconProvider labelProvider = new SymbolIconProvider(); - - Image fieldImage = labelProvider.getImageFor(SymbolKind.Field, symbolTags); - Image methodImage = labelProvider.getImageFor(SymbolKind.Method, symbolTags); - + + // we do not need a symbol for this test, so the last argument is null + Image fieldImage = labelProvider.getImageFor(SymbolKind.Field, symbolTags, null); + Image methodImage = labelProvider.getImageFor(SymbolKind.Method, symbolTags, null); + assertNotNull(fieldImage); assertNotNull(methodImage); } - + @ParameterizedTest @EnumSource(value=CompletionItemKind.class, mode=Mode.EXCLUDE, names= { "Color", "Event", "Operator" }) public void testAllImagesForCompletionItemKindAvailable(CompletionItemKind kind) { CompletionItem item = new CompletionItem(); item.setKind(kind); - + Image img = LSPImages.imageFromCompletionItem(item); - + assertNotNull(img); } From f0ae7f1e22d1d3f2d3d743c944ffe590055b94d3 Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Thu, 16 Apr 2026 17:17:00 +0200 Subject: [PATCH 05/13] Add a developer note to explain an unexpected method parameter --- .../src/org/eclipse/lsp4e/ui/SymbolIconProvider.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java index 5509b4c1f..3ec44d881 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java @@ -282,6 +282,8 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List symbolTags, int severity, Object symbol) { + // The symbol parameter is intentionally added to the method signature in order to give subclasses + // a way to adapt the overlay icons depending on a symbol's properties. ImageDescriptor severityImageDescriptor = getOverlayForMarkerSeverity(severity); ImageDescriptor deprecatedImageDescriptor = getUnderlayForDeprecation(SymbolsUtil.isDeprecated(symbolTags)); From 15a92e95857b260bd2770c7790e8081e5fde312b Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Thu, 16 Apr 2026 17:25:53 +0200 Subject: [PATCH 06/13] Bump version in test bundle --- org.eclipse.lsp4e.test/META-INF/MANIFEST.MF | 2 +- org.eclipse.lsp4e.test/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF b/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF index 09733f8d6..d9ad4b604 100644 --- a/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF @@ -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.16.9.qualifier +Bundle-Version: 0.16.10.qualifier Fragment-Host: org.eclipse.lsp4e Bundle-Vendor: Eclipse LSP4E Bundle-RequiredExecutionEnvironment: JavaSE-21 diff --git a/org.eclipse.lsp4e.test/pom.xml b/org.eclipse.lsp4e.test/pom.xml index a098539ea..9347b66c7 100644 --- a/org.eclipse.lsp4e.test/pom.xml +++ b/org.eclipse.lsp4e.test/pom.xml @@ -8,7 +8,7 @@ org.eclipse.lsp4e.test eclipse-test-plugin - 0.16.9-SNAPSHOT + 0.16.10-SNAPSHOT From c7fe851c6f340171751e702a2e8bc4da940745d6 Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Fri, 17 Apr 2026 10:15:14 +0200 Subject: [PATCH 07/13] Fix potential NPE --- .../symbols/internal/SymbolIconProviderRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java index 6369dae35..eefccb895 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java @@ -99,7 +99,7 @@ public static SymbolIconProvider getSymbolIconProviderFor(Object symbol) { private SymbolIconProvider getIconProvider(Object symbol) { URI uri = getUri(symbol); if (uri == null) { - return null; + return defaultIconProvider; } String fileName = null; @@ -107,7 +107,7 @@ private SymbolIconProvider getIconProvider(Object symbol) { fileName = Path.of(uri.getPath()).getFileName().toString(); } catch (Exception e) { Platform.getLog(getClass()).warn("Failed to parse file name from URI " + uri, e); //$NON-NLS-1$ - return null; + return defaultIconProvider; } IContentType[] contentTypes = Platform.getContentTypeManager().findContentTypesFor(fileName); From 14125e0900476fe2edbe567707883ff86fa50248 Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Fri, 17 Apr 2026 10:20:42 +0200 Subject: [PATCH 08/13] Make singleton implementation thread-safe --- .../internal/SymbolIconProviderRegistry.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java index eefccb895..2b3183643 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java @@ -36,8 +36,6 @@ public class SymbolIconProviderRegistry { private static final String EXTENSION_POINT_ID = LanguageServerPlugin.PLUGIN_ID + ".symbolIconsProvider"; //$NON-NLS-1$ - private static SymbolIconProviderRegistry INSTANCE = null; - // default icon provider from LSP4E private final SymbolIconProvider defaultIconProvider = new SymbolIconProvider(); @@ -48,11 +46,17 @@ private SymbolIconProviderRegistry() { loadExtensions(); } + /** + * Initialization-on-demand holder: the JVM guarantees that this nested class + * is loaded and initialized exactly once, in a thread-safe manner, the first + * time {@link #get()} is called. + */ + private static final class Holder { + static final SymbolIconProviderRegistry INSTANCE = new SymbolIconProviderRegistry(); + } + private static SymbolIconProviderRegistry get() { - if (INSTANCE == null) { - INSTANCE = new SymbolIconProviderRegistry(); - } - return INSTANCE; + return Holder.INSTANCE; } private void loadExtensions() { From 680b710ee432aa44ad76f751c613f6c90600bf1c Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Fri, 17 Apr 2026 10:28:26 +0200 Subject: [PATCH 09/13] Extend extension point documentation --- org.eclipse.lsp4e/schema/symbolIconsProvider.exsd | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd b/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd index 794019a5a..166dbcb2c 100644 --- a/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd +++ b/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd @@ -23,7 +23,9 @@ - + The fully qualified identifier of the extension point. + +This extension point offers client plug-ins options to adapt placement and selection of overlay icons for document symbols and other related elements. @@ -62,7 +64,7 @@ - + The fully qualified name of the class implementing this symbol icon provider. The class must extend <code>org.eclipse.lsp4e.ui.SymbolIconProvider</code>. Clients can override individual methods to customize overlay icons and their placement (top-left, top-right, bottom-left, bottom-right, underlay). @@ -77,7 +79,7 @@ - + The unique identifier of a content type (as registered with the <code>org.eclipse.core.contenttype.contentTypes</code> extension point) for which this icon provider should be used. The provider is also used for all derived content types that do not have a more specific provider registered. @@ -92,7 +94,7 @@ - [Enter the first release in which this extension point appears.] + 0.19.10 @@ -119,7 +121,7 @@ - [Enter information about supplied implementation of this extension point.] + LSP4E provides a default implementation via <code>org.eclipse.lsp4e.ui.SymbolIconProvider</code>. It is used automatically for all content types that have no specific provider registered through this extension point. It renders symbol icons based on the LSP <code>SymbolKind</code> and up to four overlay icons derived from <code>SymbolTag</code>s (visibility, static/final/abstract/etc., marker severity) plus a deprecation underlay. From e36780a448aa867f11e34ff1d3ce19944ea2fdbb Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Fri, 17 Apr 2026 10:50:50 +0200 Subject: [PATCH 10/13] Use LSP4E logging convenience methods --- .../src/org/eclipse/lsp4e/LanguageServerPlugin.java | 12 +++++++++++- .../symbols/internal/SymbolIconProviderRegistry.java | 8 ++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java index 39d0e808c..6c109bf6a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java @@ -11,7 +11,7 @@ *******************************************************************************/ package org.eclipse.lsp4e; -import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.charset.StandardCharsets.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -91,6 +91,16 @@ protected void initializeImageRegistry(ImageRegistry registry) { LSPImages.initalize(registry); } + /** + * Utility method to log errors. + * + * @param message + * User comprehensible message + */ + public static void logError(final String message) { + logError(message, null); + } + /** * Utility method to log errors. * diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java index 2b3183643..135b980db 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java @@ -63,7 +63,7 @@ private void loadExtensions() { IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(EXTENSION_POINT_ID); if (extensionPoint == null) { - Platform.getLog(getClass()).error("No extension point found for ID " + EXTENSION_POINT_ID); //$NON-NLS-1$ + LanguageServerPlugin.logError("No extension point found for ID " + EXTENSION_POINT_ID); //$NON-NLS-1$ return; } @@ -79,7 +79,7 @@ private void loadExtensions() { try { iconProvider = (SymbolIconProvider) configurationElement.createExecutableExtension("class"); //$NON-NLS-1$ } catch (CoreException | ClassCastException e) { - Platform.getLog(getClass()).error("Failed instantiating class " + className, e); //$NON-NLS-1$ + LanguageServerPlugin.logError("Failed instantiating class " + className, e); //$NON-NLS-1$ continue; } @@ -110,7 +110,7 @@ private SymbolIconProvider getIconProvider(Object symbol) { try { fileName = Path.of(uri.getPath()).getFileName().toString(); } catch (Exception e) { - Platform.getLog(getClass()).warn("Failed to parse file name from URI " + uri, e); //$NON-NLS-1$ + LanguageServerPlugin.logWarning("Failed to parse file name from URI " + uri, e); //$NON-NLS-1$ return defaultIconProvider; } @@ -151,7 +151,7 @@ private SymbolIconProvider getIconProvider(Object symbol) { try { return URI.create(uri); } catch (IllegalArgumentException e) { - Platform.getLog(getClass()).warn("Failed to parse URI " + uri, e); //$NON-NLS-1$ + LanguageServerPlugin.logWarning("Failed to parse URI " + uri, e); //$NON-NLS-1$ return null; } } From 44962f26393dea821549910e6bea5b8c1be873b9 Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Fri, 17 Apr 2026 10:58:50 +0200 Subject: [PATCH 11/13] Introduce a self-explaining convenience method for empty images --- .../src/org/eclipse/lsp4e/ui/LSPImages.java | 11 ++++++++++- .../src/org/eclipse/lsp4e/ui/SymbolIconProvider.java | 3 +-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java index ee5dadc5d..f278f2b38 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java @@ -239,6 +239,15 @@ private record ImageWithOverlaysKey(String baseImageKey, @Nullable ImageDescriptor overlayBottomLeftDescriptor, @Nullable ImageDescriptor overlayBottomRightDescriptor, @Nullable ImageDescriptor underlayDescriptor) {} + /** + * Returns an empty fallback image f(16x16 pixels). + * + * @return an empty 16x16 icon + */ + public static Image getEmptyImage() { + return EMPTY_IMAGE; + } + /** * Returns the Image identified by the given key, or null if it does not exist. */ @@ -303,7 +312,7 @@ public static ImageRegistry getImageRegistry() { */ public static @Nullable Image imageFromSymbolKind(@Nullable SymbolKind kind) { if (kind == null) { - return EMPTY_IMAGE; + return getEmptyImage(); } String imgKey = imageKeyFromSymbolKind(kind); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java index 3ec44d881..f026fc6ad 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java @@ -256,8 +256,7 @@ protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List symbolTags, int severity, Object symbol) { if (symbolKind == null) { - // return empty image - return LSPImages.imageFromSymbolKind(symbolKind); + return LSPImages.getEmptyImage(); } final List finalSymbolTags = symbolTags != null ? symbolTags : Collections.emptyList(); From 44da61bcb314dcd43681c00cc8bb627fe7798d28 Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Mon, 20 Apr 2026 09:51:22 +0200 Subject: [PATCH 12/13] Replace complex instanceof cascade with a switch expression --- .../internal/SymbolIconProviderRegistry.java | 19 ++++++-------- .../eclipse/lsp4e/ui/SymbolIconProvider.java | 25 +++++++------------ 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java index 135b980db..05349e4c4 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/internal/SymbolIconProviderRegistry.java @@ -130,17 +130,14 @@ private SymbolIconProvider getIconProvider(Object symbol) { } private @Nullable URI getUri(Object symbol) { - if (symbol instanceof SymbolInformation info) - return toUri(info.getLocation().getUri()); - if (symbol instanceof WorkspaceSymbol ws) - return toUri(ws.getLocation().map(Location::getUri, WorkspaceSymbolLocation::getUri)); - if (symbol instanceof DocumentSymbolWithURI s) - return s.uri; - if (symbol instanceof TypeHierarchyItem item) // for subclasses handling TH - return toUri(item.getUri()); - if (symbol instanceof CallHierarchyItem item) // for subclasses handling CH - return toUri(item.getUri()); - return null; // plain DocumentSymbol — no URI + return switch (symbol) { + case SymbolInformation info -> toUri(info.getLocation().getUri()); + case WorkspaceSymbol ws -> toUri(ws.getLocation().map(Location::getUri, WorkspaceSymbolLocation::getUri)); + case DocumentSymbolWithURI s -> s.uri; + case TypeHierarchyItem item -> toUri(item.getUri()); + case CallHierarchyItem item -> toUri(item.getUri()); + default -> null; // plain DocumentSymbol — no URI + }; } private @Nullable URI toUri(String uri) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java index f026fc6ad..ce5253e22 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java @@ -50,22 +50,15 @@ public class SymbolIconProvider { * @return the symbol's name or null. */ protected @Nullable String getName(Object symbol) { - String name = null; - if (symbol instanceof SymbolInformation info) { - name = info.getName(); - } else if (symbol instanceof WorkspaceSymbol wpSymbol) { - name = wpSymbol.getName(); - } else if (symbol instanceof DocumentSymbol docSymbol) { - name = docSymbol.getName(); - } else if (symbol instanceof DocumentSymbolWithURI symbolWithURI) { - name = symbolWithURI.symbol.getName(); - } else if (symbol instanceof CallHierarchyItem callHierItem) { - name = callHierItem.getName(); - } else if (symbol instanceof TypeHierarchyItem typeHierItem) { - name = typeHierItem.getName(); - } - - return name; + return switch (symbol) { + case SymbolInformation info -> info.getName(); + case WorkspaceSymbol wpSymbol -> wpSymbol.getName(); + case DocumentSymbol docSymbol -> docSymbol.getName(); + case DocumentSymbolWithURI symbolWithURI -> symbolWithURI.symbol.getName(); + case CallHierarchyItem callHierItem -> callHierItem.getName(); + case TypeHierarchyItem typeHierItem -> typeHierItem.getName(); + default -> null; + }; } /** From a69b5955658fa08cfc8f321249f4c47c34d1d011 Mon Sep 17 00:00:00 2001 From: Dietrich Travkin Date: Mon, 20 Apr 2026 16:47:47 +0200 Subject: [PATCH 13/13] Remove empty / dummy annotations in extension point definition --- .../schema/symbolIconsProvider.exsd | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd b/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd index 166dbcb2c..cbfbbb433 100644 --- a/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd +++ b/org.eclipse.lsp4e/schema/symbolIconsProvider.exsd @@ -98,23 +98,7 @@ This extension point offers client plug-ins options to adapt placement and selec - - - - - - [Enter extension point usage example here.] - - - - - - - - [Enter API information here.] - -