2 * Copyright 2000-2015 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.xdebugger.impl.evaluate.quick;
18 import com.intellij.codeInsight.hint.HintManager;
19 import com.intellij.codeInsight.hint.HintUtil;
20 import com.intellij.execution.console.LanguageConsoleView;
21 import com.intellij.execution.impl.ConsoleViewImpl;
22 import com.intellij.execution.ui.ConsoleView;
23 import com.intellij.openapi.Disposable;
24 import com.intellij.openapi.actionSystem.ActionManager;
25 import com.intellij.openapi.actionSystem.AnActionEvent;
26 import com.intellij.openapi.actionSystem.ShortcutSet;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.editor.Editor;
30 import com.intellij.openapi.fileEditor.FileDocumentManager;
31 import com.intellij.openapi.keymap.KeymapUtil;
32 import com.intellij.openapi.project.DumbAwareAction;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.util.Disposer;
35 import com.intellij.openapi.util.Key;
36 import com.intellij.openapi.util.Pair;
37 import com.intellij.openapi.util.text.StringUtil;
38 import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase;
39 import com.intellij.openapi.vfs.VirtualFile;
40 import com.intellij.ui.SimpleColoredComponent;
41 import com.intellij.ui.SimpleColoredText;
42 import com.intellij.ui.SimpleTextAttributes;
43 import com.intellij.util.Consumer;
44 import com.intellij.xdebugger.XDebugSession;
45 import com.intellij.xdebugger.XDebuggerUtil;
46 import com.intellij.xdebugger.XSourcePosition;
47 import com.intellij.xdebugger.evaluation.ExpressionInfo;
48 import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
49 import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink;
50 import com.intellij.xdebugger.frame.XFullValueEvaluator;
51 import com.intellij.xdebugger.frame.XValue;
52 import com.intellij.xdebugger.frame.XValuePlace;
53 import com.intellij.xdebugger.frame.presentation.XValuePresentation;
54 import com.intellij.xdebugger.impl.XDebugSessionImpl;
55 import com.intellij.xdebugger.impl.actions.handlers.XDebuggerEvaluateActionHandler;
56 import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHint;
57 import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType;
58 import com.intellij.xdebugger.impl.frame.XValueMarkers;
59 import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
60 import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
61 import com.intellij.xdebugger.impl.ui.tree.nodes.XEvaluationCallbackBase;
62 import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
63 import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodePresentationConfigurator;
64 import org.jetbrains.annotations.NotNull;
65 import org.jetbrains.annotations.Nullable;
69 import java.awt.event.MouseEvent;
74 public class XValueHint extends AbstractValueHint {
75 private static final Logger LOG = Logger.getInstance(XValueHint.class);
77 private final XDebuggerEvaluator myEvaluator;
78 private final XDebugSession myDebugSession;
79 private final String myExpression;
80 private final String myValueName;
81 private final @Nullable XSourcePosition myExpressionPosition;
82 private final ExpressionInfo myExpressionInfo;
83 private Disposable myDisposable;
85 private static final Key<XValueHint> HINT_KEY = Key.create("allows only one value hint per editor");
87 public XValueHint(@NotNull Project project, @NotNull Editor editor, @NotNull Point point, @NotNull ValueHintType type,
88 @NotNull ExpressionInfo expressionInfo, @NotNull XDebuggerEvaluator evaluator,
89 @NotNull XDebugSession session) {
90 super(project, editor, point, type, expressionInfo.getTextRange());
92 myEvaluator = evaluator;
93 myDebugSession = session;
94 myExpression = XDebuggerEvaluateActionHandler.getExpressionText(expressionInfo, editor.getDocument());
95 myValueName = XDebuggerEvaluateActionHandler.getDisplayText(expressionInfo, editor.getDocument());
96 myExpressionInfo = expressionInfo;
99 ConsoleView consoleView = ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW.get(editor);
100 if (consoleView instanceof LanguageConsoleView) {
101 LanguageConsoleView console = ((LanguageConsoleView)consoleView);
102 file = console.getHistoryViewer() == editor ? console.getVirtualFile() : null;
105 file = FileDocumentManager.getInstance().getFile(editor.getDocument());
108 myExpressionPosition = file != null ? XDebuggerUtil.getInstance().createPositionByOffset(file, expressionInfo.getTextRange().getStartOffset()) : null;
112 protected boolean canShowHint() {
117 protected boolean showHint(final JComponent component) {
118 boolean result = super.showHint(component);
119 if (result && getType() == ValueHintType.MOUSE_OVER_HINT) {
120 myDisposable = Disposer.newDisposable();
121 ShortcutSet shortcut = ActionManager.getInstance().getAction("ShowErrorDescription").getShortcutSet();
122 new DumbAwareAction() {
124 public void actionPerformed(@NotNull AnActionEvent e) {
126 final Point point = new Point(myPoint.x, myPoint.y + getEditor().getLineHeight());
127 new XValueHint(getProject(), getEditor(), point, ValueHintType.MOUSE_CLICK_HINT, myExpressionInfo, myEvaluator, myDebugSession).invokeHint();
129 }.registerCustomShortcutSet(shortcut, getEditor().getContentComponent(), myDisposable);
132 XValueHint prev = getEditor().getUserData(HINT_KEY);
136 getEditor().putUserData(HINT_KEY, this);
142 protected void onHintHidden() {
143 super.onHintHidden();
144 XValueHint prev = getEditor().getUserData(HINT_KEY);
146 getEditor().putUserData(HINT_KEY, null);
148 if (myDisposable != null) {
149 Disposer.dispose(myDisposable);
155 public void hideHint() {
157 if (myDisposable != null) {
158 Disposer.dispose(myDisposable);
163 protected void evaluateAndShowHint() {
164 myEvaluator.evaluate(myExpression, new XEvaluationCallbackBase() {
166 public void evaluated(@NotNull final XValue result) {
167 result.computePresentation(new XValueNodePresentationConfigurator.ConfigurableXValueNodeImpl() {
168 private XFullValueEvaluator myFullValueEvaluator;
169 private boolean myShown = false;
172 public void applyPresentation(@Nullable Icon icon,
173 @NotNull XValuePresentation valuePresenter,
174 boolean hasChildren) {
175 if (isHintHidden()) {
179 SimpleColoredText text = new SimpleColoredText();
180 text.append(StringUtil.trimMiddle(myValueName, 200), XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES);
181 XValueNodeImpl.buildText(valuePresenter, text);
184 SimpleColoredComponent component = HintUtil.createInformationComponent();
185 text.appendToComponent(component);
186 if (myFullValueEvaluator != null) {
187 component.append(myFullValueEvaluator.getLinkText(), XDebuggerTreeNodeHyperlink.TEXT_ATTRIBUTES,
188 new Consumer<MouseEvent>() {
190 public void consume(MouseEvent event) {
191 DebuggerUIUtil.showValuePopup(myFullValueEvaluator, event, getProject(), getEditor());
194 LinkMouseListenerBase.installSingleTagOn(component);
198 else if (getType() == ValueHintType.MOUSE_CLICK_HINT) {
204 if (getType() == ValueHintType.MOUSE_OVER_HINT) {
205 text.insert(0, "(" + KeymapUtil.getFirstKeyboardShortcutText("ShowErrorDescription") + ") ",
206 SimpleTextAttributes.GRAYED_ATTRIBUTES);
209 JComponent component = createExpandableHintComponent(text, new Runnable() {
221 public void setFullValueEvaluator(@NotNull XFullValueEvaluator fullValueEvaluator) {
222 myFullValueEvaluator = fullValueEvaluator;
226 public boolean isObsolete() {
227 return isHintHidden();
229 }, XValuePlace.TOOLTIP);
233 public void errorOccurred(@NotNull final String errorMessage) {
234 ApplicationManager.getApplication().invokeLater(new Runnable() {
237 int start = 0, end = 0;
238 if (getCurrentRange() != null) {
239 start = getCurrentRange().getStartOffset();
240 end = getCurrentRange().getEndOffset();
242 HintManager.getInstance().showErrorHint(getEditor(), errorMessage, start,
243 end, HintManager.ABOVE,
244 HintManager.HIDE_BY_ESCAPE
245 | HintManager.HIDE_BY_TEXT_CHANGE
246 | HintManager.HIDE_BY_SCROLLING,
250 LOG.debug("Cannot evaluate '" + myExpression + "':" + errorMessage);
252 }, myExpressionPosition);
255 private void showTree(@NotNull XValue value) {
256 XValueMarkers<?,?> valueMarkers = ((XDebugSessionImpl)myDebugSession).getValueMarkers();
257 XDebuggerTreeCreator creator = new XDebuggerTreeCreator(myDebugSession.getProject(), myDebugSession.getDebugProcess().getEditorsProvider(),
258 myDebugSession.getCurrentPosition(), valueMarkers);
259 showTreePopup(creator, Pair.create(value, myValueName));