blob: d72c967f67be792e1363d362be0cbabe86e87652 [file] [log] [blame]
/*
* Copyright 2000-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.debugger.ui.impl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.debugger.ui.tree.ValueDescriptor;
import com.intellij.icons.AllIcons;
import com.intellij.ide.highlighter.JavaHighlightingColors;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.*;
import com.intellij.util.PlatformIcons;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
public class DebuggerTreeRenderer extends ColoredTreeCellRenderer {
private static final SimpleTextAttributes DEFAULT_ATTRIBUTES = new SimpleTextAttributes(Font.PLAIN, null);
private static final SimpleTextAttributes SPECIAL_NODE_ATTRIBUTES = new SimpleTextAttributes(Font.PLAIN, new JBColor(Color.lightGray, Gray._130));
private static final SimpleTextAttributes OBJECT_ID_HIGHLIGHT_ATTRIBUTES = new SimpleTextAttributes(Font.PLAIN, new JBColor(Color.lightGray, Gray._130));
public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
final DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl) value;
if (node != null) {
final SimpleColoredText text = node.getText();
if (text != null) {
text.appendToComponent(this);
}
setIcon(node.getIcon());
}
}
@Nullable
public static Icon getDescriptorIcon(NodeDescriptorImpl descriptor) {
Icon nodeIcon = null;
if (descriptor instanceof ThreadGroupDescriptorImpl) {
nodeIcon = (((ThreadGroupDescriptorImpl)descriptor).isCurrent() ? AllIcons.Debugger.ThreadGroupCurrent : AllIcons.Debugger.ThreadGroup);
}
else if (descriptor instanceof ThreadDescriptorImpl) {
ThreadDescriptorImpl threadDescriptor = (ThreadDescriptorImpl)descriptor;
nodeIcon = threadDescriptor.getIcon();
}
else if (descriptor instanceof StackFrameDescriptorImpl) {
StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)descriptor;
nodeIcon = stackDescriptor.getIcon();
}
else if (descriptor instanceof ValueDescriptorImpl) {
nodeIcon = getValueIcon((ValueDescriptorImpl)descriptor);
}
else if (descriptor instanceof MessageDescriptor) {
MessageDescriptor messageDescriptor = (MessageDescriptor)descriptor;
if (messageDescriptor.getKind() == MessageDescriptor.ERROR) {
nodeIcon = XDebuggerUIConstants.ERROR_MESSAGE_ICON;
}
else if (messageDescriptor.getKind() == MessageDescriptor.INFORMATION) {
nodeIcon = XDebuggerUIConstants.INFORMATION_MESSAGE_ICON;
}
else if (messageDescriptor.getKind() == MessageDescriptor.SPECIAL) {
nodeIcon = null;
}
}
else if (descriptor instanceof StaticDescriptorImpl) {
nodeIcon = AllIcons.Nodes.Static;
}
return nodeIcon;
}
public static Icon getValueIcon(ValueDescriptorImpl valueDescriptor) {
Icon nodeIcon;
if (valueDescriptor instanceof FieldDescriptorImpl && ((FieldDescriptorImpl)valueDescriptor).isStatic()) {
nodeIcon = PlatformIcons.FIELD_ICON;
}
else if (valueDescriptor.isArray()) {
nodeIcon = AllIcons.Debugger.Db_array;
}
else if (valueDescriptor.isPrimitive()) {
nodeIcon = AllIcons.Debugger.Db_primitive;
}
else {
if (valueDescriptor instanceof WatchItemDescriptor) {
nodeIcon = AllIcons.Debugger.Watch;
}
else {
nodeIcon = AllIcons.Debugger.Value;
}
}
final Icon valueIcon = valueDescriptor.getValueIcon();
if (nodeIcon != null && valueIcon != null) {
final RowIcon composite = new RowIcon(2);
composite.setIcon(nodeIcon, 0);
composite.setIcon(valueIcon, 1);
nodeIcon = composite;
}
return nodeIcon;
}
public static SimpleColoredText getDescriptorText(DebuggerContextImpl debuggerContext,
NodeDescriptorImpl descriptor,
EditorColorsScheme colorsScheme,
boolean multiline) {
return getDescriptorText(debuggerContext, descriptor, colorsScheme, multiline, true);
}
public static SimpleColoredText getDescriptorText(final DebuggerContextImpl debuggerContext, NodeDescriptorImpl descriptor, boolean multiline) {
return getDescriptorText(debuggerContext, descriptor, DebuggerUIUtil.getColorScheme(null), multiline, true);
}
public static SimpleColoredText getDescriptorTitle(final DebuggerContextImpl debuggerContext, NodeDescriptorImpl descriptor) {
return getDescriptorText(debuggerContext, descriptor, DebuggerUIUtil.getColorScheme(null), false, false);
}
private static SimpleColoredText getDescriptorText(DebuggerContextImpl debuggerContext,
NodeDescriptorImpl descriptor,
EditorColorsScheme colorScheme,
boolean multiline,
boolean appendValue) {
SimpleColoredText descriptorText = new SimpleColoredText();
String text;
String nodeName;
if (descriptor == null) {
text = "";
nodeName = null;
}
else {
text = descriptor.getLabel();
nodeName = descriptor.getName();
}
if(text.equals(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE)) {
descriptorText.append(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE, XDebuggerUIConstants.COLLECTING_DATA_HIGHLIGHT_ATTRIBUTES);
return descriptorText;
}
if (descriptor instanceof ValueDescriptor) {
final ValueMarkup markup = ((ValueDescriptor)descriptor).getMarkup(debuggerContext.getDebugProcess());
if (markup != null) {
descriptorText.append("[" + markup.getText() + "] ", new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, markup.getColor()));
}
}
String[] strings = breakString(text, nodeName);
if (strings[0] != null) {
if (descriptor instanceof MessageDescriptor && ((MessageDescriptor)descriptor).getKind() == MessageDescriptor.SPECIAL) {
descriptorText.append(strings[0], SPECIAL_NODE_ATTRIBUTES);
}
else {
descriptorText.append(strings[0], DEFAULT_ATTRIBUTES);
}
}
if (strings[1] != null) {
descriptorText.append(strings[1], XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES);
}
if (strings[2] != null) {
if (descriptor instanceof ValueDescriptorImpl) {
if(multiline && strings[2].indexOf('\n') >=0) {
strings = breakString(strings[2], "=");
if(strings[2] != null) {
strings[2] = strings[0] + strings[1] + "\n" + strings[2];
}
}
ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
String valueLabel = valueDescriptor.getValueLabel();
strings = breakString(strings[2], valueLabel);
if (strings[0] != null) {
descriptorText.append(strings[0], DEFAULT_ATTRIBUTES);
}
if (appendValue && strings[1] != null) {
if(valueLabel != null && StringUtil.startsWithChar(valueLabel, '{') && valueLabel.indexOf('}') > 0 && !StringUtil.endsWithChar(valueLabel, '}')) {
int idx = valueLabel.indexOf('}');
String objectId = valueLabel.substring(0, idx + 1);
valueLabel = valueLabel.substring(idx + 1);
descriptorText.append(objectId, OBJECT_ID_HIGHLIGHT_ATTRIBUTES);
}
valueLabel = DebuggerUtilsEx.truncateString(valueLabel);
final SimpleTextAttributes valueLabelAttribs;
if (valueDescriptor.isDirty()) {
valueLabelAttribs = XDebuggerUIConstants.CHANGED_VALUE_ATTRIBUTES;
}
else {
TextAttributes attributes = null;
if (valueDescriptor.isNull()){
attributes = colorScheme.getAttributes(JavaHighlightingColors.KEYWORD);
}
else if (valueDescriptor.isString()) {
attributes = colorScheme.getAttributes(JavaHighlightingColors.STRING);
}
valueLabelAttribs = attributes != null? SimpleTextAttributes.fromTextAttributes(attributes) : DEFAULT_ATTRIBUTES;
}
final EvaluateException exception = descriptor.getEvaluateException();
if(exception != null) {
final String errorMessage = exception.getMessage();
if(valueLabel.endsWith(errorMessage)) {
appendValueTextWithEscapesRendering(descriptorText, valueLabel.substring(0, valueLabel.length() - errorMessage.length()), valueLabelAttribs, colorScheme);
descriptorText.append(errorMessage, XDebuggerUIConstants.EXCEPTION_ATTRIBUTES);
}
else {
appendValueTextWithEscapesRendering(descriptorText, valueLabel, valueLabelAttribs, colorScheme);
descriptorText.append(errorMessage, XDebuggerUIConstants.EXCEPTION_ATTRIBUTES);
}
}
else {
if(valueLabel.equals(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE)) {
descriptorText.append(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE, XDebuggerUIConstants.COLLECTING_DATA_HIGHLIGHT_ATTRIBUTES);
}
else {
appendValueTextWithEscapesRendering(descriptorText, valueLabel, valueLabelAttribs, colorScheme);
}
}
}
}
else {
descriptorText.append(strings[2], DEFAULT_ATTRIBUTES);
}
}
return descriptorText;
}
private static void appendValueTextWithEscapesRendering(SimpleColoredText descriptorText,
String valueText,
SimpleTextAttributes attribs,
EditorColorsScheme colorScheme) {
SimpleTextAttributes escapeAttribs = null;
final StringBuilder buf = new StringBuilder();
boolean slashFound = false;
for (int idx= 0; idx < valueText.length(); idx++) {
final char ch = valueText.charAt(idx);
if (slashFound) {
slashFound = false;
if (ch == '\\' || ch == '\"' || ch == 'b'|| ch == 't'|| ch == 'n'|| ch == 'f'|| ch == 'r' ) {
if (buf.length() > 0) {
descriptorText.append(buf.toString(), attribs);
buf.setLength(0);
}
if (escapeAttribs == null) { // lazy init
TextAttributes fromHighlighter = colorScheme.getAttributes(JavaHighlightingColors.VALID_STRING_ESCAPE);
if (fromHighlighter != null) {
escapeAttribs = SimpleTextAttributes.fromTextAttributes(fromHighlighter);
}
else {
escapeAttribs = DEFAULT_ATTRIBUTES.derive(SimpleTextAttributes.STYLE_BOLD, JBColor.GRAY, null, null);
}
}
if (ch != '\\' && ch != '\"') {
descriptorText.append("\\", escapeAttribs);
}
descriptorText.append(String.valueOf(ch), escapeAttribs);
}
else {
buf.append('\\').append(ch);
}
}
else {
if (ch == '\\') {
slashFound = true;
}
else {
buf.append(ch);
}
}
}
if (buf.length() > 0) {
descriptorText.append(buf.toString(), attribs);
}
}
private static String[] breakString(String source, String substr) {
if (substr != null && substr.length() > 0) {
int index = Math.max(source.indexOf(substr), 0);
String prefix = (index > 0) ? source.substring(0, index) : null;
index += substr.length();
String suffix = (index < source.length() - 1) ? source.substring(index) : null;
return new String[]{prefix, substr, suffix};
}
return new String[]{source, null, null};
}
}