diff --git a/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java b/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java index cff4e4356286..3cb968f5c64e 100644 --- a/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java +++ b/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java @@ -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) { } } diff --git a/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java b/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java index f9c8d65f70db..c3dbe9075f4e 100644 --- a/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java +++ b/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java @@ -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)event -> { + link.onClick(event); + } + ); + } + } + protected @Nullable TextRange getCurrentRange() { return myCurrentRange; } diff --git a/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodePresentationConfigurator.java b/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodePresentationConfigurator.java index 8def3848372d..110f2ffe5ec4 100644 --- a/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodePresentationConfigurator.java +++ b/platform/xdebugger-impl/ui/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodePresentationConfigurator.java @@ -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);