Best way to implement code highlighting/annotation by file+line number?

Is there a way to highlight code given line and filename, using the plugin API.

Such that a small snippet of information is displayed in conjunction with that line, ( from an external source.)

I have looked into the available methods, and it seems to be based on parsing rather than arbitrary line numbers.

thanks

13 comments

The data holds information about the code, which should be displayed at the according line. Several languages are currently supported, but constantly extended on the server-side. Therefore, it would be best, if the plugin was language independent, so it would not have to be maintained for that matter.

1

@Bmibferreira I am using a SideBarFactory to create a sidebar with annotations:

 

public class MySideBarFactory implements SideBarFactory {

Implement the createSideBar method and determine the open file:

 @Override
public JComponent createSideBar(JTextComponent editor) {
TopComponent tc = TopComponent.getRegistry().getActivated();
if (tc == null) {
LOGGER.info("No active TopComponent");
return null;
}

FileObject fileObject = tc.getLookup().lookup(FileObject.class);
if (fileObject == null) {
LOGGER.info("FileObject of current TopComponent is null");
return null;
}

MyData data = getDataForFile(fileObject);
return new MySidebarComponent(editor, data);
}

Do the painting in MySideBarComponent:

public class MySideBarComponent extends JPanel {
public MySideBarComponent(JTextComponent target, MyData data) {
// constructor stuff
}

@Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);

// Do paint action on g
}
}

//EDIT:

Don't forget to register the SideBarFactory in your layer.xml:


<filesystem>
...
<folder name="Editors">
<folder name="SideBar">
<!-- See: https://blogs.oracle.com/geertjan/orgnetbeanseditorsidebarfactory-part-1 -->
<file name="com-my-package-MySideBarFactory.instance">
<attr name="position" intvalue="1580"/>
</file>
</folder>
</folder>
</filesystem>

 

1

It seems com.intellij.lang.annotation.ExternalAnnotator might be suitable here. You can then register highlighting without PSI using com.intellij.lang.annotation.AnnotationHolder with TextRange parameter, which can be calculated to match certain line(s) in editor.

0

Thanks for the reply @yannc76, will investigate.

0

I have the same requirements. Could you provide some more details?

The documentation seems out of date, com.intellij.lang.annotation.ExternalAnnotator is no longer an interface. How can I use the com.intellij.lang.annotation.AnnotationHolder for this? Is there any documentation beside the source code?

My plugin loads information from a server. The information contains the exact position of the text (line number/character), which has to be annotated. How would I do that?

0

Thanks for noting, we're going to update this part.

Still using ExternalAnnotator seems a solution for your problem here. Unfortunately there are not too many examples in Community Edition sources.

You'll have to map line/col information from server to a TextRange (full line or specific elements at that position) to map the results to the current file.

See DartAnalysisServerAnnotator.java in contrib repository.

0

I can't find any file called DartAnalysisServerAnnotator.java. Could you provide me with a link?

0

When I extend ExternalAnnotator, a PluginException is thrown, which says: "No key specified for extension of class FindingsAnnotator"

 
<externalAnnotator
    implementationClass="FindingsAnnotator">
</externalAnnotator>


Do I have to declare a language like it is done in the plugin.xml for DartAnalysisServerAnnotator? My plugin is independent of the used language.

0

Currently you'll have to register your annotator for every supported language explicitly. What kind of data do you provide that language/filetype does not matter at all?

0

Hi! I'm having the same problem that @Benedikt had. I need to annotate a specific line in the document but it must be language agnostic, I just want to highlight it and have a tooltip with some information about the Warning/Error on the UI. How can I do this?

0

Hi @Benedikt, thanks for your answer. I searched for SideBarFactory but I didn't found it. Is your example valid for IntelliJ? Because it seems that SideBarFactory is from Netbeans (http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-editor-lib2/org/netbeans/spi/editor/SideBarFactory.html?is-external=true)

0

@BmiBferreira You are totally right... I completely mixed it up. So if you plan to develop your plugin for Netbeans as well, you already know how to do it ;)

For Intellij IDEA, you can use an ActionGutterRenderer:

First you need to implement a FileEditorManagerListener and register it to the MessageBus, in order to get notified, when the file of the editor is changed:

public class MyFileManagerListener implements FileEditorManagerListener {
public MyFileManagerListener() {
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
MessageBusConnection connection = bus.connect();
connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, this);
}

@Override
public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
MyData data = getDataForFile(file);
ApplicationManager.getApplication().invokeLater(() -> {
Editor selectedEditor = manager.getSelectedTextEditor();
if (selectedEditor != null && selectedEditor instanceof EditorEx) {
EditorEx editor = (EditorEx) selectedEditor;
MarkupModel model = editor.getMarkupModel();
RangeHighlighter highlighter = model.addRangeHighlighter(data.lineStartOffset, data.lineEndOffset, 1, null, HighlighterTargetArea.LINES_IN_RANGE);
highlighter.setLineMarkerRenderer(new MyGutterRenderer(editor, myData));
}
}
}
}

Second you need to implement your renderer:

public class MyGutterRenderer implements ActiveGutterRenderer {

Implement the required methods:

@Override
public void paint(Editor editor, Graphics g, Rectangle r) {
g.setColor(new JBColor(new Color(255, 255, 255), new Color(200, 200, 200)));
g.fillRect(r.x, r.y, 3, r.height);
}

@Override
public void doAction(Editor editor, MouseEvent e) {
e.consume();
// react on mouse events on the gutter
}

@Override
public boolean canDoAction(MouseEvent e) {
// return true, when the mouse event is valid for an action. This will then call doAction.
}

Now you need to register both classes as ProjectComponents in your plugin.xml:

<project-components>
<component>
<implementation-class>
com.mycompany.MyRenderer
</implementation-class>
</component>
<component>
<implementation-class>
com.mycompany.MyFileManagerListener
</implementation-class>
</component>
...

Hope this helps.

0

Please sign in to leave a comment.