From d58d166cd477d35ded416d00bddcf9bc7fd60a86 Mon Sep 17 00:00:00 2001 From: Konstantin Ulitin Date: Thu, 8 Aug 2024 10:26:15 +0000 Subject: [PATCH] [ui] fix loading plugin icons in html view (IJPL-43721) Co-authored-by: Victor Turansky Merge-request: IJ-MR-126946 Merged-by: Victor Turansky GitOrigin-RevId: 11aa1c46f9348b3c4713ff5f35bec6abdce0e642 --- .../com/intellij/ui/icons/CoreIconManager.kt | 7 +++ .../util/src/com/intellij/ui/IconManager.kt | 3 + .../ui/icons/ImageDataByPathLoader.kt | 55 ++++++++++--------- .../util/ui/ExtendableHTMLViewFactory.kt | 16 +++++- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/platform/ide-core-impl/src/com/intellij/ui/icons/CoreIconManager.kt b/platform/ide-core-impl/src/com/intellij/ui/icons/CoreIconManager.kt index d7a0c3a7c85f..4caa6560185d 100644 --- a/platform/ide-core-impl/src/com/intellij/ui/icons/CoreIconManager.kt +++ b/platform/ide-core-impl/src/com/intellij/ui/icons/CoreIconManager.kt @@ -8,6 +8,7 @@ import com.intellij.AbstractBundle import com.intellij.DynamicBundle import com.intellij.icons.AllIcons import com.intellij.ide.IconLayerProvider +import com.intellij.ide.plugins.PluginManager import com.intellij.ide.plugins.PluginManagerCore import com.intellij.ide.plugins.cl.PluginAwareClassLoader import com.intellij.openapi.diagnostic.logger @@ -257,6 +258,12 @@ class CoreIconManager : IconManager, CoreAwareIconManager { return plugin.content.modules.firstOrNull { it.name == moduleId }?.requireDescriptor()?.classLoader } } + + override fun getClassLoaderByClassName(className: String): ClassLoader? { + val pluginId = PluginManager.getPluginByClassNameAsNoAccessToClass(className) + val plugin = PluginManagerCore.getPlugin(pluginId) + return plugin?.classLoader + } } private class IconLayer(@JvmField val flagMask: Int, @JvmField val icon: Icon) { diff --git a/platform/util/src/com/intellij/ui/IconManager.kt b/platform/util/src/com/intellij/ui/IconManager.kt index 3f5369c6e0e7..fc42799d5d8d 100644 --- a/platform/util/src/com/intellij/ui/IconManager.kt +++ b/platform/util/src/com/intellij/ui/IconManager.kt @@ -107,6 +107,9 @@ interface IconManager { fun getPluginAndModuleId(classLoader: ClassLoader): Pair = "com.intellij" to null fun getClassLoader(pluginId: String, moduleId: String?): ClassLoader? = IconManager::class.java.classLoader + + @Internal + fun getClassLoaderByClassName(className: String): ClassLoader? = IconManager::class.java.classLoader } private object DummyIconManager : IconManager { diff --git a/platform/util/ui/src/com/intellij/ui/icons/ImageDataByPathLoader.kt b/platform/util/ui/src/com/intellij/ui/icons/ImageDataByPathLoader.kt index a711e95b4424..16676eb5214b 100644 --- a/platform/util/ui/src/com/intellij/ui/icons/ImageDataByPathLoader.kt +++ b/platform/util/ui/src/com/intellij/ui/icons/ImageDataByPathLoader.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ui.icons import com.github.benmanes.caffeine.cache.Cache @@ -144,34 +144,39 @@ fun isReflectivePath(path: String): Boolean { @Internal fun getReflectiveIcon(path: String, classLoader: ClassLoader): Icon? { try { - var dotIndex = path.lastIndexOf('.') - val fieldName = path.substring(dotIndex + 1) - val builder = StringBuilder(path.length + 20) - builder.append(path, 0, dotIndex) - var separatorIndex = -1 - do { - dotIndex = path.lastIndexOf('.', dotIndex - 1) - // if starts with a lower case, char - it is a package name - if (dotIndex == -1 || path[dotIndex + 1].isLowerCase()) { - break - } - if (separatorIndex != -1) { - builder.setCharAt(separatorIndex, '$') - } - separatorIndex = dotIndex - } - while (true) - if (!builder[0].isLowerCase()) { - if (separatorIndex != -1) { - builder.setCharAt(separatorIndex, '$') - } - builder.insert(0, if (path.startsWith("AllIcons.")) "com.intellij.icons." else "icons.") - } - val aClass = classLoader.loadClass(builder.toString()) + val fieldName = path.substring(path.lastIndexOf('.') + 1) + val className = getClassNameByIconPath(path) + val aClass = classLoader.loadClass(className) return LOOKUP.findStaticGetter(aClass, fieldName, Icon::class.java).invoke() as Icon } catch (e: Throwable) { logger().warn("Cannot get reflective icon (path=$path)", e) return null } +} + +internal fun getClassNameByIconPath(path: String): String { + var dotIndex = path.lastIndexOf('.') + val builder = StringBuilder(path.length + 20) + builder.append(path, 0, dotIndex) + var separatorIndex = -1 + do { + dotIndex = path.lastIndexOf('.', dotIndex - 1) + // if starts with a lower case, char - it is a package name + if (dotIndex == -1 || path[dotIndex + 1].isLowerCase()) { + break + } + if (separatorIndex != -1) { + builder.setCharAt(separatorIndex, '$') + } + separatorIndex = dotIndex + } + while (true) + if (!builder[0].isLowerCase()) { + if (separatorIndex != -1) { + builder.setCharAt(separatorIndex, '$') + } + builder.insert(0, if (path.startsWith("AllIcons.")) "com.intellij.icons." else "icons.") + } + return builder.toString() } \ No newline at end of file diff --git a/platform/util/ui/src/com/intellij/util/ui/ExtendableHTMLViewFactory.kt b/platform/util/ui/src/com/intellij/util/ui/ExtendableHTMLViewFactory.kt index c783b056cbae..0f757b92503a 100644 --- a/platform/util/ui/src/com/intellij/util/ui/ExtendableHTMLViewFactory.kt +++ b/platform/util/ui/src/com/intellij/util/ui/ExtendableHTMLViewFactory.kt @@ -6,6 +6,9 @@ package com.intellij.util.ui import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.util.findIconUsingNewImplementation import com.intellij.openapi.util.text.HtmlChunk +import com.intellij.ui.IconManager +import com.intellij.ui.icons.getClassNameByIconPath +import com.intellij.ui.icons.isReflectivePath import com.intellij.ui.scale.JBUIScale import com.intellij.util.asSafely import com.intellij.util.text.nullize @@ -32,7 +35,7 @@ import kotlin.math.max */ class ExtendableHTMLViewFactory internal constructor( private val extensions: List<(Element, View) -> View?>, - private val base: ViewFactory = HTMLEditorKit().viewFactory + private val base: ViewFactory = HTMLEditorKit().viewFactory, ) : HTMLFactory() { internal constructor(vararg extensions: (Element, View) -> View?) : this(extensions.asList()) @@ -190,7 +193,16 @@ private class IconExtension(private val existingIconProvider: (key: String) -> I if (existingIcon != null) { return existingIcon } - return findIconUsingNewImplementation(path = src, classLoader = ExtendableHTMLViewFactory::class.java.classLoader) + + val classLoader = if (isReflectivePath(src)) { + val className = getClassNameByIconPath(src) + IconManager.getInstance().getClassLoaderByClassName(className) + } else null + + return findIconUsingNewImplementation( + path = src, + classLoader = classLoader ?: ExtendableHTMLViewFactory::class.java.classLoader, + ) } }