Problem with GutterIconRenderer Follow
Hello Jetbrain Community !
I am currently developing a plugin for SonarJ based on the Idea 9.0 snapshot and have problems with GutterIconRenderer and MarkupModel.
One of the features of the plugin allows the user to switch on/off error markers in the gutter for certain problems like architecture violations or metric warnings. This is controlled by a toggle action.
My class LineMarker implements GutterIconRenderer. Every LineMarker can represent several issues. The issue with the highest priority determines the icon to be shown and the error stripe color.
Here is an extract from the code. Most of the action happens in updateHighlighterAttributes(). If the most important issue changes, the current highlighter is removed and replaced by a new one. If the last issue for the line disappears, the highlighter is removed from the markup model.
Now unfortunately Idea calls the getIcon method even after the highlighter has been removed from the model and even after having called setGutterIconRenderer with null. So obviously the gutter implementation caches instances of GutterIconRenderer and the cache is not updated when the markup model is changed by removing highlighters. I worked around the problem by returning an empty icon, but this certainly looks like buggy behavior to me. After I removed a highlighter the associated GutterIconRenderer should not be called by Idea anymore.
I also noticed that the gutter width is only recalculated when new markers are added. Removing a marker does not cause a recalculation of the width although I think it should. (Because removing a marker could make the width shrink)
Or am I using the API in the wrong way. Any help is highly appreciated.
class LineMarker extends GutterIconRenderer
{
private final MarkerVisibilityManager visibilityManager;
private MarkupModel markupModel;
private RangeHighlighter highlighter, oldHighlighter;
private int line;
private List<ProblemManager.Issue> issues = new ArrayList<ProblemManager.Issue>();
private ProblemManager.Issue currentIssue = null;
// ...
@NotNull
@Override
public Icon getIcon()
{
if (currentIssue == null)
{
System.out.println("bad access: "+this.toString());
return IconLoader.getIcon("/icons/empty.gif");
}
return currentIssue.getIssueClass().getIcon();
}
private void updateHighlighterAttributes()
{
if (markupModel != null)
{
if (currentIssue != null)
{
if (highlighter != null)
{
markupModel.removeHighlighter(highlighter);
}
highlighter = markupModel.addLineHighlighter(line-1, HighlighterLayer.WARNING+1, null);
highlighter.setGutterIconRenderer(this);
highlighter.setErrorStripeMarkColor(getErrorStripeColor());
highlighter.setErrorStripeTooltip(new ToolTipFetcher());
}
else if (highlighter != null)
{
System.out.println("Highlighter of "+this.toString()+" is removed !");
highlighter.setGutterIconRenderer(null);
highlighter.setErrorStripeMarkColor(null);
highlighter.setErrorStripeTooltip(null);
markupModel.removeHighlighter(highlighter);
oldHighlighter = highlighter;
highlighter = null;
}
}
}
void showMarkerClass(ProblemManager.IssueClass cls)
{
ProblemManager.Issue toBeHighlighted = getHighlightedIssue();
if (toBeHighlighted != currentIssue)
{
currentIssue = toBeHighlighted;
updateHighlighterAttributes();
}
}
void hideMarkerClass(ProblemManager.IssueClass cls)
{
if (currentIssue != null && currentIssue.getIssueClass() == cls)
{
currentIssue = getHighlightedIssue();
updateHighlighterAttributes();
}
}
}
Message was edited by: Alexander von Zitzewitz to add Idea version
Please sign in to leave a comment.
Working with gutter renderers can be a bit tricky. The safest way in my opinion is to update markers in batch on a file level, e.g. first remove all (your) markers and then add the ones you want to be shown. As long as you build the markers from an in-memory model you should be good.
I did something similar not so long ago in the IntelliGuard plugin. The code can be found here: http://code.google.com/p/intelliguard/source/browse/
Perhaps com.googlecode.intelliguard.action.GutterAction and the classes in the com.googlecode.intelliguard.gutter package can give some inspiration.
Thank you - I will try that approach. I have an in memory model of all the markers, so it should be easy.
Still hoping for some official comment from JetBrains. Either there is a bug or I a missing something in the API.
Hello Alexander,
Our own code is built with the assumption that GutterIconRenderers are added
by inspection passes (for example, by means of LineMarkerProvider class).
The behavior you describe does look like a bug, and I think it's worth reporting
as a YouTrack issue.
--
Dmitry Jemerov
Development Lead
JetBrains, Inc.
http://www.jetbrains.com/
"Develop with Pleasure!"
Thanks, Dmitry !
The problem is that SonarJ's analysis is mainly based on the byte code, so the plugin runs as a validation compiler. Moreover I give the user the possibility to toggle marker visibility - the user can click the toggle button anytime to hide or show markers. How do I report that as a bug?
Best regards
Alexander
Hello Alexander,
To report this as a bug, please create an issue at http://youtrack.jetbrains.net/
in the IntelliJ IDEA project.
--
Dmitry Jemerov
Development Lead
JetBrains, Inc.
http://www.jetbrains.com/
"Develop with Pleasure!"
Created Issue IDEA-64332
http://youtrack.jetbrains.net/issue/IDEA-64332?projectKey=IDEARegards
Alexander