if-else logic in plugin dependencies?
Is there any way to implement conditions when declaring plugin dependencies in plugin.xml?
Let's say I want to implement some extensions if module com.intellij.modules.cidr.lang presents and some OTHER extensions if not. Something like next:
if <depends optional="true" config-file="clion.xml">com.intellij.modules.cidr.lang</depends>
else <depends optional="true" config-file="idea.xml"></depends>
My case: Since Android Studio 3.2 Canary 7, it has built-in CMake support (the same implementation as for CLion). Before I just checked for the presence of com.intellij.modules.cidr.lang or com.intellij.modules.java and use different extension's implementations for my cmake plugin in CLion and in Idea/AS:
<depends optional="true" config-file="clion.xml">com.intellij.modules.cidr.lang</depends>
<depends optional="true" config-file="idea.xml">com.intellij.modules.java</depends>
clion.xml
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<annotator language="CMake" implementationClass="com.cmakeplugin.annotator.CMakeCLionAnnotator"/>
</extensions>
</idea-plugin>
idea.xml
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<fileTypeFactory implementation="com.cmakeplugin.CMakeFileTypeFactory"/>
<lang.parserDefinition language="CMake" implementationClass="com.cmakeplugin.CMakeParserDefinition"/>
<lang.syntaxHighlighterFactory key="CMake" implementationClass="com.cmakeplugin.CMakeSyntaxHighlighterFactory"/>
<lang.braceMatcher language="CMake" implementationClass="com.cmakeplugin.CMakeBraceMatcher"/>
<lang.refactoringSupport language="CMake" implementationClass="com.cmakeplugin.CMakeRefactoringSupportProvider"/>
<annotator language="CMake" implementationClass="com.cmakeplugin.annotator.CMakeIdeaAnnotator"/>
<lang.findUsagesProvider language="CMake" implementationClass="com.cmakeplugin.CMakeFindUsagesProvider"/>
</extensions>
</idea-plugin>
I can't do that anymore for Android Studio 3.2 Canary 7 and above, as it has both that modules and I need to use only clion.xml for it.
Also, I would like to keep idea.xml functionality for AS 3.1 and below, so I can't just make a check for com.intellij.modules.androidstudio and use clion.xml for any AS.
The best solution would be to use clion.xml if com.intellij.modules.cidr.lang presence and idea.xml otherwise. Does that possible somehow in terms of plugin.xml syntax?
Please sign in to leave a comment.
No, conditions aren't supported in plugin.xml file. However you can fix your case by replacing dependency on 'com.intellij.modules.java' by 'com.intellij.modules.idea'. The latter module is present in IntelliJ IDEA Community and Ultimate (since 2018.1) and isn't present in Android Studio.
Instead of providing a implementationClass use a factoryClass. Implement the interface com.intellij.openapi.extensions.ExtensionFactory, and return the Annotator instance you want. This will reduce your configuration file count to one for this piece of functionality. The annotator element would look like this:
<annotator language="CMake" implementationClass="com.cmakeplugin.annotator.CMakeAnnotator" factoryClass="com.cmakeplugin.annotator.CMakeAnnotatorFactory" />
In your CMakeAnnotatorFactory, you would use the PluginManager to detect the existence of the plugin you want to drive your functionality, then return an instance of the appropriate Annotator class. Your implementationClass should be some common base class or interface.
Keep in mind I've never tried this so your mileage may vary, but it seems plausible. If the user disables the plugin driving your functionality, they will have to restart IntelliJ anyway so you wouldn't need to care about handling changes in the plugin.
EDIT: I just tested this out, and it works.
Thank you for your response. It's a shame we don't have that condition logic in plugin.xml. Conditioning dependencies will make very simple and elegant solution.
Unfortunately, replacing dependency on 'com.intellij.modules.java' by 'com.intellij.modules.idea' will disable functionality of `idea.xml` for all AS versions. While I would like to keep it for AS below 3.2 (current stable is 3.1.1).
Looks like I have to wait till AS 3.2 will be released and only then split my plugin functionality by 'com.intellij.modules.idea' (IDEA CE/UE) and `com.intellij.modules.cidr.lang` (CLion, AS)... But that will not work well for current AS Canary users and then for legacy AS users...
PS Would be very useful to be able to check at plugin.xml for the presence of some particular class (not module) in platform under which plugin is running. Something like what we can do at runtime by checking `Class.forName(className)`. Looks like it's not to difficult to implement that in IdeaPluginDescriptorImpl.
Thank you @Bhandy! I've started looking to that direction of managing extensions from the java code somehow and even found quite promising method `com.intellij.lang.LanguageAnnotators.INSTANCE.addExplicitExtension()`. But you gave me really clear roadmap!
Recent update.
Dealing with `factoryClass` works fine if we need to provide a specific implementation (let say Annotator) depending on a platform we started under.
But in case if we should not provide any implementation for some of the platforms, we are in trouble. As far as `ExtensionFactory.createInstance` must return some kind of `Object` and that will be registered for extension point we call `createInstance` from.
In my case, that solution work fine for Annotators (I have either version for CLion and Idea), but fall with the error (CLion) or strange behaviour (AS 3.2 Canary 10) for `parserDefinition`, if I return either `null` or empty Object: `new Object()`.
The workaround would be creating <application-components> to register extension explicitly:
Unfortunately, that (again) doesn't work for `findUsagesProvider` and (sometimes works, sometimes not) for `refactoringSupport`. It looks like because both of that extensions register default implementations:
and my ExplicitExtension for some reasons doesn't override it (bug? race conditions? ... I've been lost debugging through Idea source code...).
So, I have to use mixed solution with both ExtensionFactory and addExplicitExtension approaches:
Can't say I'm happy with the beauty of the final code. It works, but... I believe it should be a more elegant solution. Any case, I would be glad to hear any ideas and pieces of advice. And hope that will help other fellows who will stack in the future with the same problem.
PS Some promising methods might be among `com.intellij.openapi.extensions` and `com.intellij.core.CoreApplicationEnvironment` modules. In paticular: