mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
IDEA-219140 Devkit: support <applicationListeners> (2019.2+)
GitOrigin-RevId: 2ea90efc767b84de616fd3ef0c501b4606e7ec75
This commit is contained in:
committed by
intellij-monorepo-bot
parent
154dfcd4aa
commit
b3f64ec64b
@@ -138,6 +138,9 @@ public interface IdeaPlugin extends DomElement {
|
||||
|
||||
Actions addActions();
|
||||
|
||||
/**
|
||||
* Available since 192.
|
||||
*/
|
||||
@NotNull
|
||||
@SubTagList("applicationListeners")
|
||||
List<Listeners> getApplicationListeners();
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user