diff --git a/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF b/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF index 8962a6a6a..7367814e1 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.7.qualifier +Bundle-Version: 0.16.8.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 d95907fcd..1dbcfaff1 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.7-SNAPSHOT + 0.16.8-SNAPSHOT diff --git a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/symbols/SymbolsUtilTest.java b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/symbols/SymbolsUtilTest.java new file mode 100644 index 000000000..e954c4f38 --- /dev/null +++ b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/symbols/SymbolsUtilTest.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * 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.test.symbols; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.lsp4e.operations.symbols.SymbolsUtil; +import org.eclipse.lsp4j.DocumentSymbol; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.SymbolTag; +import org.eclipse.lsp4j.WorkspaceSymbol; +import org.junit.jupiter.api.Test; + +public class SymbolsUtilTest { + + private static final List symbolTagsWithDeprecated = Arrays.asList( + SymbolTag.Package, SymbolTag.Deprecated, SymbolTag.ReadOnly); + private static final List symbolTagsWithoutDeprecated = Arrays.asList( + SymbolTag.Public, SymbolTag.Declaration, SymbolTag.Static); + + @Test + public void testDeprecatedCheckForSymbolInformation() { + var symbolInformation = new SymbolInformation(); + + assertFalse(SymbolsUtil.isDeprecated(symbolInformation)); + + symbolInformation.setDeprecated(true); + + assertTrue(SymbolsUtil.isDeprecated(symbolInformation)); + + symbolInformation = new SymbolInformation(); + symbolInformation.setTags(symbolTagsWithDeprecated); + + assertTrue(SymbolsUtil.isDeprecated(symbolInformation)); + + symbolInformation.setTags(symbolTagsWithoutDeprecated); + + assertFalse(SymbolsUtil.isDeprecated(symbolInformation)); + } + + @Test + public void testDeprecatedCheckForWorkspaceSymbol() { + var workspaceSymbol = new WorkspaceSymbol(); + + assertFalse(SymbolsUtil.isDeprecated(workspaceSymbol)); + + workspaceSymbol.setTags(symbolTagsWithDeprecated); + + assertTrue(SymbolsUtil.isDeprecated(workspaceSymbol)); + + workspaceSymbol.setTags(symbolTagsWithoutDeprecated); + + assertFalse(SymbolsUtil.isDeprecated(workspaceSymbol)); + } + + @Test + public void testDeprecatedCheckForDocumentSymbol() { + var documentSymbol = new DocumentSymbol(); + + assertFalse(SymbolsUtil.isDeprecated(documentSymbol)); + + documentSymbol.setDeprecated(true); + + assertTrue(SymbolsUtil.isDeprecated(documentSymbol)); + + documentSymbol = new DocumentSymbol(); + documentSymbol.setTags(symbolTagsWithDeprecated); + + assertTrue(SymbolsUtil.isDeprecated(documentSymbol)); + + documentSymbol.setTags(symbolTagsWithoutDeprecated); + + assertFalse(SymbolsUtil.isDeprecated(documentSymbol)); + } + +} 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 new file mode 100644 index 000000000..369aeaafe --- /dev/null +++ b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/LSPImagesTest.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.test.utils; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.List; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.lsp4e.ui.LSPImages; +import org.eclipse.lsp4e.ui.SymbolIconProvider; +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.SymbolKind; +import org.eclipse.lsp4j.SymbolTag; +import org.eclipse.swt.graphics.Image; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +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); + } + + // Deprecated is used to test the case where no visibility tag is available, that should default to the standard symbol icon + @ParameterizedTest + @EnumSource(value=SymbolTag.class, mode=Mode.INCLUDE, names= { "Private", "Package", "Protected", "Public", + "Internal", "File", "Deprecated"}) + 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); + + 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); + } + +} diff --git a/org.eclipse.lsp4e/META-INF/MANIFEST.MF b/org.eclipse.lsp4e/META-INF/MANIFEST.MF index ffe2dbf57..494912d00 100644 --- a/org.eclipse.lsp4e/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Language Server Protocol client for Eclipse IDE (Incubation) Bundle-SymbolicName: org.eclipse.lsp4e;singleton:=true -Bundle-Version: 0.19.7.qualifier +Bundle-Version: 0.19.8.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.12.0", org.eclipse.equinox.common;bundle-version="3.8.0", diff --git a/org.eclipse.lsp4e/icons/full/obj16/constructor.png b/org.eclipse.lsp4e/icons/full/obj16/constructor.png index ab97bb71e..71e558555 100644 Binary files a/org.eclipse.lsp4e/icons/full/obj16/constructor.png and b/org.eclipse.lsp4e/icons/full/obj16/constructor.png differ diff --git a/org.eclipse.lsp4e/icons/full/obj16/constructor.svg b/org.eclipse.lsp4e/icons/full/obj16/constructor.svg index c1fdb7378..4a229c816 100644 --- a/org.eclipse.lsp4e/icons/full/obj16/constructor.svg +++ b/org.eclipse.lsp4e/icons/full/obj16/constructor.svg @@ -2,23 +2,23 @@ + inkscape:export-ydpi="96" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + inkscape:window-y="58" + inkscape:window-maximized="0" + inkscape:showpageshadow="0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#505050"> + snapvisiblegridlinesonly="true" + originx="0" + originy="0" + spacingy="1" + spacingx="1" + units="px" /> @@ -467,19 +475,46 @@ id="tspan4209" x="4.8992257" y="938.64496" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:6.14256096px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#ffffff;fill-opacity:1">M - + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:6.14256px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#ffffff;fill-opacity:1">M + + + id="path4163-4-0" + d="m 15.347633,1041.4132 v 0.5002 c 0,0 -0.532599,1.0498 -1.506129,1.0498 h -1.188536 c -0.515959,0 -1.124963,-0.7698 -1.124963,-0.9276 l -0.148438,-0.6884 v -0.8854" + style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.01003;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/constructor@2x.png b/org.eclipse.lsp4e/icons/full/obj16/constructor@2x.png index 92aab92e7..bcae997b3 100644 Binary files a/org.eclipse.lsp4e/icons/full/obj16/constructor@2x.png and b/org.eclipse.lsp4e/icons/full/obj16/constructor@2x.png differ diff --git a/org.eclipse.lsp4e/icons/full/obj16/field_file_vis_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/field_file_vis_obj.svg new file mode 100644 index 000000000..cac739db4 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/field_file_vis_obj.svg @@ -0,0 +1,522 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/field_internal_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/field_internal_obj.svg new file mode 100644 index 000000000..0c27a94c1 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/field_internal_obj.svg @@ -0,0 +1,444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/field_package_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/field_package_obj.svg new file mode 100644 index 000000000..03cd8110e --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/field_package_obj.svg @@ -0,0 +1,422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/field_private_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/field_private_obj.svg new file mode 100644 index 000000000..c5d4ab012 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/field_private_obj.svg @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/field_protected_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/field_protected_obj.svg new file mode 100644 index 000000000..f939b486b --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/field_protected_obj.svg @@ -0,0 +1,511 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/field_public_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/field_public_obj.svg new file mode 100644 index 000000000..71b9836ad --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/field_public_obj.svg @@ -0,0 +1,460 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/method_file_vis_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/method_file_vis_obj.svg new file mode 100644 index 000000000..0db1dbbaa --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/method_file_vis_obj.svg @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/method_internal_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/method_internal_obj.svg new file mode 100644 index 000000000..63390382d --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/method_internal_obj.svg @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/method_package_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/method_package_obj.svg new file mode 100644 index 000000000..2040c9630 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/method_package_obj.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/method_private_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/method_private_obj.svg new file mode 100644 index 000000000..f83fdcf3a --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/method_private_obj.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/method_protected_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/method_protected_obj.svg new file mode 100644 index 000000000..0a8783f76 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/method_protected_obj.svg @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/obj16/method_public_obj.svg b/org.eclipse.lsp4e/icons/full/obj16/method_public_obj.svg new file mode 100644 index 000000000..e69941d40 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/obj16/method_public_obj.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/abstract_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/abstract_co.svg new file mode 100644 index 000000000..416c9365f --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/abstract_co.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/constr_ovr.svg b/org.eclipse.lsp4e/icons/full/ovr16/constr_ovr.svg new file mode 100644 index 000000000..8c4fab343 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/constr_ovr.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/declaration_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/declaration_co.svg new file mode 100644 index 000000000..95c493013 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/declaration_co.svg @@ -0,0 +1,1052 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/definition_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/definition_co.svg new file mode 100644 index 000000000..b106c342e --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/definition_co.svg @@ -0,0 +1,1052 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/deprecated.svg b/org.eclipse.lsp4e/icons/full/ovr16/deprecated.svg new file mode 100644 index 000000000..76431fe24 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/deprecated.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/final_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/final_co.svg new file mode 100644 index 000000000..61c5103a6 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/final_co.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/implement_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/implement_co.svg new file mode 100644 index 000000000..d1325bddf --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/implement_co.svg @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/non_null_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/non_null_co.svg new file mode 100644 index 000000000..fec5a6692 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/non_null_co.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/nullable_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/nullable_co.svg new file mode 100644 index 000000000..92bdf7366 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/nullable_co.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/override_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/override_co.svg new file mode 100644 index 000000000..ca6269a86 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/override_co.svg @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/read_only_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/read_only_co.svg new file mode 100644 index 000000000..2aaecc516 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/read_only_co.svg @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/sealed_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/sealed_co.svg new file mode 100644 index 000000000..b235e7d44 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/sealed_co.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/static_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/static_co.svg new file mode 100644 index 000000000..a94d9c822 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/static_co.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/synch_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/synch_co.svg new file mode 100644 index 000000000..abea286be --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/synch_co.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/transient_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/transient_co.svg new file mode 100644 index 000000000..5f98881e1 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/transient_co.svg @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/virtual_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/virtual_co.svg new file mode 100644 index 000000000..eb3e4d528 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/virtual_co.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/vis_file_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/vis_file_co.svg new file mode 100644 index 000000000..f98b7d046 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/vis_file_co.svg @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/vis_internal_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/vis_internal_co.svg new file mode 100644 index 000000000..3fdc638c0 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/vis_internal_co.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/vis_package_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/vis_package_co.svg new file mode 100644 index 000000000..1af42b609 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/vis_package_co.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/vis_private_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/vis_private_co.svg new file mode 100644 index 000000000..12ff1c672 --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/vis_private_co.svg @@ -0,0 +1,4448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/vis_protected_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/vis_protected_co.svg new file mode 100644 index 000000000..cd68055ed --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/vis_protected_co.svg @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/vis_public_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/vis_public_co.svg new file mode 100644 index 000000000..14bb68faa --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/vis_public_co.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/icons/full/ovr16/volatile_co.svg b/org.eclipse.lsp4e/icons/full/ovr16/volatile_co.svg new file mode 100644 index 000000000..979eda22c --- /dev/null +++ b/org.eclipse.lsp4e/icons/full/ovr16/volatile_co.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/org.eclipse.lsp4e/pom.xml b/org.eclipse.lsp4e/pom.xml index e379bb9ca..8868cfff3 100644 --- a/org.eclipse.lsp4e/pom.xml +++ b/org.eclipse.lsp4e/pom.xml @@ -10,7 +10,7 @@ org.eclipse.lsp4e eclipse-plugin - 0.19.7-SNAPSHOT + 0.19.8-SNAPSHOT diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java index d2cd575c6..39d0e808c 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java @@ -67,8 +67,13 @@ public void start(BundleContext context) throws Exception { @Override public void stop(BundleContext context) throws Exception { plugin = null; - LanguageServiceAccessor.shutdownAllDispatchers(); - super.stop(context); + try { + LanguageServiceAccessor.shutdownAllDispatchers(); + LSPImages.dispose(); + } + finally { + super.stop(context); + } } /** 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 cc874037c..809a2ed88 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java @@ -16,7 +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.ui.LSPImages; +import org.eclipse.lsp4e.ui.SymbolIconProvider; import org.eclipse.lsp4j.CallHierarchyItem; import org.eclipse.swt.graphics.Image; @@ -25,11 +25,21 @@ */ 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 = LSPImages.imageFromSymbolKind(callContainer.getKind()); + Image res = symbolIconProvider.getImageFor(callContainer.getKind(), callContainer.getTags()); if (res != null) { return res; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java index f674b4eed..f8c3615c8 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java @@ -57,6 +57,8 @@ import org.eclipse.lsp4j.SymbolCapabilities; import org.eclipse.lsp4j.SymbolKind; import org.eclipse.lsp4j.SymbolKindCapabilities; +import org.eclipse.lsp4j.SymbolTag; +import org.eclipse.lsp4j.SymbolTagSupportCapabilities; import org.eclipse.lsp4j.SynchronizationCapabilities; import org.eclipse.lsp4j.TextDocumentClientCapabilities; import org.eclipse.lsp4j.TypeDefinitionCapabilities; @@ -120,6 +122,7 @@ public static TextDocumentClientCapabilities getTextDocumentClientCapabilities() final var documentSymbol = new DocumentSymbolCapabilities(); documentSymbol.setHierarchicalDocumentSymbolSupport(true); documentSymbol.setSymbolKind(new SymbolKindCapabilities(List.of(SymbolKind.values()))); + documentSymbol.setTagSupport(new SymbolTagSupportCapabilities(List.of(SymbolTag.values()))); textDocumentClientCapabilities.setDocumentSymbol(documentSymbol); final var foldingRangeCapabilities = new FoldingRangeCapabilities(); foldingRangeCapabilities.setLineFoldingOnly(true); @@ -153,7 +156,9 @@ public static WorkspaceClientCapabilities getWorkspaceClientCapabilities() { workspaceClientCapabilities.setApplyEdit(true); workspaceClientCapabilities.setConfiguration(true); workspaceClientCapabilities.setExecuteCommand(new ExecuteCommandCapabilities(true)); - workspaceClientCapabilities.setSymbol(new SymbolCapabilities(true)); + SymbolCapabilities symbolCapabilities = new SymbolCapabilities(true); + symbolCapabilities.setTagSupport(new SymbolTagSupportCapabilities(List.of(SymbolTag.values()))); + workspaceClientCapabilities.setSymbol(symbolCapabilities); workspaceClientCapabilities.setWorkspaceFolders(true); final var editCapabilities = new WorkspaceEditCapabilities(); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/SymbolsUtil.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/SymbolsUtil.java new file mode 100644 index 000000000..2dc99295a --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/SymbolsUtil.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * 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; + +import java.util.Collections; +import java.util.List; + +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; + +public class SymbolsUtil { + + public static SymbolKind getKind(SymbolInformation symbolInformation) { + return symbolInformation.getKind(); + } + + public static SymbolKind getKind(WorkspaceSymbol workspaceSymbol) { + return workspaceSymbol.getKind(); + } + + public static SymbolKind getKind(DocumentSymbol documentSymbol) { + return documentSymbol.getKind(); + } + + public static SymbolKind getKind(DocumentSymbolWithURI documentSymbolWithUri) { + return getKind(documentSymbolWithUri.symbol); + } + + public static List getSymbolTags(SymbolInformation symbolInformation) { + if (symbolInformation.getTags() != null) { + return symbolInformation.getTags(); + } + + return Collections.emptyList(); + } + + public static List getSymbolTags(WorkspaceSymbol workspaceSymbol) { + if (workspaceSymbol.getTags() != null) { + return workspaceSymbol.getTags(); + } + + return Collections.emptyList(); + } + + public static List getSymbolTags(DocumentSymbol documentSymbol) { + if (documentSymbol.getTags() != null) { + return documentSymbol.getTags(); + } + + return Collections.emptyList(); + } + + public static List getSymbolTags(DocumentSymbolWithURI documentSymbolWithUri) { + return getSymbolTags(documentSymbolWithUri.symbol); + } + + public static boolean hasSymbolTag(List tagList, SymbolTag tag) { + return tagList.contains(tag); + } + + public static boolean hasSymbolTag(SymbolInformation symbolInformation, SymbolTag tag) { + return getSymbolTags(symbolInformation).contains(tag); + } + + public static boolean hasSymbolTag(WorkspaceSymbol workspaceSymbol, SymbolTag tag) { + return getSymbolTags(workspaceSymbol).contains(tag); + } + + public static boolean hasSymbolTag(DocumentSymbol documentSymbol, SymbolTag tag) { + return getSymbolTags(documentSymbol).contains(tag); + } + + public static boolean hasSymbolTag(DocumentSymbolWithURI documentSymbolWithUri, SymbolTag tag) { + return getSymbolTags(documentSymbolWithUri).contains(tag); + } + + public static boolean isDeprecated(SymbolInformation symbolInformation) { + boolean deprecated = isDeprecated(getSymbolTags(symbolInformation)); + return deprecated || Boolean.TRUE.equals(symbolInformation.getDeprecated()); + } + + public static boolean isDeprecated(WorkspaceSymbol workspaceSymbol) { + return isDeprecated(getSymbolTags(workspaceSymbol)); + } + + public static boolean isDeprecated(DocumentSymbol documentSymbol) { + boolean deprecated = isDeprecated(getSymbolTags(documentSymbol)); + return deprecated || Boolean.TRUE.equals(documentSymbol.getDeprecated()); + } + + public static boolean isDeprecated(DocumentSymbolWithURI documentSymbolWithUri) { + return isDeprecated(documentSymbolWithUri.symbol); + } + + public static boolean isDeprecated(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Deprecated); + } + + public static boolean isPrivate(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Private); + } + + public static boolean isPackage(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Package); + } + + public static boolean isProtected(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Protected); + } + + public static boolean isPublic(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Public); + } + + public static boolean isInternal(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Internal); + } + + public static boolean isFileVisible(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.File); + } + + public static boolean isStatic(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Static); + } + + public static boolean isAbstract(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Abstract); + } + + public static boolean isFinal(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Final); + } + + public static boolean isSealed(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Sealed); + } + + public static boolean isTransient(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Transient); + } + + public static boolean isVolatile(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Volatile); + } + + public static boolean isSynchronized(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Synchronized); + } + + public static boolean isVirtual(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Virtual); + } + + public static boolean isNullable(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Nullable); + } + + public static boolean isNonNull(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.NonNull); + } + + public static boolean isDeclaration(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Declaration); + } + + public static boolean isDefinition(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Definition); + } + + public static boolean isReadOnly(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.ReadOnly); + } + + public static boolean isOverrides(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Overrides); + } + + public static boolean isImplements(List tags) { + return SymbolsUtil.hasSymbolTag(tags, SymbolTag.Implements); + } + +} 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 1cea86359..4d426704e 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,12 +12,22 @@ import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; -import org.eclipse.lsp4e.ui.LSPImages; +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) { @@ -29,7 +39,7 @@ public String getText(Object element) { @Override public @Nullable Image getImage(@Nullable Object element) { if (element instanceof TypeHierarchyItem item) { - return LSPImages.imageFromSymbolKind(item.getKind()); + return symbolIconProvider.getImageFor(item.getKind(), item.getTags()); } 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 69720ffbc..e981fc337 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java @@ -8,19 +8,20 @@ * * Contributors: * Mickael Istria (Red Hat Inc.) - initial implementation + * Dietrich Travkin (SOLUNAR GmbH) - Add overlay icons for new symbol tags *******************************************************************************/ package org.eclipse.lsp4e.outline; import static org.eclipse.lsp4e.LSPEclipseUtils.findResourceFor; -import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNullable; import java.net.URI; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; @@ -35,9 +36,7 @@ import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.viewers.DecorationOverlayIcon; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; -import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.LabelProviderChangedEvent; @@ -45,9 +44,11 @@ import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.internal.StyleUtil; +import org.eclipse.lsp4e.operations.symbols.SymbolsUtil; import org.eclipse.lsp4e.outline.SymbolsModel.DocumentSymbolWithURI; import org.eclipse.lsp4e.ui.LSPImages; import org.eclipse.lsp4e.ui.Messages; +import org.eclipse.lsp4e.ui.SymbolIconProvider; import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Range; @@ -71,6 +72,8 @@ 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 { @@ -87,11 +90,7 @@ public class SymbolsLabelProvider extends LabelProvider LanguageServerPlugin.logError(ex); } }; - /* - * key: initial object image - * value: array of images decorated with marker for severity (index + 1) - */ - private final Map overlays = new HashMap<>(); + private final Map resourceCache = new HashMap<>(); private final boolean showLocation; @@ -103,9 +102,14 @@ public SymbolsLabelProvider() { .getBoolean(CNFOutlinePage.SHOW_KIND_PREFERENCE, false)); } - public SymbolsLabelProvider(boolean showLocation, boolean showKind) { + 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); } @@ -114,14 +118,16 @@ public SymbolsLabelProvider(boolean showLocation, boolean showKind) { public void dispose() { ResourcesPlugin.getWorkspace().removeResourceChangeListener(listener); InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID).removePreferenceChangeListener(this); - overlays.values().stream().flatMap(Arrays::stream).filter(Objects::nonNull).forEach(Image::dispose); - overlays.clear(); super.dispose(); } @Override - public @Nullable Image getImage(@Nullable Object element) { - if (element == null) { + public @Nullable Image getImage(final @Nullable Object element) { + // If needed, we could use more overlays like in org.eclipse.jdt.ui.JavaElementImageDescriptor, + // but this would demand more space in various views. + // See guidelines, Section "Icon Overlays": https://www.eclipse.org/articles/Article-UI-Guidelines/Contents.html + + if (element == null){ return null; } if (element instanceof PendingUpdateAdapter) { @@ -130,29 +136,60 @@ public void dispose() { if (element instanceof Throwable) { return LSPImages.getSharedImage(ISharedImages.IMG_OBJS_ERROR_TSK); } + + var actualElement = element; if (element instanceof Either either) { - element = either.get(); + actualElement = either.get(); + } + SymbolKind symbolKind = null; + List symbolTags = Collections.emptyList(); + boolean deprecated = false; + if (actualElement instanceof SymbolInformation info) { + symbolKind = SymbolsUtil.getKind(info); + symbolTags = SymbolsUtil.getSymbolTags(info); + deprecated = SymbolsUtil.isDeprecated(info); + } else if (actualElement instanceof WorkspaceSymbol symbol) { + symbolKind = SymbolsUtil.getKind(symbol); + symbolTags = SymbolsUtil.getSymbolTags(symbol); + deprecated = SymbolsUtil.isDeprecated(symbol); + } else if (actualElement instanceof DocumentSymbol symbol) { + symbolKind = SymbolsUtil.getKind(symbol); + symbolTags = SymbolsUtil.getSymbolTags(symbol); + deprecated = SymbolsUtil.isDeprecated(symbol); + } else if (actualElement instanceof DocumentSymbolWithURI symbolWithURI) { + symbolKind = SymbolsUtil.getKind(symbolWithURI); + symbolTags = SymbolsUtil.getSymbolTags(symbolWithURI); + deprecated = SymbolsUtil.isDeprecated(symbolWithURI); + } + + if (deprecated && !symbolTags.contains(SymbolTag.Deprecated)) { + var adaptedTags = new ArrayList(symbolTags.size() + 1); + adaptedTags.addAll(symbolTags); + adaptedTags.add(SymbolTag.Deprecated); + symbolTags = adaptedTags; } - Image image = null; + if (actualElement != null && symbolKind != null) { + return symbolIconProvider.getImageFor(symbolKind, symbolTags, getMaxSeverity(actualElement)); + } + + return null; + } + + private int getMaxSeverity(final Object element) { IResource file = null; if (element instanceof SymbolInformation info) { - image = LSPImages.imageFromSymbolKind(info.getKind()); file = resourceCache.computeIfAbsent(info.getLocation().getUri(), uri -> findResourceFor((String) uri)); } else if (element instanceof WorkspaceSymbol symbol) { - image = LSPImages.imageFromSymbolKind(symbol.getKind()); file = resourceCache.computeIfAbsent(getUri(symbol), uri -> findResourceFor((String) uri)); - } else if (element instanceof DocumentSymbol symbol) { - image = LSPImages.imageFromSymbolKind(symbol.getKind()); } else if (element instanceof DocumentSymbolWithURI symbolWithURI) { - image = LSPImages.imageFromSymbolKind(symbolWithURI.symbol.getKind()); file = resourceCache.computeIfAbsent(symbolWithURI.uri, uri -> findResourceFor((URI) uri)); } /* * Implementation node: for problem decoration, maybe consider using a ILabelDecorator/IDelayedLabelDecorator? */ - if (file != null && image != null) { + if (file != null) { Range range = null; if (element instanceof SymbolInformation symbol) { range = symbol.getLocation().getRange(); @@ -163,6 +200,7 @@ public void dispose() { } else if (element instanceof DocumentSymbolWithURI symbolWithURI) { range = symbolWithURI.symbol.getRange(); } + if (range != null) { try { // use existing documents only to calculate the severity @@ -171,20 +209,17 @@ public void dispose() { IDocument doc = LSPEclipseUtils.getExistingDocument(file); if (doc != null) { - int maxSeverity = getMaxSeverity(file, doc, range); - if (maxSeverity > IMarker.SEVERITY_INFO) { - return getOverlay(image, maxSeverity); - } + return getMaxSeverity(file, doc, range); } } catch (CoreException | BadLocationException e) { LanguageServerPlugin.logError(e); } } } - return image; + return -1; } - protected int getMaxSeverity(IResource resource, IDocument doc, Range range) + protected int getMaxSeverity(final IResource resource, final IDocument doc, final Range range) throws CoreException, BadLocationException { if (!severities.containsKey(resource)) { refreshMarkersByLine(resource); @@ -207,7 +242,7 @@ protected int getMaxSeverity(IResource resource, IDocument doc, Range range) .orElse(-1); } - private void refreshMarkersByLine(IResource resource) throws CoreException { + private void refreshMarkersByLine(final IResource resource) throws CoreException { RangeMap rangeMap = TreeRangeMap.create(); Arrays.stream(resource.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_ZERO)) .filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) > IMarker.SEVERITY_INFO) @@ -228,31 +263,13 @@ private void refreshMarkersByLine(IResource resource) throws CoreException { severities.put(resource, rangeMap); } - private Image getOverlay(Image res, int maxSeverity) { - if (maxSeverity != 1 && maxSeverity != 2) { - throw new IllegalArgumentException("Severity " + maxSeverity + " not supported."); //$NON-NLS-1$ //$NON-NLS-2$ - } - Image[] currentOverlays = this.overlays.computeIfAbsent(res, key -> new Image [2]); - if (castNullable(currentOverlays[maxSeverity - 1]) == null) { - String overlayId = null; - if (maxSeverity == IMarker.SEVERITY_ERROR) { - overlayId = ISharedImages.IMG_DEC_FIELD_ERROR; - } else if (maxSeverity == IMarker.SEVERITY_WARNING) { - overlayId = ISharedImages.IMG_DEC_FIELD_WARNING; - } - currentOverlays[maxSeverity - 1] = new DecorationOverlayIcon(res, - LSPImages.getSharedImageDescriptor(overlayId), IDecoration.BOTTOM_LEFT).createImage(); - } - return currentOverlays[maxSeverity - 1]; - } - @Override - public String getText(Object element) { + public String getText(final Object element) { return getStyledText(element).getString(); } @Override - public StyledString getStyledText(@Nullable Object element) { + public StyledString getStyledText(final @Nullable Object element) { if (element instanceof PendingUpdateAdapter) { return new StyledString(Messages.outline_computingSymbols); @@ -268,44 +285,46 @@ public StyledString getStyledText(@Nullable Object element) { if (element == null){ return res; } + + var actualElement = element; if (element instanceof Either either) { - element = either.get(); + actualElement = either.get(); } String name = null; SymbolKind kind = null; String detail = null; URI location = null; boolean deprecated = false; - if (element instanceof SymbolInformation symbolInformation) { + if (actualElement instanceof SymbolInformation symbolInformation) { name = symbolInformation.getName(); kind = symbolInformation.getKind(); - deprecated = isDeprecated(symbolInformation.getTags()) || symbolInformation.getDeprecated() != null && symbolInformation.getDeprecated(); + deprecated = SymbolsUtil.isDeprecated(symbolInformation); try { location = URI.create(symbolInformation.getLocation().getUri()); } catch (IllegalArgumentException e) { LanguageServerPlugin.logError("Invalid URI: " + symbolInformation.getLocation().getUri(), e); //$NON-NLS-1$ } - } else if (element instanceof WorkspaceSymbol workspaceSymbol) { + } else if (actualElement instanceof WorkspaceSymbol workspaceSymbol) { name = workspaceSymbol.getName(); kind = workspaceSymbol.getKind(); String rawUri = getUri(workspaceSymbol); - deprecated = isDeprecated(workspaceSymbol.getTags()); + deprecated = SymbolsUtil.isDeprecated(workspaceSymbol); try { location = URI.create(rawUri); } catch (IllegalArgumentException e) { LanguageServerPlugin.logError("Invalid URI: " + rawUri, e); //$NON-NLS-1$ } - } else if (element instanceof DocumentSymbol documentSymbol) { + } else if (actualElement instanceof DocumentSymbol documentSymbol) { name = documentSymbol.getName(); kind = documentSymbol.getKind(); detail = documentSymbol.getDetail(); - deprecated = isDeprecated(documentSymbol.getTags()) || documentSymbol.getDeprecated() != null && documentSymbol.getDeprecated(); - } else if (element instanceof DocumentSymbolWithURI symbolWithURI) { + deprecated = SymbolsUtil.isDeprecated(documentSymbol); + } else if (actualElement instanceof DocumentSymbolWithURI symbolWithURI) { name = symbolWithURI.symbol.getName(); kind = symbolWithURI.symbol.getKind(); detail = symbolWithURI.symbol.getDetail(); location = symbolWithURI.uri; - deprecated = isDeprecated(symbolWithURI.symbol.getTags()) || symbolWithURI.symbol.getDeprecated() != null && symbolWithURI.symbol.getDeprecated(); + deprecated = SymbolsUtil.isDeprecated(symbolWithURI); } if (name != null) { if (deprecated) { @@ -332,13 +351,6 @@ public StyledString getStyledText(@Nullable Object element) { return res; } - private boolean isDeprecated(@Nullable List tags) { - if(tags != null){ - return tags.contains(SymbolTag.Deprecated); - } - return false; - } - @Override public void restoreState(final IMemento aMemento) { } @@ -357,7 +369,7 @@ public void init(final ICommonContentExtensionSite aConfig) { } @Override - public void preferenceChange(PreferenceChangeEvent event) { + public void preferenceChange(final PreferenceChangeEvent event) { if (event.getKey().equals(CNFOutlinePage.SHOW_KIND_PREFERENCE)) { this.showKind = Boolean.parseBoolean(String.valueOf(event.getNewValue())); for (Object listener : this.getListeners()) { @@ -368,7 +380,7 @@ public void preferenceChange(PreferenceChangeEvent event) { } } - private static String getUri(WorkspaceSymbol symbol) { + private static String getUri(final WorkspaceSymbol symbol) { return symbol.getLocation().map(Location::getUri, WorkspaceSymbolLocation::getUri); } 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 d371adfb6..df3106af0 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java @@ -8,12 +8,16 @@ * * Contributors: * Michał Niewrzał (Rogue Wave Software Inc.) - initial implementation + * Dietrich Travkin (Solunar GmbH) - add overlay images computation, dispose cached images *******************************************************************************/ package org.eclipse.lsp4e.ui; import java.net.URL; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.Path; @@ -21,10 +25,12 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.jface.viewers.DecorationOverlayIcon; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.SymbolKind; +import org.eclipse.lsp4j.SymbolTag; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; @@ -40,10 +46,26 @@ private LSPImages() { } private static @Nullable ImageRegistry imageRegistry; + private static final Map colorToImageCache = new HashMap<>(); + + /** + *

Cache for symbol images with various overlays and / or an underlay.

+ *
    + *
  • Key: a combined key based on the element's kind (e.g. class or method) + * and a set of overlay image descriptors for the icon corners and the underlay,
  • + *
  • Value: the base image with optional overlays and an optional underlay combined in one image.
  • + *
+ * + * See {@link #getImageWithOverlays(String, ImageDescriptor, ImageDescriptor, ImageDescriptor, ImageDescriptor, ImageDescriptor)} + */ + private static final Map overlayImagesCache = new HashMap<>(); + private static final String ICONS_PATH = "$nl$/icons/full/"; //$NON-NLS-1$ private static final String OBJECT = ICONS_PATH + "obj16/"; // basic colors - size 16x16 //$NON-NLS-1$ private static final String ACTION = ICONS_PATH + "elcl16/"; // basic colors - size 16x16 //$NON-NLS-1$ + private static final String OVERLAY = ICONS_PATH + "ovr16/"; // basic colors - size 7x8 and 14x16 //$NON-NLS-1$ + private static final Image EMPTY_IMAGE = new Image(UI.getDisplay(), 16, 16); public static final String IMG_MODULE = "IMG_MODULE"; //$NON-NLS-1$ @@ -52,8 +74,20 @@ private LSPImages() { public static final String IMG_CLASS = "IMG_CLASS"; //$NON-NLS-1$ public static final String IMG_TYPE_PARAMETER = "IMG_TYPE_PARAMETER"; //$NON-NLS-1$ public static final String IMG_METHOD = "IMG_METOHD"; //$NON-NLS-1$ + public static final String IMG_METHOD_VIS_FILE = "IMG_METH_FILE"; //$NON-NLS-1$ + public static final String IMG_METHOD_VIS_INTERNAL = "IMG_METH_INTERNAL"; //$NON-NLS-1$ + public static final String IMG_METHOD_VIS_PRIVATE = "IMG_METH_PRIVATE"; //$NON-NLS-1$ + public static final String IMG_METHOD_VIS_PACKAGE = "IMG_METH_PACKAGE"; //$NON-NLS-1$ + public static final String IMG_METHOD_VIS_PROTECTED = "IMG_METH_PROTECTED"; //$NON-NLS-1$ + public static final String IMG_METHOD_VIS_PUBLIC = "IMG_METH_PUBLIC"; //$NON-NLS-1$ public static final String IMG_PROPERTY = "IMG_PROPERTY"; //$NON-NLS-1$ public static final String IMG_FIELD = "IMG_FIELD"; //$NON-NLS-1$ + public static final String IMG_FIELD_VIS_FILE = "IMG_FIELD_FILE"; //$NON-NLS-1$ + public static final String IMG_FIELD_VIS_INTERNAL = "IMG_FIELD_INTERNAL"; //$NON-NLS-1$ + public static final String IMG_FIELD_VIS_PRIVATE = "IMG_FIELD_PRIVATE"; //$NON-NLS-1$ + public static final String IMG_FIELD_VIS_PACKAGE = "IMG_FIELD_PACKAGE"; //$NON-NLS-1$ + public static final String IMG_FIELD_VIS_PROTECTED = "IMG_FIELD_PROTECTED"; //$NON-NLS-1$ + public static final String IMG_FIELD_VIS_PUBLIC = "IMG_FIELD_PUBLIC"; //$NON-NLS-1$ public static final String IMG_CONSTRUCTOR = "IMG_CONSTRUCTOR"; //$NON-NLS-1$ public static final String IMG_ENUM = "IMG_ENUM"; //$NON-NLS-1$ public static final String IMG_ENUM_MEMBER = "IMG_ENUM_MEMBER"; //$NON-NLS-1$ @@ -81,6 +115,31 @@ private LSPImages() { public static final String IMG_SUPERTYPE = "IMG_SUPERTYPE"; //$NON-NLS-1$ public static final String IMG_SUBTYPE = "IMG_SUBTYPE"; //$NON-NLS-1$ + public static final String IMG_OVR_CONSTRUCTOR = "IMG_OVR_CONSTRUCTOR"; //$NON-NLS-1$ + public static final String IMG_OVR_DEPRECATED = "IMG_OVR_DEPRECATED"; //$NON-NLS-1$ + public static final String IMG_OVR_PRIVATE = "IMG_OVR_PRIVATE"; //$NON-NLS-1$ + public static final String IMG_OVR_PACKAGE = "IMG_OVR_PACKAGE"; //$NON-NLS-1$ + public static final String IMG_OVR_PROTECTED = "IMG_OVR_PROTECTED"; //$NON-NLS-1$ + public static final String IMG_OVR_PUBLIC = "IMG_OVR_PUBLIC"; //$NON-NLS-1$ + public static final String IMG_OVR_INTERNAL = "IMG_OVR_INTERNAL"; //$NON-NLS-1$ + public static final String IMG_OVR_FILE_VIS = "IMG_OVR_FILE_VIS"; //$NON-NLS-1$ + public static final String IMG_OVR_ABSTRACT = "IMG_OVR_ABSTRACT"; //$NON-NLS-1$ + public static final String IMG_OVR_VIRTUAL = "IMG_OVR_VIRTUAL"; //$NON-NLS-1$ + public static final String IMG_OVR_FINAL = "IMG_OVR_FINAL"; //$NON-NLS-1$ + public static final String IMG_OVR_SEALED = "IMG_OVR_SEALED"; //$NON-NLS-1$ + public static final String IMG_OVR_STATIC = "IMG_OVR_STATIC"; //$NON-NLS-1$ + public static final String IMG_OVR_SYNC = "IMG_OVR_SYNC"; //$NON-NLS-1$ + public static final String IMG_OVR_TRANSIENT = "IMG_OVR_TRANSIENT"; //$NON-NLS-1$ + public static final String IMG_OVR_VOLATILE = "IMG_OVR_VOLATILE"; //$NON-NLS-1$ + public static final String IMG_OVR_NULLABLE = "IMG_OVR_NULLABLE"; //$NON-NLS-1$ + public static final String IMG_OVR_NON_NULL = "IMG_OVR_NON_NULL"; //$NON-NLS-1$ + public static final String IMG_OVR_DECLARATION = "IMG_OVR_DECLARATION"; //$NON-NLS-1$ + public static final String IMG_OVR_DEFINITION = "IMG_OVR_DEFINITION"; //$NON-NLS-1$ + public static final String IMG_OVR_READ_ONLY = "IMG_OVR_READ_ONLY"; //$NON-NLS-1$ + public static final String IMG_OVR_IMPLEMENT = "IMG_OVR_IMPLEMENT"; //$NON-NLS-1$ + public static final String IMG_OVR_OVERRIDE = "IMG_OVR_OVERRIDE"; //$NON-NLS-1$ + + public static void initalize(ImageRegistry registry) { imageRegistry = registry; @@ -90,8 +149,24 @@ public static void initalize(ImageRegistry registry) { declareRegistryImage(IMG_CLASS, OBJECT + "class.svg"); //$NON-NLS-1$ declareRegistryImage(IMG_TYPE_PARAMETER, OBJECT + "type_parameter.svg"); //$NON-NLS-1$ declareRegistryImage(IMG_METHOD, OBJECT + "method.svg"); //$NON-NLS-1$ + + declareRegistryImage(IMG_METHOD_VIS_FILE, OBJECT + "method_file_vis_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_METHOD_VIS_INTERNAL, OBJECT + "method_internal_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_METHOD_VIS_PRIVATE, OBJECT + "method_private_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_METHOD_VIS_PACKAGE, OBJECT + "method_package_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_METHOD_VIS_PROTECTED, OBJECT + "method_protected_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_METHOD_VIS_PUBLIC, OBJECT + "method_public_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_PROPERTY, OBJECT + "property.svg"); //$NON-NLS-1$ declareRegistryImage(IMG_FIELD, OBJECT + "field.svg"); //$NON-NLS-1$ + + declareRegistryImage(IMG_FIELD_VIS_FILE, OBJECT + "field_file_vis_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_FIELD_VIS_INTERNAL, OBJECT + "field_internal_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_FIELD_VIS_PRIVATE, OBJECT + "field_private_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_FIELD_VIS_PACKAGE, OBJECT + "field_package_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_FIELD_VIS_PROTECTED, OBJECT + "field_protected_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_FIELD_VIS_PUBLIC, OBJECT + "field_public_obj.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_CONSTRUCTOR, OBJECT + "constructor.svg"); //$NON-NLS-1$ declareRegistryImage(IMG_ENUM, OBJECT + "enum.svg"); //$NON-NLS-1$ declareRegistryImage(IMG_ENUM_MEMBER, OBJECT + "enum_member.svg"); //$NON-NLS-1$ @@ -118,6 +193,32 @@ public static void initalize(ImageRegistry registry) { declareRegistryImage(IMG_SUPERTYPE, ACTION + "super_co.svg"); //$NON-NLS-1$ declareRegistryImage(IMG_SUBTYPE, ACTION + "sub_co.svg"); //$NON-NLS-1$ declareRegistryImage(IMG_TERMINATE_CO, OBJECT + "terminate_co.svg"); //$NON-NLS-1$ + + declareRegistryImage(IMG_OVR_CONSTRUCTOR, OVERLAY + "constr_ovr.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_DEPRECATED, OVERLAY + "deprecated.svg"); //$NON-NLS-1$ + + declareRegistryImage(IMG_OVR_PRIVATE, OVERLAY + "vis_private_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_PACKAGE, OVERLAY + "vis_package_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_PROTECTED, OVERLAY + "vis_protected_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_PUBLIC, OVERLAY + "vis_public_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_INTERNAL, OVERLAY + "vis_internal_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_FILE_VIS, OVERLAY + "vis_file_co.svg"); //$NON-NLS-1$ + + declareRegistryImage(IMG_OVR_ABSTRACT, OVERLAY + "abstract_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_VIRTUAL, OVERLAY + "virtual_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_FINAL, OVERLAY + "final_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_SEALED, OVERLAY + "sealed_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_STATIC, OVERLAY + "static_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_SYNC, OVERLAY + "synch_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_TRANSIENT, OVERLAY + "transient_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_VOLATILE, OVERLAY + "volatile_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_NULLABLE, OVERLAY + "nullable_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_NON_NULL, OVERLAY + "non_null_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_DECLARATION, OVERLAY + "declaration_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_DEFINITION, OVERLAY + "definition_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_READ_ONLY, OVERLAY + "read_only_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_IMPLEMENT, OVERLAY + "implement_co.svg"); //$NON-NLS-1$ + declareRegistryImage(IMG_OVR_OVERRIDE, OVERLAY + "override_co.svg"); //$NON-NLS-1$ } private static void declareRegistryImage(String key, String path) { @@ -133,6 +234,11 @@ private static void declareRegistryImage(String key, String path) { getImageRegistry().put(key, desc); } + private record ImageWithOverlaysKey(String baseImageKey, + @Nullable ImageDescriptor overlayTopLeftDescriptor, @Nullable ImageDescriptor overlayTopRightDescriptor, + @Nullable ImageDescriptor overlayBottomLeftDescriptor, @Nullable ImageDescriptor overlayBottomRightDescriptor, + @Nullable ImageDescriptor underlayDescriptor) {} + /** * Returns the Image identified by the given key, or null if it does not exist. */ @@ -177,35 +283,81 @@ public static ImageRegistry getImageRegistry() { return PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(imageId); } + /** + * Returns an image representing the given symbol kind. Does not consider the document symbol's visibility + * (given by {@link SymbolTag}s). + * + * @param kind a document symbol's kind + * @return an image representing the given symbol kind or null + * + * @see #getImage(String)) + * @see #getImageDescriptor(String) + * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List) + * @see SymbolIconProvider#getImageFor(SymbolKind, java.util.List, int) + */ public static @Nullable Image imageFromSymbolKind(@Nullable SymbolKind kind) { + if (kind == null) { + return EMPTY_IMAGE; + } + + String imgKey = imageKeyFromSymbolKind(kind); + if (ISharedImages.IMG_OBJ_FILE.equals(imgKey)) { + return getSharedImage(imgKey); + } + + return getImage(imgKey); + } + + public static @Nullable ImageDescriptor imageDescriptorFromSymbolKind(SymbolKind kind) { + String imgKey = imageKeyFromSymbolKind(kind); + if (ISharedImages.IMG_OBJ_FILE.equals(imgKey)) { + return getSharedImageDescriptor(imgKey); + } + + return getImageDescriptor(imgKey); + } + + /** + * Returns an image identifier (key) that can be used with the image registry or in {@link #getImage(String)} and + * {@link #getImageDescriptor(String)} to retrieve the image representing the given document symbol kind. + * Does not consider any visibility details given in a document symbol's {@link SymbolTag}s. + * + * @param kind a document symbol's kind + * @return an image identifier (key) representing the given symbol kind's corresponding image + * + * @see #getImage(String) + * @see #getImageDescriptor(String) + * @see #getImageWithOverlays(String, ImageDescriptor, ImageDescriptor, ImageDescriptor, ImageDescriptor, ImageDescriptor) + * @see SymbolIconProvider#getImageKeyFromSymbolKindWithVisibility(SymbolKind, java.util.List) + */ + public static String imageKeyFromSymbolKind(SymbolKind kind) { return switch (kind) { - case Array -> getImage(IMG_ARRAY); - case Boolean -> getImage(IMG_BOOLEAN); - case Class -> getImage(IMG_CLASS); - case Constant -> getImage(IMG_CONSTANT); - case Constructor -> getImage(IMG_CONSTRUCTOR); - case Enum -> getImage(IMG_ENUM); - case EnumMember -> getImage(IMG_ENUM_MEMBER); - case Struct -> getImage(IMG_STRUCT); - case Field -> getImage(IMG_FIELD); - case File -> getSharedImage(ISharedImages.IMG_OBJ_FILE); - case Function -> getImage(IMG_FUNCTION); - case Interface -> getImage(IMG_INTERACE); - case Method -> getImage(IMG_METHOD); - case Module -> getImage(IMG_MODULE); - case Namespace -> getImage(IMG_NAMESPACE); - case Number -> getImage(IMG_NUMBER); - case Object -> getImage(IMG_OBJECT); - case Package -> getImage(IMG_PACKAGE); - case Property -> getImage(IMG_PROPERTY); - case String -> getImage(IMG_TEXT); - case TypeParameter -> getImage(IMG_TYPE_PARAMETER); - case Variable -> getImage(IMG_VARIABLE); - case Null -> getImage(IMG_NULL); - case Event -> getImage(IMG_EVENT); - case Key -> getImage(IMG_KEY); - case Operator -> getImage(IMG_OPERATOR); - case null -> EMPTY_IMAGE; + case Array -> IMG_ARRAY; + case Boolean -> IMG_BOOLEAN; + case Class -> IMG_CLASS; + case Constant -> IMG_CONSTANT; + case Constructor -> IMG_CONSTRUCTOR; + case Enum -> IMG_ENUM; + case EnumMember -> IMG_ENUM_MEMBER; + case Struct -> IMG_STRUCT; + case Field -> IMG_FIELD; + case File -> ISharedImages.IMG_OBJ_FILE; + case Function -> IMG_FUNCTION; + case Interface -> IMG_INTERACE; + case Method -> IMG_METHOD; + case Module -> IMG_MODULE; + case Namespace -> IMG_NAMESPACE; + case Number -> IMG_NUMBER; + case Object -> IMG_OBJECT; + case Package -> IMG_PACKAGE; + case Property -> IMG_PROPERTY; + case String -> IMG_TEXT; + case TypeParameter -> IMG_TYPE_PARAMETER; + case Variable -> IMG_VARIABLE; + case Null -> IMG_NULL; + case Event -> IMG_EVENT; + case Key -> IMG_KEY; + case Operator -> IMG_OPERATOR; }; } @@ -240,6 +392,52 @@ public static ImageRegistry getImageRegistry() { }; } + /** + * Returns an overlay icon ({@link Image}) representing the given symbol tag, + * e.g. a document symbol's visibility "private" or the symbol being "static" or "final". + * + * @param symbolTag a document symbol's tag to be represented as an overlay icon + * @return the overlay icon corresponding to the given tag or null if the image cannot be found + * + * @see #imageDescriptorOverlayFromSymbolTag(SymbolTag) + */ + public static @Nullable Image imageOverlayFromSymbolTag(SymbolTag symbolTag) { + String imgKey = imageOverlayKeyFromSymbolTag(symbolTag); + return getImage(imgKey); + } + + public static @Nullable ImageDescriptor imageDescriptorOverlayFromSymbolTag(SymbolTag symbolTag) { + String imgKey = imageOverlayKeyFromSymbolTag(symbolTag); + return getImageDescriptor(imgKey); + } + + private static String imageOverlayKeyFromSymbolTag(SymbolTag symbolTag) { + return switch (symbolTag) { + case Deprecated -> IMG_OVR_DEPRECATED; + case Private -> IMG_OVR_PRIVATE; + case Package -> IMG_OVR_PACKAGE; + case Protected -> IMG_OVR_PROTECTED; + case Public -> IMG_OVR_PUBLIC; + case Internal -> IMG_OVR_INTERNAL; + case File -> IMG_OVR_FILE_VIS; + case Static -> IMG_OVR_STATIC; + case Abstract -> IMG_OVR_ABSTRACT; + case Final -> IMG_OVR_FINAL; + case Sealed -> IMG_OVR_SEALED; + case Transient -> IMG_OVR_TRANSIENT; + case Volatile -> IMG_OVR_VOLATILE; + case Synchronized -> IMG_OVR_SYNC; + case Virtual -> IMG_OVR_VIRTUAL; + case Nullable -> IMG_OVR_NULLABLE; + case NonNull -> IMG_OVR_NON_NULL; + case Declaration -> IMG_OVR_DECLARATION; + case Definition -> IMG_OVR_DEFINITION; + case ReadOnly -> IMG_OVR_READ_ONLY; + case Overrides -> IMG_OVR_OVERRIDE; + case Implements -> IMG_OVR_IMPLEMENT; + }; + } + private static @Nullable Image getImageForColor(CompletionItem completionItem) { String hexValue = null; @@ -274,4 +472,68 @@ public static ImageRegistry getImageRegistry() { return image; }); } + + /** + * Returns a new or cached image built from the given arguments. + * The image is a combination of a base image with optional overlay and underlay icons. + * + * @param baseImageKey the image identifier given by e.g. {@link #imageKeyFromSymbolKind(SymbolKind)} + * @param topLeftOverlayDescriptor the overlay icon descriptor for the upper left corner of the base icon + * @param topRightOverlayDescriptor the overlay icon descriptor for the upper right corner of the base icon + * @param bottomLeftOverlayDescriptor the overlay icon descriptor for the lower left corner of the base icon + * @param bottomRightOverlayDescriptor the overlay icon descriptor for the lower right corner of the base icon + * @param underlayImageDescriptor the underlay icon descriptor for being drawn behind the the base icon + * @return returns a new or cached image built from the given arguments. + * + * @see #imageKeyFromSymbolKind(SymbolKind) + * @see SymbolIconProvider#getImageKeyFromSymbolKindWithVisibility(SymbolKind, java.util.List) + */ + public static @Nullable Image getImageWithOverlays(String baseImageKey, + @Nullable ImageDescriptor topLeftOverlayDescriptor, @Nullable ImageDescriptor topRightOverlayDescriptor, + @Nullable ImageDescriptor bottomLeftOverlayDescriptor, @Nullable ImageDescriptor bottomRightOverlayDescriptor, + @Nullable ImageDescriptor underlayImageDescriptor) { + + // array index: 0 = top left, 1 = top right, 2 = bottom left, 3 = bottom right, 4 = underlay + // see IDecoration.TOP_LEFT ... IDecoration.BOTTOM_RIGHT, IDecoration.UNDERLAY + @Nullable ImageDescriptor[] overlays = { + topLeftOverlayDescriptor, topRightOverlayDescriptor, + bottomLeftOverlayDescriptor, bottomRightOverlayDescriptor, + underlayImageDescriptor}; + + long numOverlays = Arrays.stream(overlays) + .filter(Objects::nonNull) + .count(); + if (numOverlays == 0L) { + return getImage(baseImageKey); + } + + ImageWithOverlaysKey key = new ImageWithOverlaysKey(baseImageKey, + topLeftOverlayDescriptor, topRightOverlayDescriptor, + bottomLeftOverlayDescriptor, bottomRightOverlayDescriptor, underlayImageDescriptor); + + if (overlayImagesCache.containsKey(key)) { + return overlayImagesCache.get(key); + } + + final Image baseImage = getImage(baseImageKey); + if (baseImage == null) { + // Do not create cache entries for non existing base images + return null; + } + + Image imageWithOverlays = new DecorationOverlayIcon(baseImage, overlays).createImage(); + overlayImagesCache.put(key, imageWithOverlays); + + return imageWithOverlays; + } + + public static final void dispose() { + Stream.concat( + colorToImageCache.values().stream(), + overlayImagesCache.values().stream()) + .filter(Objects::nonNull) + .forEach(Image::dispose); + overlayImagesCache.clear(); + colorToImageCache.clear(); + } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java new file mode 100644 index 000000000..c675253ce --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/SymbolIconProvider.java @@ -0,0 +1,272 @@ +/******************************************************************************* + * 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.ui; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.lsp4e.operations.symbols.SymbolsUtil; +import org.eclipse.lsp4j.SymbolKind; +import org.eclipse.lsp4j.SymbolTag; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.ISharedImages; + + +/** + * Customizable class for creating document symbol icons with overlays depending on the symbol's + * {@link SymbolKind} and {@link SymbolTag}s. This class is meant to be used with {@link LabelProvider}s. + */ +public class SymbolIconProvider { + + /** + * Returns an overlay icon {@link ImageDescriptor} for the given severity. + * + * @param severity one of IMarker.SEVERITY_ERROR or IMarker.SEVERITY_WARNING + * @return image descriptor for a warning or an error, or null in all other cases + */ + protected @Nullable ImageDescriptor getOverlayForMarkerSeverity(int severity) { + return switch(severity) { + case IMarker.SEVERITY_ERROR -> LSPImages.getSharedImageDescriptor(ISharedImages.IMG_DEC_FIELD_ERROR); + case IMarker.SEVERITY_WARNING -> LSPImages.getSharedImageDescriptor(ISharedImages.IMG_DEC_FIELD_WARNING); + default -> null; + }; + } + + /** + * Returns an underlay icon {@link ImageDescriptor} if the given argument is true, null otherwise. + * + * @param deprecated if a symbol is deprecated + * @return a deprecation underlay icon or null + */ + protected @Nullable ImageDescriptor getUnderlayForDeprecation(boolean deprecated) { + if (!deprecated) { + return null; + } + return LSPImages.imageDescriptorOverlayFromSymbolTag(SymbolTag.Deprecated); + } + + private static final List VISIBILITY_PRECEDENCE = List.of( + SymbolTag.Public, SymbolTag.Protected, SymbolTag.Package, + SymbolTag.Internal, SymbolTag.File, SymbolTag.Private); + + /** + * Returns a list of visibility {@link SymbolTag}s with decreasing precedence. + * May be overridden by subclasses to change the visibility overlay icons shown. + * + * @return a list of visibility {@link SymbolTag}s + */ + protected List getVisibilityPrecedence() { + return VISIBILITY_PRECEDENCE; + } + + // 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( + SymbolTag.Static, SymbolTag.Final, SymbolTag.Abstract, + SymbolTag.Overrides, SymbolTag.Implements, SymbolTag.Virtual, SymbolTag.Sealed, + SymbolTag.Synchronized, SymbolTag.Transient, SymbolTag.Volatile, + SymbolTag.ReadOnly); + + /** + * Returns a list of {@link SymbolTag}s excluding visibility and deprecation tags with decreasing precedence. + * May be overridden by subclasses to change the overlay icons shown in addition to visibility and deprecation. + * The default implementation also excludes the following tags: + * SymbolTag.Nullable, SymbolTag.NonNull, + * SymbolTag.Declaration, SymbolTag.Definition + * + * @return a list of {@link SymbolTag}s without visibility and deprecation tags + */ + protected List getAdditionalTagsPrecedence() { + return ADDITIONAL_TAGS_PRECEDENCE; + } + + /** + * Returns the visibility {@link SymbolTag} to be shown in the UI. All other {@link SymbolTag}s will be ignored. + * + * @param symbolTags a document symbol's {@link SymbolTag}s + * @return the highest precedence visibility {@link SymbolTag} if available + * + * @see #getVisibilityPrecedence() + */ + protected final Optional getHighestPrecedenceVisibilitySymbolTag(List symbolTags) { + final var precedenceList = getVisibilityPrecedence(); + return symbolTags.stream() + .filter(precedenceList::contains) + .min(Comparator.comparing(precedenceList::indexOf)); + } + + /** + * Returns a list of a document symbol's {@link SymbolTag}s excluding visibility and deprecation tags + * sorted according to their precedence. Symbol tags with higher precedence are more likely to be shown in the UI. + * + * @param symbolTags a document symbol's {@link SymbolTag}s + * @return a sorted list of {@link SymbolTag}s excluding visibility and deprecation tags + * + * @see #getAdditionalTagsPrecedence() + */ + protected final List getAdditionalSymbolTagsSorted(List symbolTags) { + final var precedenceList = getAdditionalTagsPrecedence(); + return symbolTags.stream() + .filter(precedenceList::contains) + .sorted(Comparator.comparing(precedenceList::indexOf)) + .toList(); + } + + private @Nullable ImageDescriptor getOverlayForVisibility(List symbolTags) { + Optional visibilityTag = getHighestPrecedenceVisibilitySymbolTag(symbolTags); + + if (visibilityTag.isEmpty()) { + return null; + } + + return LSPImages.imageDescriptorOverlayFromSymbolTag(visibilityTag.get()); + } + + /** + * Determines an image key (identifier) for the given arguments that can be used with the image registry. + * Instead of just determining the icon for a document symbol, the given optional visibility {@link SymbolTag}s + * are considered, i.e. fields and methods (incl. constructors) get different symbol icons depending on their visibility. + * + * @param kind a document symbol's kind, e.g. field, method, constructor, class, property + * @param symbolTags a document symbol's {@link SymbolTag}s, only visibility tags are considered + * @return an image's key (identifier) for the use with the image registry + * + * @see LSPImages#getImage(String) + * @see LSPImages#getImageDescriptor(String) + */ + protected String getImageKeyFromSymbolKindWithVisibility(SymbolKind kind, List symbolTags) { + + Optional visibilityTag = getHighestPrecedenceVisibilitySymbolTag(symbolTags); + + if (visibilityTag.isEmpty()) { + return LSPImages.imageKeyFromSymbolKind(kind); + } + + SymbolTag visibility = visibilityTag.get(); + + if (kind == SymbolKind.Field) { + return switch (visibility) { + case Private -> LSPImages.IMG_FIELD_VIS_PRIVATE; + case Package -> LSPImages.IMG_FIELD_VIS_PACKAGE; + case Protected -> LSPImages.IMG_FIELD_VIS_PROTECTED; + case Public -> LSPImages.IMG_FIELD_VIS_PUBLIC; + case Internal -> LSPImages.IMG_FIELD_VIS_INTERNAL; + case File -> LSPImages.IMG_FIELD_VIS_FILE; + default -> LSPImages.IMG_FIELD; + }; + } else if (kind == SymbolKind.Method || kind == SymbolKind.Constructor) { + return switch (visibility) { + case Private -> LSPImages.IMG_METHOD_VIS_PRIVATE; + case Package -> LSPImages.IMG_METHOD_VIS_PACKAGE; + case Protected -> LSPImages.IMG_METHOD_VIS_PROTECTED; + case Public -> LSPImages.IMG_METHOD_VIS_PUBLIC; + case Internal -> LSPImages.IMG_METHOD_VIS_INTERNAL; + case File -> LSPImages.IMG_METHOD_VIS_FILE; + default -> LSPImages.IMG_METHOD; + }; + } + + return LSPImages.imageKeyFromSymbolKind(kind); + } + + /** + * Returns an image for the given arguments. + * + * @param symbolKind the kind of symbol + * @param symbolTags the symbol tags + * @return a new or cached image for the given symbol kind with overlay icons computed for the given arguments. + * + * @see #getImageFor(SymbolKind, List, int) + */ + public @Nullable Image getImageFor(@Nullable SymbolKind symbolKind, @Nullable List symbolTags) { + return getImageFor(symbolKind, symbolTags, -1); + } + + /** + * Returns an image for the given arguments. + * Uses caching for all combinations of a symbol kind and a set of overlays. + * Deprecation is shown if the deprecated parameter is true + * or {@link SymbolTag#Deprecated} is in the set of symbol tags. + * + * @param symbolKind the kind of symbol + * @param symbolTags the symbol tags + * @param severity one of -1, {@link IMarker#SEVERITY_WARNING}, and {@link IMarker#SEVERITY_ERROR}. -1 indicates no overlay icon. + * @return a new or cached image for the given symbol kind with overlay icons computed for the given arguments. + * + * @see #getImageFor(SymbolKind, List) + */ + public @Nullable Image getImageFor(final @Nullable SymbolKind symbolKind, + final @Nullable List symbolTags, int severity) { + + if (symbolKind == null) { + return LSPImages.imageFromSymbolKind(symbolKind); + } + + final List finalSymbolTags = symbolTags != null ? symbolTags : Collections.emptyList(); + + String baseImageKey = getImageKeyFromSymbolKindWithVisibility(symbolKind, finalSymbolTags); + + ImageDescriptor severityImageDescriptor = getOverlayForMarkerSeverity(severity); + ImageDescriptor deprecatedImageDescriptor = getUnderlayForDeprecation(SymbolsUtil.isDeprecated(finalSymbolTags)); + + List additionalTags = getAdditionalSymbolTagsSorted(finalSymbolTags); + + ImageDescriptor topLeftOverlayDescriptor = null; + ImageDescriptor topRightOverlayDescriptor = null; + ImageDescriptor bottomLeftOverlayDescriptor = severityImageDescriptor; + ImageDescriptor bottomRightOverlayDescriptor = null; + ImageDescriptor underlayDescriptor = deprecatedImageDescriptor; + + // special case for a constructor with visibility tag => we need to add a "C" overlay to show it's a constructor + if (SymbolKind.Constructor == symbolKind + && !baseImageKey.equals(LSPImages.imageKeyFromSymbolKind(symbolKind))) { + topRightOverlayDescriptor = LSPImages.getImageDescriptor(LSPImages.IMG_OVR_CONSTRUCTOR); + } + + if (!additionalTags.isEmpty()) { + topLeftOverlayDescriptor = LSPImages.imageDescriptorOverlayFromSymbolTag(additionalTags.get(0)); + + if (additionalTags.size() > 1) { + if (SymbolKind.Constructor == symbolKind) { + // a constructor has an overlay in the top right corner, + // in this case we place the second symbol tag's overlay icon at the lower right corner + bottomRightOverlayDescriptor = LSPImages.imageDescriptorOverlayFromSymbolTag(additionalTags.get(1)); + } else { + topRightOverlayDescriptor = LSPImages.imageDescriptorOverlayFromSymbolTag(additionalTags.get(1)); + } + } + } + + if (SymbolKind.Field == symbolKind || SymbolKind.Method == symbolKind) { + // In these cases the visibility is already expressed by the symbol icon, so we can display one more symbol tag + if (additionalTags.size() > 2) { + bottomRightOverlayDescriptor = LSPImages.imageDescriptorOverlayFromSymbolTag(additionalTags.get(2)); + } + } else if (SymbolKind.Constructor != symbolKind) { + // We place the visibility overlay icon on the lower right corner, similar to JDT. + // The top left and top right corners remain for additional symbol tags (besides visibility, severity, deprecation) + // In case of constructors we already have a "C" for "constructor" in the upper right corner + // and have use the lower right corner for another additional symbol tag. + bottomRightOverlayDescriptor = getOverlayForVisibility(finalSymbolTags); + } + + return LSPImages.getImageWithOverlays(baseImageKey, topLeftOverlayDescriptor, topRightOverlayDescriptor, + bottomLeftOverlayDescriptor, bottomRightOverlayDescriptor, underlayDescriptor); + } + +}