Plugin support multiple IJ APIs?

2018.2 looks like a whale of a change in terms of API churn.  It breaks my plugin at the subclassing level -- extension APIs changed with no backward support.  For instance, `MethodUsagesSearcher` and `TemplateDataElementType` both changed so that my new implementation will not be compatible with IJ versions older than 2018.2.

Is there a recommended approach to provide multiple versions of a plugin?  Or are there recommended hacks for a single plugin to programmatically provide different implementations based on IJ version?  It's getting pretty sporty in there!

Thanks.

0
3 comments

I don't think there is a "recommended" approach to this.  JetBrains has traditionally encouraged all plugin authors to support the latest builds and not worry about backward compatibility.  (i.e. "Why would you use an older version when the newer version has all of the bug-fixes?")

The intellij-haxe plugin supports IDEA API changes usually using reflection, which is brittle.  We build separate binaries for the various targets though we don't always need to.  Our older mechanisms (check out a pre-2016 build) also relied on multiple source trees where we would isolate changes between versions and include the appropriate tree in the target build.  That became tedious, so we moved to the reflection-based version, though we may eventually move back if it becomes less painful than fixing the reflection regressions.

You can find the repo here: https://github.com/HaxeFoundation/intellij-haxe.  Look at https://github.com/HaxeFoundation/intellij-haxe/tree/master/src/common/com/intellij/plugins/haxe/build to see the wrappers we currently use.  There are generated files for version detection that are built via the ant tartgets: https://github.com/HaxeFoundation/intellij-haxe/blob/f3a85f7c8f7702991d7ab00f61a50bc98569b0c7/common.xml#L192-L208,  In a purely reflection-based solution, though, there are already dynamic means to detect the version provided by the `ApplicationInfo` class.

0

Thanks for your comment, I'll checkout haxe. 

I've been going with a single release technique using reflection since 2016.x and it's worked fine.  But this technique breaks down quick with structural changes in extension interfaces and base classes, as happened in 2018.2.  I either have to pre-build an extension class per interface version, or dynamically compile them (see Dark Java).  Neither of these is desirable, but I'll probably go with Dark Java if I continue to support older IJ releases.

0

In my plugin I committed to supporting the last two years' worth of IntelliJ versions - my plugin is commercial, so that seems fair. I release separate versions for each IntelliJ version, so for version 1.2.3 of my plugin I release 1.2.3-2018.2, 1.2.3-2018.1 and so on. I have a couple of scripts for merging changes across all branches, releasing the plugin etc. Currently I have 8 branches - 2016.2, 2016.3, 2017.*, and 2018.* including the current EAP. Since 2018.2 has been GA for a while now when I release a stable version of my current EAP cycle that will be the last version to receive updates for 2016.2.

It really depends on your plugin - mine is a full language plugin so it touches a lot of APIs, and reflection is not an option. Smaller plugins can probably get away with it, at least for a while. Basically, my approach requires automating releasing, merging etc which is annoying, but once it's done it's not so bad - on an ongoing basis the only issue is fixing merge conflicts when APIs change.

0

Please sign in to leave a comment.