External annotators for a base language are ignored when another external annotator exists for a more specific flavor
Hello,
It seems that external annotators for a base language are ignored when another external annotator exists for a more specific flavor. This leads to conflicting behavior in plugins that act as external annotators, such as SonarLint, ESLint, Flow. This is a new behavior in 2016.3, there was no such problem in 2016.2.x and earlier.
For example given this scenario:
- External annotator X1 is registered for "JavaScript"
- External annotator X2 is registered for "JavaScript" and "Flow JS"
Then, when annotating a JavaScript file, only X2 will be used, X1 will be ignored.
This is a problem, because for example SonarLint is registered for all JavaScript versions and flavors, but ESLint and Flow are registered for less specific flavors. As a result only SonarLint annotations are shown, others are not. It seems a plugin can mute another plugin by registering itself for a more specific flavor, which is clearly not desirable.
This is a regression, because up until 2016.2.5 there was no problem, all annotations were shown from these plugins.
The problem is reproducible by having the ESLint plugin together with a custom plugin registering external annotators:
<externalAnnotator language="JavaScript" implementationClass="..."/>
<externalAnnotator language="Flow JS" implementationClass="..."/>
The annotations by ESLint will not be visible in 2016.3, even though they are visible in 2016.2.5.
I created a ticket for this: https://youtrack.jetbrains.com/issue/WEB-24475
I hope somebody can look at it soon, as not being able to use multiple plugins is hurting the efforts of developers to catch the most number of defects and bad practices, unless they downgrade to 2016.2.x.
Please sign in to leave a comment.
I've pushed a change to master that will collect external annotators for all base languages. It will be available in the second 2017.1 EAP, let's see how it goes
That's how language extension points used to work for quite a while, are you sure that you haven't started registering new annotator recently?
It's not always correct to pick language extensions (even annotators) from a parent language (e.g. not all linters can handle Flow)
Is there a reason you need to register one annotator for both JS and Flow? Flow's parent is JavaScript, so it should be enough to register it just once
> That's how language extension points used to work for quite a while, are you sure that you haven't started registering new annotator recently?
No, we haven't registered new annotators recently. My original assumption was that something changed in SonarLint at some point, so I tested versions of this year and last year against IntelliJ 2016.3. The behavior was always the same, ESLint annotations not visible. Then I repeated my tests in older IntelliJ versions, and confirmed that all annotations of all plugins are visible in 2016.2.5 and other earlier versions I tested. It seems evident that something has changed in 2016.3.
> Is there a reason you need to register one annotator for both JS and Flow? Flow's parent is JavaScript, so it should be enough to register it just once
The question is why does pluginY (BTW can you post the link to it?) register annotator for both JavaScript and Flow, just JavaScript should be fine.
Nope, I'm suggesting the other way around: register as a less specific flavour.
Here is the link to the SonarLint plugin: https://plugins.jetbrains.com/plugin/7973
The problem of registering only to a less specific flavour (such as JavaScript) is that if there is another plugin with an annotator registered to Flow (or any other flavour of Javascript), SonarLint's annotator won't be loaded and its highlightings won't show up!
As explained by Janos, it seems that there is an important regression in IDEA 2016.3 in the way external annotators are handled. The collection of external annotators is done from bottom-up (more specific first) in the hierarchy of languages. As soon as one annotator is found for a language, all other annotators of all base languages up in the hierarchy are ignored.
So for example if we have 3 plugins registering annotators for JavaScript, and one plugin with an annotator for Flow, only the latter will be used.
This flawed strategy is defined by the following method: https://github.com/JetBrains/intellij-community/blob/master/platform/core-api/src/com/intellij/lang/LanguageExtension.java#L76
This issue will potentially have an impact on any plugin using external annotators registered to a language which is not the most specific one, since those annotators might never be used.