IDEA-219140 Devkit: support <applicationListeners> (2019.2+)

GitOrigin-RevId: 2ea90efc767b84de616fd3ef0c501b4606e7ec75
This commit is contained in:
Yann Cébron
2019-07-26 17:46:33 +02:00
committed by intellij-monorepo-bot
parent 154dfcd4aa
commit b3f64ec64b
10 changed files with 175 additions and 9 deletions

View File

@@ -138,6 +138,9 @@ public interface IdeaPlugin extends DomElement {
Actions addActions();
/**
* Available since 192.
*/
@NotNull
@SubTagList("applicationListeners")
List<Listeners> getApplicationListeners();

View File

@@ -4,21 +4,29 @@ package org.jetbrains.idea.devkit.dom;
import com.intellij.psi.PsiClass;
import com.intellij.util.xml.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.devkit.dom.impl.PluginPsiClassConverter;
import java.util.List;
public interface Listeners extends DomElement {
@NotNull
@SubTagList("listener")
List<Listener> getListeners();
interface Listener extends DomElement {
@Attribute("class")
@Required
@Convert(PluginPsiClassConverter.class)
@ExtendClass(allowNonPublic = true, allowAbstract = false, allowInterface = false, allowEnum = false)
GenericAttributeValue<PsiClass> getListenerClassName();
@Attribute("topic")
@Required
@Convert(PluginPsiClassConverter.class)
@ExtendClass(allowNonPublic = true, allowEnum = false, instantiatable = false)
GenericAttributeValue<PsiClass> getTopicClassName();
@Attribute("activeInTestMode")

View File

@@ -67,6 +67,7 @@ import org.jetbrains.idea.devkit.dom.*;
import org.jetbrains.idea.devkit.dom.impl.PluginPsiClassConverter;
import org.jetbrains.idea.devkit.inspections.quickfix.AddWithTagFix;
import org.jetbrains.idea.devkit.module.PluginModuleType;
import org.jetbrains.idea.devkit.util.DescriptorUtil;
import org.jetbrains.idea.devkit.util.PsiUtil;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
@@ -89,15 +90,16 @@ public class PluginXmlDomInspection extends BasicDomElementsInspection<IdeaPlugi
@XCollection
public List<PluginModuleSet> PLUGINS_MODULES = new ArrayList<>();
private final AtomicClearableLazyValue<Map<String, PluginModuleSet>> myPluginModuleSetByModuleName = AtomicClearableLazyValue.create(() -> {
Map<String, PluginModuleSet> result = new HashMap<>();
for (PluginModuleSet modulesSet : PLUGINS_MODULES) {
for (String module : modulesSet.modules) {
result.put(module, modulesSet);
private final AtomicClearableLazyValue<Map<String, PluginModuleSet>> myPluginModuleSetByModuleName =
AtomicClearableLazyValue.create(() -> {
Map<String, PluginModuleSet> result = new HashMap<>();
for (PluginModuleSet modulesSet : PLUGINS_MODULES) {
for (String module : modulesSet.modules) {
result.put(module, modulesSet);
}
}
}
return result;
});
return result;
});
public PluginXmlDomInspection() {
@@ -108,7 +110,8 @@ public class PluginXmlDomInspection extends BasicDomElementsInspection<IdeaPlugi
@Override
public JComponent createOptionsPanel() {
ListTable table = new ListTable(new ListWrappingTableModel(myRegistrationCheckIgnoreClassList, ""));
JPanel panel = UiUtils.createAddRemoveTreeClassChooserPanel(table, DevKitBundle.message("inspections.plugin.xml.add.ignored.class.title"));
JPanel panel =
UiUtils.createAddRemoveTreeClassChooserPanel(table, DevKitBundle.message("inspections.plugin.xml.add.ignored.class.title"));
PanelGridBuilder grid = UI.PanelFactory.grid();
grid.resize().add(
UI.PanelFactory.panel(panel)
@@ -211,6 +214,9 @@ public class PluginXmlDomInspection extends BasicDomElementsInspection<IdeaPlugi
else if (element instanceof Helpset) {
highlightRedundant(element, DevKitBundle.message("inspections.plugin.xml.deprecated.helpset"), holder);
}
else if (element instanceof Listeners) {
annotateListeners((Listeners)element, holder);
}
}
if (element instanceof GenericDomValue) {
@@ -239,6 +245,54 @@ public class PluginXmlDomInspection extends BasicDomElementsInspection<IdeaPlugi
ModuleRootManager.getInstance(module).getFileIndex().isUnderSourceRootOfType(virtualFile, JavaModuleSourceRootTypes.PRODUCTION);
}
private static final int LISTENERS_PLATFORM_VERSION = 192;
private static void annotateListeners(Listeners listeners, DomElementAnnotationHolder holder) {
final Module module = listeners.getModule();
if (module == null || PsiUtil.isIdeaProject(module.getProject())) return;
IdeaPlugin ideaPlugin = DomUtil.getParentOfType(listeners, IdeaPlugin.class, true);
assert ideaPlugin != null;
if (!hasRealPluginId(ideaPlugin)) {
ideaPlugin = findMainDescriptor(module);
}
if (ideaPlugin == null) {
holder.createProblem(listeners, ProblemHighlightType.ERROR,
"Could not locate main plugin.xml file to determine required <idea-version> 'since-build'", null)
.highlightWholeElement();
return;
}
final GenericAttributeValue<BuildNumber> sinceBuild = ideaPlugin.getIdeaVersion().getSinceBuild();
final boolean noSinceBuildXml = !DomUtil.hasXml(sinceBuild);
if (noSinceBuildXml ||
sinceBuild.getValue() == null) {
holder.createProblem(listeners, ProblemHighlightType.ERROR,
"Must specify <idea-version> 'since-build'", null,
noSinceBuildXml ? new AddDomElementQuickFix<>(ideaPlugin.getIdeaVersion()) : null)
.highlightWholeElement();
return;
}
final int baselineVersion = sinceBuild.getValue().getBaselineVersion();
if (baselineVersion >= LISTENERS_PLATFORM_VERSION) return;
holder.createProblem(listeners, ProblemHighlightType.ERROR,
"Feature available in platform version " + LISTENERS_PLATFORM_VERSION + " or later only, " +
"but specified 'since-build' platform is '" + baselineVersion + "'", null)
.highlightWholeElement();
}
@Nullable
private static IdeaPlugin findMainDescriptor(@NotNull Module module) {
final XmlFile mainPluginXml = PluginModuleType.getPluginXml(module);
if (mainPluginXml == null) return null;
final DomFileElement<IdeaPlugin> mainDomFileElement = DescriptorUtil.getIdeaPlugin(mainPluginXml);
return mainDomFileElement != null ? mainDomFileElement.getRootElement() : null;
}
private static void annotateDependency(Dependency dependency, DomElementAnnotationHolder holder) {
final GenericAttributeValue<Boolean> optional = dependency.getOptional();
if (optional.getValue() == Boolean.FALSE) {

View File

@@ -0,0 +1,28 @@
<idea-plugin>
<id>foo</id>
<vendor>JetBrains</vendor>
<version>1.0</version>
<idea-version since-build="192.*"/>
<applicationListeners>
<listener class="java.lang.String" topic="java.lang.String"
activeInHeadlessMode="true" activeInTestMode="true"/>
</applicationListeners>
<applicationListeners>
<<error descr="'class' attribute should be defined">listener</error> topic="java.lang.String"
activeInHeadlessMode="true" activeInTestMode="true"/>
<<error descr="'topic' attribute should be defined">listener</error> class="java.lang.String"
activeInHeadlessMode="true" activeInTestMode="true"/>
<listener class="<error descr="'java.lang.Runnable' is not a concrete class"><error descr="Interface is not allowed">java.lang.Runnable</error></error>"
topic="java.lang.String"/>
<listener class="java.lang.String" topic="java.lang.String"
activeInHeadlessMode="<error descr="Cannot resolve symbol 'INVALID_VALUE'">INVALID_VALUE</error>"
activeInTestMode="<error descr="Cannot resolve symbol 'INVALID_VALUE'">INVALID_VALUE</error>"/>
</applicationListeners>
</idea-plugin>

View File

@@ -0,0 +1,8 @@
<idea-plugin>
<error descr="Feature available in platform version 192 or later only, but specified 'since-build' platform is '191'"><applicationListeners>
<listener class="java.lang.String" topic="java.lang.String"
activeInHeadlessMode="true" activeInTestMode="true"/>
</applicationListeners></error>
</idea-plugin>

View File

@@ -0,0 +1,9 @@
<idea-plugin>
<id>foo</id>
<vendor>JetBrains</vendor>
<version>1.0</version>
<idea-version since-build="191.*"/>
<depends config-file="dependency.xml">com.intellij.modules.vcs</depends>
</idea-plugin>

View File

@@ -0,0 +1,8 @@
<idea-plugin>
<error descr="Could not locate main plugin.xml file to determine required <idea-version> 'since-build'"><applicationListeners>
<listener class="java.lang.String" topic="java.lang.String"
activeInHeadlessMode="true" activeInTestMode="true"/>
</applicationListeners></error>
</idea-plugin>

View File

@@ -0,0 +1,12 @@
<idea-plugin>
<id>foo</id>
<vendor>JetBrains</vendor>
<version>1.0</version>
<error descr="Must specify <idea-version> 'since-build'"><applicationListeners>
<listener class="java.lang.String" topic="java.lang.String"
activeInHeadlessMode="true" activeInTestMode="true"/>
</applicationListeners></error>
</idea-plugin>

View File

@@ -0,0 +1,13 @@
<idea-plugin>
<id>foo</id>
<vendor>JetBrains</vendor>
<version>1.0</version>
<idea-version since-build="191"/>
<error descr="Feature available in platform version 192 or later only, but specified 'since-build' platform is '191'"><applicationListeners>
<listener class="java.lang.String" topic="java.lang.String"
activeInHeadlessMode="true" activeInTestMode="true"/>
</applicationListeners></error>
</idea-plugin>

View File

@@ -21,9 +21,11 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.module.StdModuleTypes
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ElementDescriptionUtil
import com.intellij.psi.PsiElement
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.TestDataFile
import com.intellij.testFramework.TestDataPath
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
@@ -95,6 +97,27 @@ class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
moduleBuilder.addLibrary("coreImpl", coreImpl)
}
void testApplicationListeners() {
doHighlightingTest("ApplicationListeners.xml")
}
void testApplicationListenersNoSinceBuild() {
doHighlightingTest("ApplicationListenersNoSinceBuild.xml")
}
void testApplicationListenersPre192() {
doHighlightingTest("ApplicationListenersPre192.xml")
}
void testApplicationListenersDepends() {
myFixture.copyFileToProject(getTestName(false) + ".xml", "META-INF/plugin.xml")
doHighlightingTest("ApplicationListenersDepends-dependency.xml")
}
void testApplicationListenersNoPluginIdStandalone() {
doHighlightingTest("ApplicationListenersNoPluginIdStandalone.xml")
}
void testExtensionI18n() {
doHighlightingTest("extensionI18n.xml",
"extensionI18nBundle.properties")