IDEA-254891 Devkit: support FQN names for icons

GitOrigin-RevId: 2e3cbfdfc8b1d55380bc39f97379d5c36d34f6b6
This commit is contained in:
Yann Cébron
2021-05-04 15:49:13 +00:00
committed by intellij-monorepo-bot
parent 695b6187ac
commit e9e08fb286
3 changed files with 92 additions and 42 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.devkit.references;
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider;
@@ -13,7 +13,6 @@ import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -55,8 +54,23 @@ import static com.intellij.patterns.PsiJavaPatterns.*;
public class IconsReferencesContributor extends PsiReferenceContributor
implements QueryExecutor<PsiReference, ReferencesSearch.SearchParameters> {
@NonNls
private static final String ALL_ICONS_FQN = "com.intellij.icons.AllIcons";
@NonNls
private static final String ALL_ICONS_NAME = "AllIcons";
@NonNls
private static final String PLATFORM_ICONS_MODULE = "intellij.platform.icons";
@NonNls
private static final String ICONS_MODULE = "icons";
@NonNls
private static final String ICONS_PACKAGE_PREFIX = "icons.";
@NonNls
private static final String COM_INTELLIJ_ICONS_PREFIX = "com.intellij.icons.";
@Override
public boolean execute(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull final Processor<? super PsiReference> consumer) {
public boolean execute(@NotNull ReferencesSearch.SearchParameters queryParameters,
@NotNull final Processor<? super PsiReference> consumer) {
final PsiElement file = queryParameters.getElementToSearch();
if (file instanceof PsiBinaryFile) {
final Module module = ReadAction.compute(() -> ModuleUtilCore.findModuleForPsiElement(file));
@@ -178,12 +192,13 @@ public class IconsReferencesContributor extends PsiReferenceContributor
if (newElementName == null) {
newElementName = ((PsiField)element).getName();
}
if (classQualifiedName.startsWith("com.intellij.icons.")) {
return replace(classQualifiedName, newElementName, "com.intellij.icons.");
if (classQualifiedName.startsWith(COM_INTELLIJ_ICONS_PREFIX)) {
return replace(classQualifiedName, newElementName, COM_INTELLIJ_ICONS_PREFIX);
}
if (classQualifiedName.startsWith("icons.")) {
return replace(classQualifiedName, newElementName, "icons.");
if (classQualifiedName.startsWith(ICONS_PACKAGE_PREFIX)) {
return replace(classQualifiedName, newElementName, ICONS_PACKAGE_PREFIX);
}
return ElementManipulators.handleContentChange(myElement, classQualifiedName + "." + newElementName);
}
}
}
@@ -202,7 +217,7 @@ public class IconsReferencesContributor extends PsiReferenceContributor
}
private static void registerForIconLoaderMethods(@NotNull PsiReferenceRegistrar registrar) {
final PsiMethodPattern method = psiMethod().withName("load").definedInClass("com.intellij.icons.AllIcons");
final PsiMethodPattern method = psiMethod().withName("load").definedInClass(ALL_ICONS_FQN);
final PsiJavaElementPattern.Capture<PsiLiteralExpression> findGetIconPattern
= literalExpression().and(psiExpression().methodCallParameter(0, method));
registrar.registerReferenceProvider(findGetIconPattern, new PsiReferenceProvider() {
@@ -212,9 +227,9 @@ public class IconsReferencesContributor extends PsiReferenceContributor
return new FileReferenceSet(element) {
@Override
protected Collection<PsiFileSystemItem> getExtraContexts() {
Module iconsModule = ModuleManager.getInstance(element.getProject()).findModuleByName("intellij.platform.icons");
Module iconsModule = ModuleManager.getInstance(element.getProject()).findModuleByName(PLATFORM_ICONS_MODULE);
if (iconsModule == null) {
iconsModule = ModuleManager.getInstance(element.getProject()).findModuleByName("icons");
iconsModule = ModuleManager.getInstance(element.getProject()).findModuleByName(ICONS_MODULE);
}
if (iconsModule == null) {
return super.getExtraContexts();
@@ -269,12 +284,13 @@ public class IconsReferencesContributor extends PsiReferenceContributor
if (newElementName == null) {
newElementName = ((PsiField)element).getName();
}
if (classQualifiedName.startsWith("com.intellij.icons.")) {
return replace(newElementName, classQualifiedName, "com.intellij.icons.");
if (classQualifiedName.startsWith(COM_INTELLIJ_ICONS_PREFIX)) {
return replace(newElementName, classQualifiedName, COM_INTELLIJ_ICONS_PREFIX);
}
if (classQualifiedName.startsWith("icons.")) {
return replace(newElementName, classQualifiedName, "icons.");
if (classQualifiedName.startsWith(ICONS_PACKAGE_PREFIX)) {
return replace(newElementName, classQualifiedName, ICONS_PACKAGE_PREFIX);
}
return ElementManipulators.handleContentChange(myElement, classQualifiedName + "." + newElementName);
}
}
}
@@ -298,7 +314,7 @@ public class IconsReferencesContributor extends PsiReferenceContributor
}
private static boolean isIconsModule(Module module) {
return module != null && ("icons".equals(module.getName()) || "intellij.platform.icons".equals(module.getName()))
return module != null && (ICONS_MODULE.equals(module.getName()) || PLATFORM_ICONS_MODULE.equals(module.getName()))
&& ModuleRootManager.getInstance(module).getSourceRoots().length == 1;
}
@@ -307,42 +323,59 @@ public class IconsReferencesContributor extends PsiReferenceContributor
return image != null && mgr.getFileTypeByFile(image) == mgr.getFileTypeByExtension("png");
}
/**
* Name of class containing icons must end with {@code Icons}.
* <p>
* Valid icon paths:
* <ul>
* <li>AllIcons.IconFieldName (=com.intellij.icons.AllIcons)</li>
* <li>MyIcons.IconFieldName (implicitly in 'icons' package)</li>
* <li>MyIcons.InnerClass.IconFieldName ("")</li>
* </ul>
* Using FQN notation:
* <ul>
* <li>com.company.MyIcons.IconFieldName</li>
* <li>com.company.MyIcons.InnerClass.IconFieldName</li>
* </ul>
*/
@Nullable
private static PsiField resolveIconPath(@NonNls @Nullable String pathStr, PsiElement element) {
if (pathStr == null) {
return null;
private static PsiField resolveIconPath(@NonNls @Nullable String path, PsiElement element) {
if (path == null) return null;
@NonNls List<String> pathElements = StringUtil.split(path, ".");
if (pathElements.size() < 2) return null;
final int iconsClassNameIdx = ContainerUtil.lastIndexOf(pathElements, s -> s.endsWith("Icons"));
if (iconsClassNameIdx == -1) return null;
PsiClass iconClass = findIconClass(element.getProject(),
StringUtil.join(ContainerUtil.getFirstItems(pathElements, iconsClassNameIdx + 1), "."),
iconsClassNameIdx != 0);
if (iconClass == null) return null;
for (int i = iconsClassNameIdx + 1; i < pathElements.size() - 1; i++) {
iconClass = iconClass.findInnerClassByName(pathElements.get(i), false);
if (iconClass == null) return null;
}
@NonNls List<String> path = StringUtil.split(pathStr, ".");
if (path.size() > 1 && path.get(0).endsWith("Icons")) {
Project project = element.getProject();
PsiClass cur = findIconClass(project, path.get(0));
if (cur == null) {
return null;
}
for (int i = 1; i < path.size() - 1; i++) {
cur = cur.findInnerClassByName(path.get(i), false);
if (cur == null) {
return null;
}
}
return cur.findFieldByName(path.get(path.size() - 1), false);
}
return null;
return iconClass.findFieldByName(pathElements.get(pathElements.size() - 1), false);
}
@Nullable
private static PsiClass findIconClass(Project project, @NonNls String className) {
final boolean isAllIcons = "AllIcons".equals(className);
final String fqnClassName = isAllIcons ? "com.intellij.icons.AllIcons" : "icons." + className;
return JavaPsiFacade.getInstance(project)
.findClass(fqnClassName, isAllIcons ? GlobalSearchScope.allScope(project) : GlobalSearchScope.projectScope(project));
private static PsiClass findIconClass(Project project, @NonNls @NotNull String iconClass, boolean isQualifiedFqn) {
final String adjustedIconClassFqn;
if (isQualifiedFqn) {
adjustedIconClassFqn = iconClass;
}
else {
adjustedIconClassFqn = ALL_ICONS_NAME.equals(iconClass) ? ALL_ICONS_FQN : ICONS_PACKAGE_PREFIX + iconClass;
}
return JavaPsiFacade.getInstance(project).findClass(adjustedIconClassFqn, GlobalSearchScope.allScope(project));
}
private static abstract class IconPsiReferenceBase extends PsiReferenceBase<PsiElement> implements EmptyResolveMessageProvider {
IconPsiReferenceBase(@NotNull PsiElement element) {
super(element, true);
}
@@ -9,18 +9,27 @@
<extensions defaultExtensionNs="com.intellij.myPlugin">
<!-- icons.MyIcons -->
<myIconEP icon="MyIcons.MyCustomIcon"/>
<!-- FQN notation -->
<myIconEP icon="my.FqnIcons.MyFqnIcon"/>
<myIconEP icon="my.FqnIcons.Inner.MyInnerFqnIcon"/>
<myIconEP icon="<error descr="Cannot resolve icon 'INVALID_VALUE'">INVALID_VALUE</error>"/>
<myIconEP icon="<error descr="Cannot resolve icon 'MyIcons.INVALID_VALUE'">MyIcons.INVALID_VALUE</error>"/>
<!-- FQN notation -->
<myIconEP icon="<error descr="Cannot resolve icon 'my.FqnIcons.INVALID_VALUE'">my.FqnIcons.INVALID_VALUE</error>"/>
</extensions>
<actions>
<action id="validIcon" class="foo.FooAction" icon="MyIcons.MyCustomIcon"/>
<action id="validFqnIcon" class="foo.FooAction" icon="my.FqnIcons.MyFqnIcon"/>
<group icon="MyIcons.MyCustomIcon"/>
<action id="invalidIcon" class="foo.FooAction" icon="<error descr="Cannot resolve icon 'MyIcons.INVALID_VALUE'">MyIcons.INVALID_VALUE</error>"/>
<action id="invalidFqnIcon" class="foo.FooAction" icon="<error descr="Cannot resolve icon 'my.FqnIcons.INVALID_VALUE'">my.FqnIcons.INVALID_VALUE</error>"/>
<group icon="<error descr="Cannot resolve icon 'MyIcons.INVALID_VALUE'">MyIcons.INVALID_VALUE</error>"/>
</actions>
@@ -418,6 +418,14 @@ class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
"public class MyIcons {" +
" public static final javax.swing.Icon MyCustomIcon = null; " +
"}")
myFixture.addClass("package my; " +
"public class FqnIcons {" +
" public static final javax.swing.Icon MyFqnIcon = null; " +
" " +
" public static class Inner {" +
" public static final javax.swing.Icon MyInnerFqnIcon = null; " +
" }" +
"}")
doHighlightingTest("iconAttribute.xml",
"MyIconAttributeEPBean.java")
}