Display popup over RangeHighlighter on mouseover


I have added some RangeHighlighters to the editor and would like display some information in a popup when the user mouse overs the highlighter.

Something like this or similiar:


Is there a way to achieve this?  Thanks in advance!



If your highlighter is generated from HighlightInfo, you can simply specify tooltip/description for it.
There's also com.intellij.codeInsight.preview.PreviewHintProvider that works on "Shift + mouse move" (ex: to show color by its defiinition).

Otherwise, you might need to register EditorMouseMotionListener and EditorMouseListener and show popup when EditorMouseEvent.getLogicalPosition points at your highlighter.
<editorFactoryMouseListener implementation="com.MyPluginListener"/>
<editorFactoryMouseMotionListener implementation="com.MyPluginListener"/>

You can use ImageOrColorPreviewManager and EditorMouseHoverPopupManager as an example.


Thanks Aleksey Pivovarov!

I'm currently adding my highlighter the following way:

RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(startoffset, endoffset, 0, new TextAttributes(JBColor.black, JBColor.WHITE, JBColor.PINK, EffectType.ROUNDED_BOX, 13), HighlighterTargetArea.EXACT_RANGE);
highlighter.setErrorStripeTooltip("some string");

Is it possible to use both RangeHighlighter for ErrorStripeTooltip and HighlightInfo for tooltip/description in the editor? Basically I want to display the same content set in the ErrorStripeTooltip when I mouse hover over highlighted range in the editor. So I want to have 2 tooltips (the error stripe tooltip, which is already there, and a tooltip over the range itself).

Hope I could describe this understandable.


HighlightInfo is created from 'com.intellij.lang.annotation.Annotation' that are created by Annotators. It can't be used together with 'addRangeHighlighter'.


So what would be most convenient way be to display a popup over the highlighted range in the editor and also keep the ErrorStripeTooltip added with the RangeHighlighter?
Should I use an Annotator?

  public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) {

Is there a way to check whether a PsiElement is highlighted? I'm asking because I just want to anotate the PsiElements which are already highlighted by RangeHighlighter.


Could you explain what you're trying to highlight in more details? It's hard to suggest anything without knowing how/why highlighting should be created and when it should be updated (file changes, referenced file changes, etc).

>Is there a way to check whether a PsiElement is highlighted?
No, annotators should not rely on other highlighters. Only on the Psi code structure.


I have integrated a tool in my plugin which checks for cyclic dependencies in the source code of a project. This tool gives me the start- and endoffset of elements in a document. I use RangeHighlighter to highlight them and show them to the user. In the update function of my plugin action I'm removing the highlights.

Currently it looks like this:

What I want to achieve additionally is to show a tooltip/popup when the user mouse hover over the rounded box with basically the same content in the errorstripe tooltip.



Is this an external tool or your own plugin? If it's external tool, consider using com.intellij.lang.annotation.ExternalAnnotator


It's a jar file which I integrated in my own plugin


It is hard to suggest a solution without seeing the whole code: who calculates what and when and how and why do updates get applied to the editor. Could you share a link to your repo?


All the calculation, highlighting, etc. is being done in those two classes:




Did you consider using https://www.jetbrains.com/help/idea/2020.2/dsm-tool-window.html? AFAIU it should do the same as your plugin


No because it's a university project and I have to do this using the tool they provided me (Bachelor thesis)


Aleksey Pivovarov

I would like to use the approach with EditorMouseMotionListener and EditorMouseListener and show popup when EditorMouseEvent.getLogicalPosition points at my highlighters.

Which class, method, interface etc. should I use to create the popup and display it?


>Which class, method, interface etc. should I use to create the popup and display it?

Probably, JBPopupFactory.getInstance().createComponentPopupBuilder(...) ... .createPopup().showInBestPositionFor(editor).


Ok, thanks!

I can't find the method EditorMouseEvent.getLogicalPosition. Are you referring to EditorMouseEvent.getMouseEvent().getLocationOnScreen()? Or am I missing something?


This method is new (since 2020.2), it's a cached version of "Editor.xyToLogicalPosition(e.getMouseEvent().getPoint())".
You can use the latter for compatibility with older IDEs.



Sorry for asking so many questions but now I have the LogicalPosition. How can I check wheter this position is between the start- and endoffset of RangeHighlighter.
Something like this maybe?

LogicalPosition pos = e.getEditor().xyToLogicalPosition(e.getMouseEvent().getPoint());
for(RangeHighlighter highlighter : myHighLighters){
int startOffsetOfHighlighter = highlighter.getStartOffset();
int endOffsetOfHighlighter = highlighter.getEndOffset();
if(startOffsetOfHighlighter <= pos && pos < endOffsetOfHighlighter) //something like this?

RangeHighlighter start/end offsets are "number of symbols since the start of file".
You can convert LogicalPosition to offset using Editor.logicalPositionToOffset.

See also https://jetbrains.org/intellij/sdk/docs/tutorials/editor_basics/coordinates_system.html

public void mouseMoved(@NotNull EditorMouseEvent e) {
if (ignoreEvent(e)) return;

for(RangeHighlighter highlighter : editorHighlighters){


JBPopupFactory.getInstance().createComponentPopupBuilder(...)... throws following exception:

2020-08-19 15:04:27,520 [  14808]  ERROR - llij.ide.plugins.PluginManager - Editor must be showing on the screen 
java.lang.AssertionError: Editor must be showing on the screen
at com.intellij.ui.popup.AbstractPopup.showInBestPositionFor(AbstractPopup.java:544)
at HoverPopupManager.mouseMoved(HoverPopupManager.java:36)
at com.intellij.openapi.editor.impl.event.EditorEventMulticasterImpl$3.lambda$mouseMoved$0(EditorEventMulticasterImpl.java:96)
at com.intellij.openapi.extensions.impl.ExtensionProcessingHelper.forEachExtensionSafe(ExtensionProcessingHelper.java:21)
at com.intellij.openapi.extensions.ExtensionPointName.forEachExtensionSafe(ExtensionPointName.java:50)
at com.intellij.openapi.editor.impl.event.EditorEventMulticasterImpl$3.mouseMoved(EditorEventMulticasterImpl.java:96)
at com.intellij.openapi.editor.impl.EditorImpl$MyMouseMotionListener.mouseMoved(EditorImpl.java:4188)
at java.desktop/java.awt.Component.processMouseMotionEvent(Component.java:6696)
at java.desktop/javax.swing.JComponent.processMouseMotionEvent(JComponent.java:3360)
at java.desktop/java.awt.Component.processEvent(Component.java:6420)
at java.desktop/java.awt.Container.processEvent(Container.java:2263)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5026)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4858)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4560)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2773)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4858)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:778)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:908)
at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.java:846)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:778)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:424)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:698)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:423)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

The content in the editor suddenly disappears.


This line takes Editor component and tries to show in in a popup. Swing does not allow to show same component twice, so editor is removed from original place (and it breaks popup showing logic later - see trace).

You can use createComponentPopupBuilder(new JLabel("text"), null) instead (and replace label with w/e should be shown in the popup).


Note that "showInBestPositionFor" is likely to show popup near caret, rather than at current mouse position.
You can use "showInScreenCoordinates" or "show(RelativePoint)" and "new RelativePoint(editor.getComponent(), mousePosition);" to show it at mouse position.
Or use Editor.logicalPositionToXY/offsetToXY to show it near start/end of the RangeHighlighter.


Thank you very much! I will hava a look at it!


These are absolute numbers. So you might need to adjust for scrollbar position by substracting Editor.getScrollingModel.getVerticalScrollOffset()/getHorizontalScrollOffset.


I have managed to make it run. I would like the popup to be instantly canceled/removed if I move the mouse away from my RangeHighlighter.

public void mouseEntered(@NotNull EditorMouseEvent event) {
// we receive MOUSE_MOVED event after MOUSE_ENTERED even if mouse wasn't physically moved,
// e.g. if a popup overlapping editor has been closed

public void mouseExited(@NotNull EditorMouseEvent event) {

public void mousePressed(@NotNull EditorMouseEvent event) {

private void clearPopups(@NotNull EditorMouseEvent event) {
List<JBPopup> popups = JBPopupFactory.getInstance().getChildPopups(event.getEditor().getComponent());
for (JBPopup popup : popups) {

private void skipMovement(){
skipMovement = true;

Am I missing something? Thanks in advance!