2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.debugger.ui.impl;
18 import com.intellij.debugger.engine.evaluation.EvaluateException;
19 import com.intellij.debugger.impl.DebuggerContextImpl;
20 import com.intellij.debugger.impl.DebuggerUtilsEx;
21 import com.intellij.debugger.ui.impl.watch.*;
22 import com.intellij.debugger.ui.tree.ValueDescriptor;
23 import com.intellij.icons.AllIcons;
24 import com.intellij.ide.highlighter.JavaHighlightingColors;
25 import com.intellij.openapi.editor.colors.EditorColorsScheme;
26 import com.intellij.openapi.editor.markup.TextAttributes;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.ui.*;
29 import com.intellij.util.PlatformIcons;
30 import com.intellij.xdebugger.XDebugSession;
31 import com.intellij.xdebugger.XDebuggerManager;
32 import com.intellij.xdebugger.impl.XDebugSessionImpl;
33 import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
34 import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
35 import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
36 import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
43 public class DebuggerTreeRenderer extends ColoredTreeCellRenderer {
45 private static final SimpleTextAttributes DEFAULT_ATTRIBUTES = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, null);
46 private static final SimpleTextAttributes SPECIAL_NODE_ATTRIBUTES = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, new JBColor(Color.lightGray, Gray._130));
47 private static final SimpleTextAttributes OBJECT_ID_HIGHLIGHT_ATTRIBUTES = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, new JBColor(Color.lightGray, Gray._130));
49 public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
50 final DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl) value;
53 final SimpleColoredText text = node.getText();
55 text.appendToComponent(this);
57 setIcon(node.getIcon());
62 public static Icon getDescriptorIcon(NodeDescriptorImpl descriptor) {
64 if (descriptor instanceof ThreadGroupDescriptorImpl) {
65 nodeIcon = (((ThreadGroupDescriptorImpl)descriptor).isCurrent() ? AllIcons.Debugger.ThreadGroupCurrent : AllIcons.Debugger.ThreadGroup);
67 else if (descriptor instanceof ThreadDescriptorImpl) {
68 ThreadDescriptorImpl threadDescriptor = (ThreadDescriptorImpl)descriptor;
69 nodeIcon = threadDescriptor.getIcon();
71 else if (descriptor instanceof StackFrameDescriptorImpl) {
72 StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)descriptor;
73 nodeIcon = stackDescriptor.getIcon();
75 else if (descriptor instanceof ValueDescriptorImpl) {
76 nodeIcon = getValueIcon((ValueDescriptorImpl)descriptor);
78 else if (descriptor instanceof MessageDescriptor) {
79 MessageDescriptor messageDescriptor = (MessageDescriptor)descriptor;
80 if (messageDescriptor.getKind() == MessageDescriptor.ERROR) {
81 nodeIcon = XDebuggerUIConstants.ERROR_MESSAGE_ICON;
83 else if (messageDescriptor.getKind() == MessageDescriptor.INFORMATION) {
84 nodeIcon = XDebuggerUIConstants.INFORMATION_MESSAGE_ICON;
86 else if (messageDescriptor.getKind() == MessageDescriptor.SPECIAL) {
90 else if (descriptor instanceof StaticDescriptorImpl) {
91 nodeIcon = AllIcons.Nodes.Static;
97 public static Icon getValueIcon(ValueDescriptorImpl valueDescriptor) {
99 if (valueDescriptor instanceof FieldDescriptorImpl) {
100 FieldDescriptorImpl fieldDescriptor = (FieldDescriptorImpl)valueDescriptor;
101 nodeIcon = PlatformIcons.FIELD_ICON;
102 if (fieldDescriptor.getField().isFinal()) {
103 nodeIcon = new LayeredIcon(nodeIcon, AllIcons.Nodes.FinalMark);
105 if (fieldDescriptor.isStatic()) {
106 nodeIcon = new LayeredIcon(nodeIcon, AllIcons.Nodes.StaticMark);
109 else if (valueDescriptor instanceof ThrownExceptionValueDescriptorImpl) {
110 nodeIcon = AllIcons.Nodes.ExceptionClass;
112 else if (valueDescriptor instanceof MethodReturnValueDescriptorImpl) {
113 nodeIcon = AllIcons.Debugger.WatchLastReturnValue;
115 else if (isParameter(valueDescriptor)) {
116 nodeIcon = PlatformIcons.PARAMETER_ICON;
118 else if (valueDescriptor.isEnumConstant()) {
119 nodeIcon = PlatformIcons.ENUM_ICON;
121 else if (valueDescriptor.isArray()) {
122 nodeIcon = AllIcons.Debugger.Db_array;
124 else if (valueDescriptor.isPrimitive()) {
125 nodeIcon = AllIcons.Debugger.Db_primitive;
128 if (valueDescriptor instanceof WatchItemDescriptor) {
129 nodeIcon = AllIcons.Debugger.Watch;
132 nodeIcon = AllIcons.Debugger.Value;
136 // if watches in variables enabled, always use watch icon
137 if (valueDescriptor instanceof WatchItemDescriptor && nodeIcon != AllIcons.Debugger.Watch) {
138 XDebugSession session = XDebuggerManager.getInstance(valueDescriptor.getProject()).getCurrentSession();
139 if (session != null) {
140 XDebugSessionTab tab = ((XDebugSessionImpl)session).getSessionTab();
141 if (tab != null && tab.isWatchesInVariables()) {
142 nodeIcon = AllIcons.Debugger.Watch;
147 final Icon valueIcon = valueDescriptor.getValueIcon();
148 if (nodeIcon != null && valueIcon != null) {
149 nodeIcon = new RowIcon(nodeIcon, valueIcon);
154 private static boolean isParameter(ValueDescriptorImpl valueDescriptor) {
155 if (valueDescriptor instanceof LocalVariableDescriptorImpl) {
157 return ((LocalVariableDescriptorImpl)valueDescriptor).getLocalVariable().getVariable().isArgument();
159 catch (EvaluateException ignored) {
162 else if (valueDescriptor instanceof ArgumentValueDescriptorImpl) {
163 return ((ArgumentValueDescriptorImpl)valueDescriptor).isParameter();
168 public static SimpleColoredText getDescriptorText(DebuggerContextImpl debuggerContext,
169 NodeDescriptorImpl descriptor,
170 EditorColorsScheme colorsScheme,
172 return getDescriptorText(debuggerContext, descriptor, colorsScheme, multiline, true);
175 public static SimpleColoredText getDescriptorText(final DebuggerContextImpl debuggerContext, NodeDescriptorImpl descriptor, boolean multiline) {
176 return getDescriptorText(debuggerContext, descriptor, DebuggerUIUtil.getColorScheme(null), multiline, true);
179 public static SimpleColoredText getDescriptorTitle(final DebuggerContextImpl debuggerContext, NodeDescriptorImpl descriptor) {
180 return getDescriptorText(debuggerContext, descriptor, DebuggerUIUtil.getColorScheme(null), false, false);
183 private static SimpleColoredText getDescriptorText(DebuggerContextImpl debuggerContext,
184 NodeDescriptorImpl descriptor,
185 EditorColorsScheme colorScheme,
187 boolean appendValue) {
188 SimpleColoredText descriptorText = new SimpleColoredText();
193 if (descriptor == null) {
198 text = descriptor.getLabel();
199 nodeName = descriptor.getName();
202 if(text.equals(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE)) {
203 descriptorText.append(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE, XDebuggerUIConstants.COLLECTING_DATA_HIGHLIGHT_ATTRIBUTES);
204 return descriptorText;
207 if (descriptor instanceof ValueDescriptor) {
208 final ValueMarkup markup = ((ValueDescriptor)descriptor).getMarkup(debuggerContext.getDebugProcess());
209 if (markup != null) {
210 descriptorText.append("[" + markup.getText() + "] ", new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, markup.getColor()));
214 String[] strings = breakString(text, nodeName);
216 if (strings[0] != null) {
217 if (descriptor instanceof MessageDescriptor && ((MessageDescriptor)descriptor).getKind() == MessageDescriptor.SPECIAL) {
218 descriptorText.append(strings[0], SPECIAL_NODE_ATTRIBUTES);
221 descriptorText.append(strings[0], DEFAULT_ATTRIBUTES);
224 if (strings[1] != null) {
225 descriptorText.append(strings[1], XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES);
227 if (strings[2] != null) {
228 if (descriptor instanceof ValueDescriptorImpl) {
229 if(multiline && strings[2].indexOf('\n') >=0) {
230 strings = breakString(strings[2], "=");
231 if(strings[2] != null) {
232 strings[2] = strings[0] + strings[1] + "\n" + strings[2];
237 ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
238 String valueLabel = valueDescriptor.getValueLabel();
240 strings = breakString(strings[2], valueLabel);
241 if (strings[0] != null) {
242 descriptorText.append(strings[0], DEFAULT_ATTRIBUTES);
244 if (appendValue && strings[1] != null) {
245 if(valueLabel != null && StringUtil.startsWithChar(valueLabel, '{') && valueLabel.indexOf('}') > 0 && !StringUtil.endsWithChar(valueLabel, '}')) {
246 int idx = valueLabel.indexOf('}');
247 String objectId = valueLabel.substring(0, idx + 1);
248 valueLabel = valueLabel.substring(idx + 1);
249 descriptorText.append(objectId, OBJECT_ID_HIGHLIGHT_ATTRIBUTES);
252 valueLabel = DebuggerUtilsEx.truncateString(valueLabel);
254 final SimpleTextAttributes valueLabelAttribs;
255 if (valueDescriptor.isDirty()) {
256 valueLabelAttribs = XDebuggerUIConstants.CHANGED_VALUE_ATTRIBUTES;
259 TextAttributes attributes = null;
260 if (valueDescriptor.isNull()){
261 attributes = colorScheme.getAttributes(JavaHighlightingColors.KEYWORD);
263 else if (valueDescriptor.isString()) {
264 attributes = colorScheme.getAttributes(JavaHighlightingColors.STRING);
266 valueLabelAttribs = attributes != null? SimpleTextAttributes.fromTextAttributes(attributes) : DEFAULT_ATTRIBUTES;
269 final EvaluateException exception = descriptor.getEvaluateException();
270 if(exception != null) {
271 final String errorMessage = exception.getMessage();
272 if(valueLabel.endsWith(errorMessage)) {
273 appendValueTextWithEscapesRendering(descriptorText, valueLabel.substring(0, valueLabel.length() - errorMessage.length()), valueLabelAttribs, colorScheme);
274 descriptorText.append(errorMessage, XDebuggerUIConstants.EXCEPTION_ATTRIBUTES);
277 appendValueTextWithEscapesRendering(descriptorText, valueLabel, valueLabelAttribs, colorScheme);
278 descriptorText.append(errorMessage, XDebuggerUIConstants.EXCEPTION_ATTRIBUTES);
282 if(valueLabel.equals(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE)) {
283 descriptorText.append(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE, XDebuggerUIConstants.COLLECTING_DATA_HIGHLIGHT_ATTRIBUTES);
286 appendValueTextWithEscapesRendering(descriptorText, valueLabel, valueLabelAttribs, colorScheme);
292 descriptorText.append(strings[2], DEFAULT_ATTRIBUTES);
296 return descriptorText;
299 private static void appendValueTextWithEscapesRendering(SimpleColoredText descriptorText,
301 SimpleTextAttributes attribs,
302 EditorColorsScheme colorScheme) {
303 SimpleTextAttributes escapeAttribs = null;
304 final StringBuilder buf = new StringBuilder();
305 boolean slashFound = false;
306 for (int idx= 0; idx < valueText.length(); idx++) {
307 final char ch = valueText.charAt(idx);
310 if (ch == '\\' || ch == '\"' || ch == 'b'|| ch == 't'|| ch == 'n'|| ch == 'f'|| ch == 'r' ) {
311 if (buf.length() > 0) {
312 descriptorText.append(buf.toString(), attribs);
316 if (escapeAttribs == null) { // lazy init
317 TextAttributes fromHighlighter = colorScheme.getAttributes(JavaHighlightingColors.VALID_STRING_ESCAPE);
318 if (fromHighlighter != null) {
319 escapeAttribs = SimpleTextAttributes.fromTextAttributes(fromHighlighter);
322 escapeAttribs = DEFAULT_ATTRIBUTES.derive(SimpleTextAttributes.STYLE_BOLD, JBColor.GRAY, null, null);
326 if (ch != '\\' && ch != '\"') {
327 descriptorText.append("\\", escapeAttribs);
329 descriptorText.append(String.valueOf(ch), escapeAttribs);
332 buf.append('\\').append(ch);
344 if (buf.length() > 0) {
345 descriptorText.append(buf.toString(), attribs);
349 private static String[] breakString(String source, String substr) {
350 if (substr != null && substr.length() > 0) {
351 int index = Math.max(source.indexOf(substr), 0);
352 String prefix = (index > 0) ? source.substring(0, index) : null;
353 index += substr.length();
354 String suffix = (index < source.length() - 1) ? source.substring(index) : null;
355 return new String[]{prefix, substr, suffix};
357 return new String[]{source, null, null};