IDEA-271872 Devkit: support resolving sub-descriptors for <module>

GitOrigin-RevId: ee6adf98c13f54309db4684bf75e317c44b43610
This commit is contained in:
Yann Cébron
2021-06-30 18:00:11 +02:00
committed by intellij-monorepo-bot
parent 5135570523
commit 7c3b56da4b
12 changed files with 162 additions and 39 deletions

View File

@@ -6,18 +6,23 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.module.Module;
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.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScopes;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.ConvertContext;
import com.intellij.util.xml.DomUtil;
import com.intellij.util.xml.ElementPresentationManager;
import com.intellij.util.xml.ResolvingConverter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.DevKitBundle;
@@ -30,10 +35,28 @@ import java.util.*;
public class ModuleDescriptorNameConverter extends ResolvingConverter<IdeaPlugin> {
@NonNls
private static final String SUB_DESCRIPTOR_DELIMITER = "/";
@NonNls
private static final String SUB_DESCRIPTOR_FILENAME_DELIMITER = ".";
@Override
public String getErrorMessage(@Nullable String s, ConvertContext context) {
String value = StringUtil.notNullize(s);
String filePath;
String moduleName;
if (isSubDescriptor(value)) {
filePath = getSubDescriptorFilePath(value);
moduleName = getSubDescriptorModuleName(value);
}
else {
filePath = getDescriptorFilePath(value);
moduleName = value;
}
return DevKitBundle.message("plugin.xml.convert.module.descriptor.name",
getDescriptorFilePath(s), s);
filePath, moduleName);
}
@Override
@@ -42,9 +65,21 @@ public class ModuleDescriptorNameConverter extends ResolvingConverter<IdeaPlugin
if (StringUtil.isEmpty(s)) return null;
final Module currentModule = context.getModule();
if (currentModule == null) return null;
final ModuleManager moduleManager = ModuleManager.getInstance(context.getProject());
final Module module = ModuleManager.getInstance(context.getProject()).findModuleByName(s);
return module != null && getDependencies(currentModule).contains(module) ? findForModule(module) : null;
if (isSubDescriptor(s)) {
final Module module = moduleManager.findModuleByName(getSubDescriptorModuleName(s));
if (module != null && (module == currentModule || getDependencies(currentModule).contains(module))) {
return findDescriptorFile(module, getSubDescriptorFilePath(s));
}
return null;
}
final Module module = moduleManager.findModuleByName(s);
if (module != null && getDependencies(currentModule).contains(module)) {
return findDescriptorFile(module, getDescriptorFilePath(module.getName()));
}
return null;
}
@Override
@@ -55,13 +90,23 @@ public class ModuleDescriptorNameConverter extends ResolvingConverter<IdeaPlugin
@NotNull
private static String getDisplayName(@NotNull IdeaPlugin plugin) {
return DomUtil.getFile(plugin).getVirtualFile().getNameWithoutExtension();
final Module module = Objects.requireNonNull(plugin.getModule());
final String moduleName = module.getName();
final VirtualFile virtualFile = DomUtil.getFile(plugin).getVirtualFile();
final String fileName = virtualFile.getNameWithoutExtension();
if (moduleName.equals(fileName)) {
return fileName;
}
return moduleName + SUB_DESCRIPTOR_DELIMITER + StringUtil.substringAfterLast(fileName, SUB_DESCRIPTOR_FILENAME_DELIMITER);
}
@Override
public @Nullable LookupElement createLookupElement(IdeaPlugin plugin) {
return LookupElementBuilder.create(plugin, getDisplayName(plugin))
final String displayName = getDisplayName(plugin);
return LookupElementBuilder.create(Objects.requireNonNull(getPsiElement(plugin)), displayName)
.withIcon(ElementPresentationManager.getIconForClass(ContentDescriptor.ModuleDescriptor.class))
.withBoldness(isSubDescriptor(displayName))
.withTypeText(plugin.getPackage().getStringValue());
}
@@ -69,17 +114,24 @@ public class ModuleDescriptorNameConverter extends ResolvingConverter<IdeaPlugin
public @NotNull Collection<? extends IdeaPlugin> getVariants(ConvertContext context) {
final Module currentModule = context.getModule();
if (currentModule == null) return Collections.emptyList();
final Project project = context.getProject();
List<IdeaPlugin> variants = new SmartList<>();
final Set<Module> dependencies = getDependencies(currentModule);
List<IdeaPlugin> variants = new SmartList<>();
dependencies.add(currentModule);
for (Module module : dependencies) {
ContainerUtil.addIfNotNull(variants, findForModule(module));
String moduleName = module.getName();
processModuleSourceRoots(module, root -> {
final Collection<IdeaPlugin> plugins = DescriptorUtil.getPlugins(project, GlobalSearchScopes.directoryScope(project, root, false));
variants.addAll(ContainerUtil.filter(plugins, plugin -> DomUtil.getFile(plugin).getName().startsWith(moduleName)));
return true;
});
}
return variants;
}
@NotNull
private static Set<Module> getDependencies(Module currentModule) {
private static Set<Module> getDependencies(@NotNull Module currentModule) {
final Set<Module> dependencies = new LinkedHashSet<>();
ModuleUtilCore.getDependencies(currentModule, dependencies);
dependencies.remove(currentModule);
@@ -87,22 +139,48 @@ public class ModuleDescriptorNameConverter extends ResolvingConverter<IdeaPlugin
}
@Nullable
private static IdeaPlugin findForModule(Module module) {
String moduleName = module.getName();
final List<VirtualFile> resourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.PRODUCTION);
for (VirtualFile root : resourceRoots) {
final VirtualFile candidate = root.findChild(getDescriptorFilePath(moduleName));
if (candidate == null) continue;
private static IdeaPlugin findDescriptorFile(@NotNull Module module, @NotNull String filePath) {
Ref<IdeaPlugin> ideaPlugin = Ref.create();
processModuleSourceRoots(module, root -> {
final VirtualFile candidate = root.findChild(filePath);
if (candidate == null) return true;
final PsiFile psiFile = PsiManager.getInstance(module.getProject()).findFile(candidate);
if (DescriptorUtil.isPluginXml(psiFile)) {
return DescriptorUtil.getIdeaPlugin((XmlFile)psiFile);
ideaPlugin.set(DescriptorUtil.getIdeaPlugin((XmlFile)psiFile));
return false;
}
return true;
});
return ideaPlugin.get();
}
private static void processModuleSourceRoots(@NotNull Module module, Processor<VirtualFile> processor) {
for (VirtualFile root : ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.PRODUCTION)) {
if (!processor.process(root)) return;
}
return null;
}
@NotNull
private static String getDescriptorFilePath(String moduleName) {
return moduleName + ".xml";
private static String getDescriptorFilePath(@NotNull String fileName) {
return fileName + ".xml";
}
private static boolean isSubDescriptor(@NotNull String value) {
return StringUtil.contains(value, SUB_DESCRIPTOR_DELIMITER);
}
@NotNull
private static String getSubDescriptorModuleName(@NotNull String value) {
final String moduleName = StringUtil.substringBefore(value, SUB_DESCRIPTOR_DELIMITER);
assert moduleName != null : value;
return moduleName;
}
@NotNull
private static String getSubDescriptorFilePath(@NotNull String value) {
final String moduleName = getSubDescriptorModuleName(value);
final String fileName = StringUtil.substringAfter(value, SUB_DESCRIPTOR_DELIMITER);
assert fileName != null : value;
return getDescriptorFilePath(moduleName + SUB_DESCRIPTOR_FILENAME_DELIMITER + fileName);
}
}

View File

@@ -88,7 +88,8 @@ public final class PluginIdDependenciesIndex extends PluginXmlIndexBase<String,
for (ContentDescriptor.ModuleDescriptor descriptor : plugin.getContent().getModuleEntry()) {
final String value = descriptor.getName().getStringValue();
if (StringUtil.isNotEmpty(value)) {
ids.add(getContentIndexingKey(value));
final String escapeSubDescriptorValue = value.replace('/', '.');
ids.add(getContentIndexingKey(escapeSubDescriptorValue));
}
}
@@ -97,7 +98,7 @@ public final class PluginIdDependenciesIndex extends PluginXmlIndexBase<String,
@Override
public int getVersion() {
return 4;
return 5;
}
public static Set<String> getPluginAndDependsIds(Project project, Set<VirtualFile> files) {

View File

@@ -0,0 +1,8 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<!-- defined in ExtensionsDependencies.xml "main" plugin -->
<myPlugin.mainPlugin
implementation="java.lang.Integer"/>
</extensions>
</idea-plugin>

View File

@@ -5,6 +5,7 @@
<content>
<module name="ExtensionsDependencies-content"/>
<module name="ExtensionsDependencies-content/subDescriptor"/>
</content>
<dependencies>

View File

@@ -6,6 +6,12 @@
<content>
<module name="anotherModule"/>
<module name="anotherModule/secondary-descriptor"/>
<module name="<error descr="Cannot resolve module descriptor 'anotherModule.INVALID_VALUE.xml' in source roots of module 'anotherModule'">anotherModule/INVALID_VALUE</error>"/>
<module name="mainModule/sub-descriptor"/>
<module name="<error descr="Cannot resolve module descriptor 'mainModule.INVALID_VALUE.xml' in source roots of module 'mainModule'">mainModule/INVALID_VALUE</error>"/>
<module name="<error descr="Cannot resolve module descriptor 'invalidModuleName.xml' in source roots of module 'invalidModuleName'">invalidModuleName</error>"/>
</content>

View File

@@ -5,6 +5,7 @@
<dependencies>
<module name="anotherModule"/>
<module name="anotherModule/secondary-descriptor"/>
<module name="<error descr="Cannot resolve module descriptor 'invalid.module.xml' in source roots of module 'invalid.module'">invalid.module</error>"/>
<plugin id="anotherModule.plugin.id"/>

View File

@@ -11,11 +11,11 @@ import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.TestDataPath;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import com.intellij.util.ArrayUtil;
import com.intellij.util.PathUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil;
import org.jetbrains.idea.devkit.inspections.PluginXmlDomInspection;
@@ -24,17 +24,14 @@ import java.util.List;
@TestDataPath("$CONTENT_ROOT/testData/codeInsight/contentDependencyDescriptor")
public class PluginXmlContentDependencyDescriptorTest extends JavaCodeInsightFixtureTestCase {
@NonNls
private static final String MAIN_MODULE_NAME = "mainModule";
@Override
protected String getBasePath() {
return DevkitJavaTestsUtil.TESTDATA_PATH + "codeInsight/contentDependencyDescriptor";
}
@Override
protected void tuneFixture(JavaModuleFixtureBuilder<?> moduleBuilder) throws Exception {
String editorUIApi = PathUtil.getJarPathForClass(AnAction.class);
moduleBuilder.addLibrary("editor-ui-api", editorUIApi);
}
public void testNonJetBrainsHighlighting() {
doHighlightingTest(ArrayUtil.EMPTY_STRING_ARRAY);
}
@@ -50,13 +47,19 @@ public class PluginXmlContentDependencyDescriptorTest extends JavaCodeInsightFix
public void testContentDescriptorModuleNameCompletion() {
setupModules("anotherModule");
List<String> lookupElements = myFixture.getCompletionVariants("META-INF/plugin.xml");
assertSameElements(lookupElements, "anotherModule");
myFixture.configureFromTempProjectFile(MAIN_MODULE_NAME + "/META-INF/plugin.xml");
myFixture.completeBasic();
List<String> lookupElements = myFixture.getLookupElementStrings();
assertSameElements(lookupElements, "anotherModule", "anotherModule/secondary-descriptor");
final LookupElementPresentation anotherModule = getLookupElementPresentation("anotherModule");
assertEquals(AllIcons.Nodes.Module, anotherModule.getIcon());
assertFalse(anotherModule.isItemTextBold());
//noinspection SpellCheckingInspection
assertEquals("mypackage.subpackage", anotherModule.getTypeText());
final LookupElementPresentation anotherModuleSecondary = getLookupElementPresentation("anotherModule/secondary-descriptor");
assertTrue(anotherModuleSecondary.isItemTextBold());
}
public void testContentDescriptorHighlighting() {
@@ -87,24 +90,30 @@ public class PluginXmlContentDependencyDescriptorTest extends JavaCodeInsightFix
setupModules(dependencyModuleNames);
myFixture.enableInspections(new PluginXmlDomInspection());
myFixture.testHighlighting(true, false, false, "META-INF/plugin.xml");
myFixture.testHighlighting(true, false, false, "mainModule/META-INF/plugin.xml");
}
private void setupModules(String... dependencyModuleNames) {
String testName = getTestName(true);
myFixture.copyDirectoryToProject("/" + testName + "/mainModule", "/");
if (dependencyModuleNames.length == 0) return;
final Module mainModule = addModule(MAIN_MODULE_NAME);
for (String moduleName : dependencyModuleNames) {
final VirtualFile dependencyModuleRoot =
myFixture.copyDirectoryToProject("/" + testName + "/" + moduleName, "/" + moduleName);
Module dependencyModule = PsiTestUtil.addModule(getProject(), StdModuleTypes.JAVA, moduleName, dependencyModuleRoot);
ModuleRootModificationUtil.addDependency(getModule(), dependencyModule);
final Module dependencyModule = addModule(moduleName);
ModuleRootModificationUtil.addDependency(mainModule, dependencyModule);
}
}
private Module addModule(String moduleName) {
String testName = getTestName(true);
final VirtualFile dependencyModuleRoot =
myFixture.copyDirectoryToProject("/" + testName + "/" + moduleName, "/" + moduleName);
Module dependencyModule = PsiTestUtil.addModule(getProject(), StdModuleTypes.JAVA, moduleName, dependencyModuleRoot);
PsiTestUtil.addLibrary(dependencyModule, "editor-ui-api", PathUtil.getJarPathForClass(AnAction.class));
return dependencyModule;
}
private LookupElementPresentation getLookupElementPresentation(String lookupString) {
final LookupElement lookupElement = ContainerUtil.find(myFixture.getLookupElements(),
element -> element.getLookupString().equals(lookupString));

View File

@@ -198,13 +198,20 @@ class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
void testExtensionsDependencies() {
addExtensionsModule("ExtensionsDependencies-module")
VirtualFile contentFile = addExtensionsModule("ExtensionsDependencies-content")
VirtualFile contentSubDescriptorFile =
myFixture.copyFileToProject("ExtensionsDependencies-content.subDescriptor.xml",
"/ExtensionsDependencies-content/ExtensionsDependencies-content.subDescriptor.xml")
doHighlightingTest("ExtensionsDependencies.xml",
"ExtensionsDependencies-plugin.xml")
myFixture.configureFromExistingVirtualFile(contentFile)
doHighlightingTest()
myFixture.configureFromExistingVirtualFile(contentSubDescriptorFile)
doHighlightingTest()
}
private VirtualFile addExtensionsModule(String name) {