IDEA-175168 On-demand renderers - implemented on-demand user renderers

This commit is contained in:
Egor.Ushakov
2017-06-29 18:58:03 +03:00
parent 01eed3f786
commit 24faab93a3
7 changed files with 128 additions and 44 deletions
@@ -157,8 +157,13 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
XValuePresentation presentation = new JavaValuePresentation(
value, myValueDescriptor.getIdLabel(), exception != null ? exception.getMessage() : null, myValueDescriptor);
if (myValueDescriptor.getLastRenderer() instanceof FullValueEvaluatorProvider) {
XFullValueEvaluator evaluator = ((FullValueEvaluatorProvider)myValueDescriptor.getLastRenderer())
// TODO move inside JavaValuePresentation
Renderer lastRenderer = myValueDescriptor.getLastRenderer();
if (lastRenderer instanceof CompoundNodeRenderer) {
lastRenderer = ((CompoundNodeRenderer)lastRenderer).getLabelRenderer();
}
if (lastRenderer instanceof FullValueEvaluatorProvider) {
XFullValueEvaluator evaluator = ((FullValueEvaluatorProvider)lastRenderer)
.getFullValueEvaluator(myEvaluationContext, myValueDescriptor);
if (evaluator != null) {
node.setFullValueEvaluator(evaluator);
@@ -302,13 +307,20 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
String value = truncateToMaxLength(myValue);
Renderer lastRenderer = myValueDescriptor.getLastRenderer();
if (lastRenderer instanceof OnDemandRenderer) {
OnDemandRenderer onDemandRenderer = (OnDemandRenderer)lastRenderer;
if (OnDemandRenderer.isCalculated(myValueDescriptor)) {
lastRenderer = onDemandRenderer.getRenderer();
}
else {
return;
}
}
if (lastRenderer instanceof CompoundTypeRenderer) {
lastRenderer = ((CompoundTypeRenderer)lastRenderer).getLabelRenderer();
}
if (lastRenderer instanceof ToStringRenderer) {
if (lastRenderer instanceof LazyToStringRenderer && !((LazyToStringRenderer)lastRenderer).isCalculated(myValueDescriptor)) {
return;
}
value = StringUtil.wrapWithDoubleQuote(value);
}
renderer.renderValue(value);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,6 +59,7 @@ class CompoundRendererConfigurable extends JPanel {
private final JRadioButton myRbDefaultLabel;
private final JRadioButton myRbExpressionLabel;
private final JBCheckBox myShowTypeCheckBox;
private final JBCheckBox myOnDemandCheckBox;
private final JRadioButton myRbDefaultChildrenRenderer;
private final JRadioButton myRbExpressionChildrenRenderer;
private final JRadioButton myRbListChildrenRenderer;
@@ -88,6 +89,7 @@ class CompoundRendererConfigurable extends JPanel {
labelButtonsGroup.add(myRbExpressionLabel);
myShowTypeCheckBox = new JBCheckBox(DebuggerBundle.message("label.compound.renderer.configurable.show.type"));
myOnDemandCheckBox = new JBCheckBox(DebuggerBundle.message("label.compound.renderer.configurable.ondemand"));
myRbDefaultChildrenRenderer = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.default.renderer"));
myRbExpressionChildrenRenderer = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.expression"));
@@ -156,6 +158,8 @@ class CompoundRendererConfigurable extends JPanel {
JBUI.insetsLeft(10), 0, 0));
panel.add(myLabelEditor.getComponent(), new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, JBUI.insetsLeft(30), 0, 0));
panel.add(myOnDemandCheckBox, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, JBUI.insetsLeft(30), 0, 0));
panel.add(new JLabel(DebuggerBundle.message("label.compound.renderer.configurable.when.expanding")),
new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
@@ -218,7 +222,9 @@ class CompoundRendererConfigurable extends JPanel {
}
private void updateEnabledState() {
myLabelEditor.setEnabled(myRbExpressionLabel.isSelected());
boolean isLabelRenderer = myRbExpressionLabel.isSelected();
myLabelEditor.setEnabled(isLabelRenderer);
myOnDemandCheckBox.setEnabled(isLabelRenderer);
final boolean isChildrenExpression = myRbExpressionChildrenRenderer.isSelected();
myChildrenExpandedEditor.setEnabled(isChildrenExpression);
@@ -321,6 +327,7 @@ class CompoundRendererConfigurable extends JPanel {
if (myRbExpressionLabel.isSelected()) {
labelRenderer = new LabelRenderer();
labelRenderer.setLabelExpression(TextWithImportsImpl.fromXExpression(myLabelEditor.getExpression()));
labelRenderer.setOnDemand(myOnDemandCheckBox.isSelected());
}
renderer.setLabelRenderer(labelRenderer);
// children
@@ -362,7 +369,9 @@ class CompoundRendererConfigurable extends JPanel {
}
else {
myRbExpressionLabel.setSelected(true);
myLabelEditor.setExpression(TextWithImportsImpl.toXExpression(((LabelRenderer)labelRenderer).getLabelExpression()));
LabelRenderer lr = (LabelRenderer)labelRenderer;
myLabelEditor.setExpression(TextWithImportsImpl.toXExpression(lr.getLabelExpression()));
myOnDemandCheckBox.setSelected(lr.isOnDemand());
}
getTableModel().clear();
@@ -22,7 +22,7 @@ import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
import com.intellij.debugger.ui.tree.render.LazyToStringRenderer;
import com.intellij.debugger.ui.tree.render.OnDemandRenderer;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.util.containers.HashMap;
@@ -119,7 +119,7 @@ public abstract class NodeDescriptorImpl implements NodeDescriptor {
// TODO introduce unified way to handle this
if (myUserData != null) {
myUserData.remove(LazyToStringRenderer.LAZY_CALCULATED); // lazy flag should not be inherited
myUserData.remove(OnDemandRenderer.ON_DEMAND_CALCULATED); // calculated flag should not be inherited
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,25 +18,25 @@ package com.intellij.debugger.ui.tree.render;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.FullValueEvaluatorProvider;
import com.intellij.debugger.engine.evaluation.*;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.debugger.ui.tree.ValueDescriptor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.sun.jdi.Value;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class LabelRenderer extends TypeRenderer implements ValueLabelRenderer{
public class LabelRenderer extends TypeRenderer implements ValueLabelRenderer, FullValueEvaluatorProvider {
public static final @NonNls String UNIQUE_ID = "LabelRenderer";
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.render.ClassLabelRenderer");
public boolean myOnDemand;
private CachedEvaluator myLabelExpression = createCachedEvaluator();
@@ -62,6 +62,10 @@ public class LabelRenderer extends TypeRenderer implements ValueLabelRenderer{
public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener labelListener)
throws EvaluateException {
if (myOnDemand && !OnDemandRenderer.isCalculated(descriptor)) {
return "";
}
final Value value = descriptor.getValue();
String result;
@@ -88,6 +92,15 @@ public class LabelRenderer extends TypeRenderer implements ValueLabelRenderer{
return result;
}
@Nullable
@Override
public XFullValueEvaluator getFullValueEvaluator(EvaluationContextImpl evaluationContext, ValueDescriptorImpl valueDescriptor) {
if (myOnDemand && !OnDemandRenderer.isCalculated(valueDescriptor)) {
return OnDemandRenderer.createFullValueEvaluator("… " + getLabelExpression().getText());
}
return null;
}
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
DefaultJDOMExternalizer.readExternal(this, element);
@@ -111,4 +124,11 @@ public class LabelRenderer extends TypeRenderer implements ValueLabelRenderer{
myLabelExpression.setReferenceExpression(expression);
}
public boolean isOnDemand() {
return myOnDemand;
}
public void setOnDemand(boolean value) {
myOnDemand = value;
}
}
@@ -16,64 +16,105 @@
package com.intellij.debugger.ui.tree.render
import com.intellij.debugger.DebuggerBundle
import com.intellij.debugger.DebuggerContext
import com.intellij.debugger.engine.FullValueEvaluatorProvider
import com.intellij.debugger.engine.JavaValue
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContext
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl
import com.intellij.debugger.ui.tree.DebuggerTreeNode
import com.intellij.debugger.ui.tree.NodeDescriptor
import com.intellij.debugger.ui.tree.ValueDescriptor
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import com.intellij.xdebugger.frame.XFullValueEvaluator
import com.intellij.xdebugger.frame.XValuePlace
import com.intellij.xdebugger.impl.ui.tree.nodes.HeadlessValueEvaluationCallback
import com.sun.jdi.Type
import com.sun.jdi.Value
import javax.swing.Icon
/**
* @author egor
*/
class LazyToStringRenderer : ToStringRenderer(), FullValueEvaluatorProvider {
class OnDemandRenderer(val renderer: NodeRendererImpl) : NodeRendererImpl(), FullValueEvaluatorProvider {
init {
isEnabled = true
}
override fun getFullValueEvaluator(evaluationContext: EvaluationContextImpl?, valueDescriptor: ValueDescriptorImpl): XFullValueEvaluator? {
if (!isCalculated(valueDescriptor)) {
return object : XFullValueEvaluator(DebuggerBundle.message("message.node.toString")) {
override fun startEvaluation(callback: XFullValueEvaluator.XFullValueEvaluationCallback) {
if (callback is HeadlessValueEvaluationCallback) {
val node = callback.node
node.clearFullValueEvaluator()
valueDescriptor.putUserData(LAZY_CALCULATED, true)
node.valueContainer.computePresentation(node, XValuePlace.TREE)
}
callback.evaluated("")
}
}.setShowValuePopup(false)
}
return null
}
@Throws(EvaluateException::class)
override fun calcLabel(descriptor: ValueDescriptor, evaluationContext: EvaluationContext, listener: DescriptorLabelListener): String? {
if (!isCalculated(descriptor)) {
return ""
}
return super.calcLabel(descriptor, evaluationContext, listener)
return renderer.calcLabel(descriptor, evaluationContext, listener)
}
fun isCalculated(descriptor: ValueDescriptor): Boolean {
return LAZY_CALCULATED.get(descriptor, false)
override fun getFullValueEvaluator(evaluationContext: EvaluationContextImpl?, valueDescriptor: ValueDescriptorImpl): XFullValueEvaluator? {
if (!isCalculated(valueDescriptor)) {
return createFullValueEvaluator(DebuggerBundle.message("message.node.toString"))
}
return null
}
override fun buildChildren(value: Value?, builder: ChildrenBuilder?, evaluationContext: EvaluationContext?) {
renderer.buildChildren(value, builder, evaluationContext)
}
override fun getChildValueExpression(node: DebuggerTreeNode?, context: DebuggerContext?): PsiElement {
return renderer.getChildValueExpression(node, context)
}
override fun isExpandable(value: Value?, evaluationContext: EvaluationContext?, parentDescriptor: NodeDescriptor?): Boolean {
return renderer.isExpandable(value, evaluationContext, parentDescriptor)
}
override fun calcValueIcon(descriptor: ValueDescriptor?,
evaluationContext: EvaluationContext?,
listener: DescriptorLabelListener?): Icon? {
return renderer.calcValueIcon(descriptor, evaluationContext, listener)
}
override fun isApplicable(type: Type?): Boolean {
return renderer.isApplicable(type)
}
override fun getName(): String {
return "Lazy" + super.getName()
return "OnDemand" + renderer.getName()
}
override fun getUniqueId(): String {
return "Lazy" + super.getUniqueId()
return "OnDemand" + renderer.getUniqueId()
}
companion object {
@JvmStatic
fun createFullValueEvaluator(text: String): XFullValueEvaluator {
return object : XFullValueEvaluator(text) {
override fun startEvaluation(callback: XFullValueEvaluator.XFullValueEvaluationCallback) {
if (callback is HeadlessValueEvaluationCallback) {
val node = callback.node
node.clearFullValueEvaluator()
setCalculated((node.valueContainer as JavaValue).descriptor)
node.valueContainer.computePresentation(node, XValuePlace.TREE)
}
callback.evaluated("")
}
}.setShowValuePopup(false)
}
@JvmField
val LAZY_CALCULATED = Key.create<Boolean>("LAZY_CALCULATED")
val ON_DEMAND_CALCULATED = Key.create<Boolean>("ON_DEMAND_CALCULATED")
@JvmStatic
fun isCalculated(descriptor: ValueDescriptor): Boolean {
return ON_DEMAND_CALCULATED.get(descriptor, false)
}
@JvmStatic
fun setCalculated(descriptor: ValueDescriptor) {
ON_DEMAND_CALCULATED.set(descriptor, true)
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@ public abstract class XFullValueEvaluator {
return myShowValuePopup;
}
@NotNull
public XFullValueEvaluator setShowValuePopup(boolean value) {
myShowValuePopup = value;
return this;
@@ -208,6 +208,7 @@ label.base.renderer.configurable.enable.toString=Enable 't&oString()' object vie
label.base.renderer.configurable.all.overriding=For all classes that override 'toString()' method
label.base.renderer.configurable.classes.from.list=For classes from the list:
label.compound.renderer.configurable.show.type=Show type and object id
label.compound.renderer.configurable.ondemand=On-demand
label.compound.renderer.configurable.use.default.renderer=Use default renderer
label.compound.renderer.configurable.use.expression=Use following expression:
label.compound.renderer.configurable.use.expression.list=Use list of expressions: