RIDER-135989 Use XValueNodeEx.addAdditionalHyperlink to show link in the popup

GitOrigin-RevId: b5f4d85b78d252e6f2ec5daf8f7c615f3c9b14c2
This commit is contained in:
Alexey Korovin
2026-02-16 19:54:44 +01:00
committed by intellij-monorepo-bot
parent 9dbced34a9
commit 2108536605
3 changed files with 87 additions and 18 deletions

View File

@@ -28,6 +28,7 @@ import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleColoredComponentWithProgress;
import com.intellij.ui.SimpleColoredText;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBPanel;
import com.intellij.util.concurrency.EdtExecutorService;
import com.intellij.util.ui.components.BorderLayoutPanel;
import com.intellij.xdebugger.XDebugSession;
@@ -36,6 +37,7 @@ import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.ExpressionInfo;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.frame.XValuePlace;
@@ -57,6 +59,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import java.awt.Point;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -187,17 +190,28 @@ public class XValueHint extends AbstractValueHint {
protected @NotNull JComponent createHintComponent(@Nullable Icon icon,
@NotNull SimpleColoredText text,
@NotNull XValuePresentation presentation,
@Nullable XFullValueEvaluator evaluator) {
@Nullable XFullValueEvaluator evaluator,
@Nullable XDebuggerTreeNodeHyperlink link) {
var panel = installInformationProperties(new BorderLayoutPanel());
SimpleColoredComponent component = HintUtil.createInformationComponent();
component.setIcon(icon);
text.appendToComponent(component);
panel.add(component);
if (evaluator != null) {
var evaluationLinkComponent = new SimpleColoredComponent();
appendEvaluatorLink(evaluator, evaluationLinkComponent);
LinkMouseListenerBase.installSingleTagOn(evaluationLinkComponent);
panel.addToRight(evaluationLinkComponent);
if (evaluator != null || link != null) {
var linksPanel = installInformationProperties(new BorderLayoutPanel());
if (link != null) {
var linkComponent = new SimpleColoredComponent();
appendAdditionalHyperlink(link, linkComponent);
LinkMouseListenerBase.installSingleTagOn(linkComponent);
linksPanel.addToLeft(linkComponent);
}
if (evaluator != null) {
var linkComponent = new SimpleColoredComponent();
appendEvaluatorLink(evaluator, linkComponent);
LinkMouseListenerBase.installSingleTagOn(linkComponent);
linksPanel.addToRight(linkComponent);
}
panel.addToRight(linksPanel);
}
return panel;
}
@@ -247,7 +261,7 @@ public class XValueHint extends AbstractValueHint {
@Override
public void evaluated(final @NotNull XValue result) {
result.computePresentation(new XValueNodePresentationConfigurator.ConfigurableXValueNodeImpl() {
result.computePresentation(new XValueNodePresentationConfigurator.ConfigurableXValueNodeExImpl() {
private XFullValueEvaluator myFullValueEvaluator;
private boolean myShown = false;
private SimpleColoredComponent mySimpleColoredComponent;
@@ -265,14 +279,14 @@ public class XValueHint extends AbstractValueHint {
SimpleColoredText text = new SimpleColoredText();
XValueNodeImpl.buildText(valuePresenter, text, false);
HintPresentation presentation = new HintPresentation(icon, text, hasChildren, valuePresenter, myFullValueEvaluator);
HintPresentation presentation = new HintPresentation(icon, text, hasChildren, valuePresenter, myFullValueEvaluator, myLink);
myHintPresentation = presentation;
if (!hasChildren) {
// show simple popup if there are no children
mySimpleColoredComponent = null;
showTooltipPopup(createHintComponent(presentation.icon(), presentation.text(),
presentation.valuePresentation(), presentation.evaluator()));
presentation.valuePresentation(), presentation.evaluator(), presentation.link()));
}
else if (getType() == ValueHintType.MOUSE_CLICK_HINT) {
// show full evaluation popup if the hint is explicitly requested
@@ -304,7 +318,7 @@ public class XValueHint extends AbstractValueHint {
mySimpleColoredComponent = createExpandableHintComponent(presentation.icon(), presentation.text(),
getShowPopupRunnable(result, presentation.evaluator()),
presentation.evaluator(), presentation.valuePresentation());
presentation.evaluator(), presentation.valuePresentation(), presentation.link());
if (mySimpleColoredComponent instanceof SimpleColoredComponentWithProgress) {
// TODO: it seems like that we are skipping "Collecting data..." this way, assuming that it will be the first presentation
// But this is not a correct way, UI should send "Collecting data..." presentation instead of the backend
@@ -327,13 +341,13 @@ public class XValueHint extends AbstractValueHint {
HintPresentation presentation = new HintPresentation(
myHintPresentation.icon(), myHintPresentation.text(), myHintPresentation.hasChildren(),
myHintPresentation.valuePresentation(), fullValueEvaluator
myHintPresentation.valuePresentation(), fullValueEvaluator, myHintPresentation.link()
);
myHintPresentation = presentation;
if (!presentation.hasChildren()) {
showTooltipPopup(createHintComponent(presentation.icon(), presentation.text(),
presentation.valuePresentation(), presentation.evaluator()));
presentation.valuePresentation(), presentation.evaluator(), presentation.link()));
return;
}
@@ -360,7 +374,7 @@ public class XValueHint extends AbstractValueHint {
var previousPreferredWidth = mySimpleColoredComponent.getPreferredSize().width;
mySimpleColoredComponent.clear();
fillSimpleColoredComponent(mySimpleColoredComponent, previousIcon, presentation.text(), presentation.evaluator());
fillSimpleColoredComponent(mySimpleColoredComponent, previousIcon, presentation.text(), presentation.evaluator(), presentation.link());
var delta = mySimpleColoredComponent.getPreferredSize().width - previousPreferredWidth;
if (delta > 0) {
@@ -368,6 +382,29 @@ public class XValueHint extends AbstractValueHint {
}
return true;
}
private @Nullable XDebuggerTreeNodeHyperlink myLink;
@Override
public void clearAdditionalHyperlinks() {
myLink = null;
}
@Override
public void addAdditionalHyperlink(@NotNull XDebuggerTreeNodeHyperlink link) {
myLink = link;
}
@Override
public void clearFullValueEvaluator() {
myFullValueEvaluator = null;
}
@Override
public @NotNull XValue getXValue() {
return result;
}
}, XValuePlace.TOOLTIP);
}
@@ -399,6 +436,7 @@ public class XValueHint extends AbstractValueHint {
@NotNull SimpleColoredText text,
boolean hasChildren,
@NotNull XValuePresentation valuePresentation,
@Nullable XFullValueEvaluator evaluator) {
@Nullable XFullValueEvaluator evaluator,
@Nullable XDebuggerTreeNodeHyperlink link) {
}
}

View File

@@ -357,11 +357,13 @@ public abstract class AbstractValueHint {
protected SimpleColoredComponent fillSimpleColoredComponent(SimpleColoredComponent component,
Icon icon,
final SimpleColoredText text,
@Nullable XFullValueEvaluator evaluator) {
@Nullable XFullValueEvaluator evaluator,
@Nullable XDebuggerTreeNodeHyperlink link) {
HintUtil.installInformationProperties(component);
component.setIcon(icon);
component.setCursor(hintCursor());
text.appendToComponent(component);
appendAdditionalHyperlink(link, component);
appendEvaluatorLink(evaluator, component);
return component;
}
@@ -370,7 +372,7 @@ public abstract class AbstractValueHint {
final SimpleColoredText text,
final Runnable expand,
@Nullable XFullValueEvaluator evaluator) {
return createExpandableHintComponent(icon, text, expand, evaluator, null);
return createExpandableHintComponent(icon, text, expand, evaluator, null, null);
}
@ApiStatus.Internal
@@ -378,12 +380,13 @@ public abstract class AbstractValueHint {
final SimpleColoredText text,
final Runnable expand,
@Nullable XFullValueEvaluator evaluator,
@Nullable XValuePresentation valuePresenter) {
@Nullable XValuePresentation valuePresenter,
@Nullable XDebuggerTreeNodeHyperlink link) {
Icon notNullIcon = icon != null
? IconManager.getInstance().createRowIcon(UIUtil.getTreeCollapsedIcon(), icon)
: UIUtil.getTreeCollapsedIcon();
SimpleColoredComponent component = fillSimpleColoredComponent(createComponent(valuePresenter), notNullIcon, text, evaluator);
SimpleColoredComponent component = fillSimpleColoredComponent(createComponent(valuePresenter), notNullIcon, text, evaluator, link);
new ClickListener() {
@Override
public boolean onClick(@NotNull MouseEvent e, int clickCount) {
@@ -433,6 +436,18 @@ public abstract class AbstractValueHint {
}
}
protected static void appendAdditionalHyperlink(@Nullable XDebuggerTreeNodeHyperlink link, SimpleColoredComponent component) {
if (link != null) {
component.append(
link.getLinkText(),
link.getTextAttributes(),
(Consumer<MouseEvent>)event -> {
link.onClick(event);
}
);
}
}
protected @Nullable TextRange getCurrentRange() {
return myCurrentRange;
}

View File

@@ -39,6 +39,22 @@ public final class XValueNodePresentationConfigurator {
}
}
public abstract static class ConfigurableXValueNodeExImpl implements ConfigurableXValueNode, XValueNodeEx {
@Override
public void setPresentation(@Nullable Icon icon, @NonNls @Nullable String type, @NonNls @NotNull String value, boolean hasChildren) {
XValueNodePresentationConfigurator.setPresentation(icon, type, value, hasChildren, this);
}
@Override
public void setPresentation(@Nullable Icon icon, @NotNull XValuePresentation presentation, boolean hasChildren) {
XValueNodePresentationConfigurator.setPresentation(icon, presentation, hasChildren, this);
}
@Override
public void setFullValueEvaluator(@NotNull XFullValueEvaluator fullValueEvaluator) {
}
}
public static void setPresentation(@Nullable Icon icon, @NotNull XValuePresentation presentation, boolean hasChildren,
ConfigurableXValueNode node) {
doSetPresentation(icon, presentation, hasChildren, node);