-
Notifications
You must be signed in to change notification settings - Fork 67
Make overlay icons in various views like the outline view customizable in client plug-ins #1521
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
Changes from 4 commits
2e77b4e
99b6592
9b3858c
dc0d05d
f0ae7f1
15a92e9
c7fe851
14125e0
680b710
e36780a
44962f2
44da61b
a69b595
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <!-- Schema file written by PDE --> | ||
| <schema targetNamespace="org.eclipse.lsp4e" xmlns="http://www.w3.org/2001/XMLSchema"> | ||
| <annotation> | ||
| <appinfo> | ||
| <meta.schema plugin="org.eclipse.lsp4e" id="symbolIconsProvider" name="Symbol Icons Provider"/> | ||
| </appinfo> | ||
| <documentation> | ||
| 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. | ||
| </documentation> | ||
| </annotation> | ||
|
|
||
| <element name="extension"> | ||
| <annotation> | ||
| <appinfo> | ||
| <meta.element /> | ||
| </appinfo> | ||
| </annotation> | ||
| <complexType> | ||
| <sequence minOccurs="1" maxOccurs="unbounded"> | ||
| <element ref="iconProvider"/> | ||
| </sequence> | ||
| <attribute name="point" type="string" use="required"> | ||
| <annotation> | ||
| <documentation> | ||
|
|
||
| </documentation> | ||
| </annotation> | ||
| </attribute> | ||
| <attribute name="id" type="string"> | ||
| <annotation> | ||
| <documentation> | ||
|
|
||
| </documentation> | ||
| </annotation> | ||
| </attribute> | ||
| <attribute name="name" type="string"> | ||
| <annotation> | ||
| <documentation> | ||
|
|
||
| </documentation> | ||
| <appinfo> | ||
| <meta.attribute translatable="true"/> | ||
| </appinfo> | ||
| </annotation> | ||
| </attribute> | ||
| </complexType> | ||
| </element> | ||
|
|
||
| <element name="iconProvider"> | ||
| <complexType> | ||
| <sequence minOccurs="1" maxOccurs="unbounded"> | ||
| <element ref="contentType"/> | ||
| </sequence> | ||
| <attribute name="id" type="string" use="required"> | ||
| <annotation> | ||
| <documentation> | ||
| A string uniquely identifying this symbol icon provider extension. | ||
| </documentation> | ||
| </annotation> | ||
| </attribute> | ||
| <attribute name="class" type="string" use="required"> | ||
| <annotation> | ||
| <documentation> | ||
|
|
||
| </documentation> | ||
| <appinfo> | ||
| <meta.attribute kind="java" basedOn="org.eclipse.lsp4e.ui.SymbolIconProvider:"/> | ||
| </appinfo> | ||
| </annotation> | ||
| </attribute> | ||
| </complexType> | ||
| </element> | ||
|
|
||
| <element name="contentType"> | ||
| <complexType> | ||
| <attribute name="contentTypeId" type="string" use="required"> | ||
| <annotation> | ||
| <documentation> | ||
|
|
||
| </documentation> | ||
| <appinfo> | ||
| <meta.attribute kind="identifier" basedOn="org.eclipse.core.contenttype.contentTypes/content-type/@id"/> | ||
| </appinfo> | ||
| </annotation> | ||
| </attribute> | ||
| </complexType> | ||
| </element> | ||
|
|
||
| <annotation> | ||
| <appinfo> | ||
| <meta.section type="since"/> | ||
| </appinfo> | ||
| <documentation> | ||
| [Enter the first release in which this extension point appears.] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if we need all of these annotations documenting the extension point. If you can provide them, they are nice, but then we need meaningful tags.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean by meaningful tags? |
||
| </documentation> | ||
| </annotation> | ||
|
|
||
| <annotation> | ||
| <appinfo> | ||
| <meta.section type="examples"/> | ||
| </appinfo> | ||
| <documentation> | ||
| [Enter extension point usage example here.] | ||
| </documentation> | ||
| </annotation> | ||
|
|
||
| <annotation> | ||
| <appinfo> | ||
| <meta.section type="apiinfo"/> | ||
| </appinfo> | ||
| <documentation> | ||
| [Enter API information here.] | ||
| </documentation> | ||
| </annotation> | ||
|
FlorianKroiss marked this conversation as resolved.
Outdated
|
||
|
|
||
| <annotation> | ||
| <appinfo> | ||
| <meta.section type="implementation"/> | ||
| </appinfo> | ||
| <documentation> | ||
| [Enter information about supplied implementation of this extension point.] | ||
| </documentation> | ||
| </annotation> | ||
|
|
||
| <annotation> | ||
| <appinfo> | ||
| <meta.section type="copyright"/> | ||
| </appinfo> | ||
| <documentation> | ||
| 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 | ||
| </documentation> | ||
| </annotation> | ||
|
|
||
| </schema> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<String, SymbolIconProvider> 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$ | ||
|
travkin79 marked this conversation as resolved.
Outdated
|
||
| 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) | ||
|
travkin79 marked this conversation as resolved.
Outdated
|
||
| 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; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you provide some documentation instead of the empty one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this applies to the other items as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've extended the documentation. Is that what you had in mind?