void (@NotNull Project project, @NotNull LinkInfo linkInfo, @NotNull Editor editor) { AnAction action = linkInfo.myAction; if (action == null) { return; } String actionName = action.getTemplatePresentation().getDescription(); Objects.requireNonNull(actionName); Ref<Balloon> ref = Ref.create(); String content = HtmlChunk.link("analyze", actionName).toString(); Color background = MessageType.INFO.getPopupBackground(); Balloon balloon = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(content, null, background, new HyperlinkAdapter() { @Override protected void hyperlinkActivated(@NotNull HyperlinkEvent e) { if (e.getDescription().equals("analyze")) { Balloon b = ref.get(); if (b != null) { Disposer.dispose(b); } linkInfo.prepare.accept(ExceptionFinder.this); PsiFile file = linkInfo.myPsiFile; ActionsCollector.getInstance().record(project, action, null, file.getLanguage()); action.actionPerformed(AnActionEvent.createFromAnAction(action, null, ActionPlaces.UNKNOWN, DataContext.EMPTY_CONTEXT)); } } }) .createBalloon(); EditorUtil.disposeWithEditor(editor, balloon); ref.set(balloon); RelativePoint point = JBPopupFactory.getInstance().guessBestPopupLocation(editor); if (linkInfo.myReason instanceof Navigatable navigatable) { int previousOffset = editor.getCaretModel().getOffset(); navigatable.navigate(true); point = JBPopupFactory.getInstance().guessBestPopupLocation(editor); editor.getCaretModel().moveToOffset(previousOffset); } if (getLine(editor, linkInfo.myReason) < getLine(editor, linkInfo.myTarget)) { Point previousPoint = point.getPoint(); balloon.show(new RelativePoint(point.getComponent(), new Point(previousPoint.x, previousPoint.y - editor.getLineHeight())), Balloon.Position.above); } else { balloon.show(point, Balloon.Position.below); } editor.getScrollingModel().addVisibleAreaListener(e -> { //skip when IDEA opens a new file and redraws it if (e.getNewRectangle().equals(e.getOldRectangle())) { return; } Disposer.dispose(balloon); }, balloon); }