mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
deleted FrameVariableTree
This commit is contained in:
@@ -20,6 +20,7 @@ import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImports;
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
|
||||
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||
import com.intellij.debugger.impl.DebuggerSession;
|
||||
@@ -31,16 +32,21 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.debugger.settings.NodeRendererSettings;
|
||||
import com.intellij.debugger.ui.breakpoints.Breakpoint;
|
||||
import com.intellij.debugger.ui.impl.FrameVariablesTree;
|
||||
import com.intellij.debugger.ui.impl.watch.*;
|
||||
import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.ColoredTextContainer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
|
||||
@@ -55,9 +61,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
@@ -246,7 +250,7 @@ public class JavaStackFrame extends XStackFrame {
|
||||
|
||||
// copied from FrameVariablesTree
|
||||
private void buildVariables(DebuggerContextImpl debuggerContext,
|
||||
EvaluationContextImpl evaluationContext,
|
||||
final EvaluationContextImpl evaluationContext,
|
||||
@NotNull DebugProcessImpl debugProcess,
|
||||
XValueChildrenList children,
|
||||
ObjectReference thisObjectReference,
|
||||
@@ -283,13 +287,12 @@ public class JavaStackFrame extends XStackFrame {
|
||||
superBuildVariables(evaluationContext, children);
|
||||
}
|
||||
else {
|
||||
final Map<String, LocalVariableProxyImpl> visibleVariables = FrameVariablesTree.getVisibleVariables(getStackFrameProxy());
|
||||
final EvaluationContextImpl evalContext = evaluationContext;
|
||||
final Map<String, LocalVariableProxyImpl> visibleVariables = getVisibleVariables(getStackFrameProxy());
|
||||
final Pair<Set<String>, Set<TextWithImports>> usedVars =
|
||||
ApplicationManager.getApplication().runReadAction(new Computable<Pair<Set<String>, Set<TextWithImports>>>() {
|
||||
@Override
|
||||
public Pair<Set<String>, Set<TextWithImports>> compute() {
|
||||
return FrameVariablesTree.findReferencedVars(ContainerUtil.union(visibleVariables.keySet(), visibleLocals), sourcePosition, evalContext);
|
||||
return findReferencedVars(ContainerUtil.union(visibleVariables.keySet(), visibleLocals), sourcePosition, evaluationContext);
|
||||
}
|
||||
});
|
||||
// add locals
|
||||
@@ -307,7 +310,7 @@ public class JavaStackFrame extends XStackFrame {
|
||||
final EvaluationContextImpl evalContextCopy = evaluationContext.createEvaluationContext(evaluationContext.getThisObject());
|
||||
evalContextCopy.setAutoLoadClasses(false);
|
||||
|
||||
final Set<TextWithImports> extraVars = computeExtraVars(usedVars, sourcePosition, evalContext);
|
||||
final Set<TextWithImports> extraVars = computeExtraVars(usedVars, sourcePosition, evaluationContext);
|
||||
|
||||
// add extra vars
|
||||
addToChildrenFrom(extraVars, children, evaluationContext);
|
||||
@@ -420,4 +423,267 @@ public class JavaStackFrame extends XStackFrame {
|
||||
return "JavaFrame position unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private static class VariablesCollector extends JavaRecursiveElementVisitor {
|
||||
private final Set<String> myVisibleLocals;
|
||||
private final TextRange myLineRange;
|
||||
private final Set<TextWithImports> myExpressions;
|
||||
private final Set<String> myVars;
|
||||
private final SourcePosition myPosition;
|
||||
private final EvaluationContextImpl myEvalContext;
|
||||
private final boolean myCollectExpressions;
|
||||
|
||||
public VariablesCollector(final Set<String> visibleLocals,
|
||||
final TextRange lineRange,
|
||||
final Set<TextWithImports> expressions,
|
||||
final Set<String> vars,
|
||||
SourcePosition position, EvaluationContextImpl evalContext) {
|
||||
myVisibleLocals = visibleLocals;
|
||||
myLineRange = lineRange;
|
||||
myExpressions = expressions;
|
||||
myVars = vars;
|
||||
myPosition = position;
|
||||
myEvalContext = evalContext;
|
||||
myCollectExpressions = XDebuggerSettingsManager.getInstance().getDataViewSettings().isAutoExpressions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitElement(final PsiElement element) {
|
||||
if (myLineRange.intersects(element.getTextRange())) {
|
||||
super.visitElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(final PsiMethodCallExpression expression) {
|
||||
if (myCollectExpressions) {
|
||||
final PsiMethod psiMethod = expression.resolveMethod();
|
||||
if (psiMethod != null && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
|
||||
myExpressions.add(new TextWithImportsImpl(expression));
|
||||
}
|
||||
}
|
||||
super.visitMethodCallExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(final PsiReferenceExpression reference) {
|
||||
if (myLineRange.intersects(reference.getTextRange())) {
|
||||
final PsiElement psiElement = reference.resolve();
|
||||
if (psiElement instanceof PsiVariable) {
|
||||
final PsiVariable var = (PsiVariable)psiElement;
|
||||
if (var instanceof PsiField) {
|
||||
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(reference, myVisibleLocals)) {
|
||||
/*
|
||||
if (var instanceof PsiEnumConstant && reference.getQualifier() == null) {
|
||||
final PsiClass enumClass = ((PsiEnumConstant)var).getContainingClass();
|
||||
if (enumClass != null) {
|
||||
final PsiExpression expression = JavaPsiFacade.getInstance(var.getProject()).getParserFacade().createExpressionFromText(enumClass.getName() + "." + var.getName(), var);
|
||||
final PsiReference ref = expression.getReference();
|
||||
if (ref != null) {
|
||||
ref.bindToElement(var);
|
||||
myExpressions.add(new TextWithImportsImpl(expression));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
myExpressions.add(new TextWithImportsImpl(reference));
|
||||
}
|
||||
*/
|
||||
final PsiModifierList modifierList = var.getModifierList();
|
||||
boolean isConstant = (var instanceof PsiEnumConstant) ||
|
||||
(modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC) && modifierList.hasModifierProperty(PsiModifier.FINAL));
|
||||
if (!isConstant) {
|
||||
myExpressions.add(new TextWithImportsImpl(reference));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (myVisibleLocals.contains(var.getName())) {
|
||||
myVars.add(var.getName());
|
||||
}
|
||||
else {
|
||||
// fix for variables used in inner classes
|
||||
if (!Comparing.equal(PsiTreeUtil.getParentOfType(reference, PsiClass.class),
|
||||
PsiTreeUtil.getParentOfType(var, PsiClass.class))) {
|
||||
myExpressions.add(new TextWithImportsImpl(reference));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitReferenceExpression(reference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
|
||||
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
|
||||
myExpressions.add(new TextWithImportsImpl(expression));
|
||||
}
|
||||
super.visitArrayAccessExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParameter(final PsiParameter parameter) {
|
||||
processVariable(parameter);
|
||||
super.visitParameter(parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(final PsiLocalVariable variable) {
|
||||
processVariable(variable);
|
||||
super.visitLocalVariable(variable);
|
||||
}
|
||||
|
||||
private void processVariable(final PsiVariable variable) {
|
||||
if (myLineRange.intersects(variable.getTextRange()) && myVisibleLocals.contains(variable.getName())) {
|
||||
myVars.add(variable.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(final PsiClass aClass) {
|
||||
// Do not step in to local and anonymous classes...
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, LocalVariableProxyImpl> getVisibleVariables(final StackFrameProxyImpl frame) throws EvaluateException {
|
||||
final Map<String, LocalVariableProxyImpl> vars = new HashMap<String, LocalVariableProxyImpl>();
|
||||
for (LocalVariableProxyImpl localVariableProxy : frame.visibleVariables()) {
|
||||
vars.put(localVariableProxy.name(), localVariableProxy);
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
private static boolean shouldSkipLine(final PsiFile file, Document doc, int line) {
|
||||
final int start = doc.getLineStartOffset(line);
|
||||
final int end = doc.getLineEndOffset(line);
|
||||
final int _start = CharArrayUtil.shiftForward(doc.getCharsSequence(), start, " \n\t");
|
||||
if (_start >= end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TextRange alreadyChecked = null;
|
||||
for (PsiElement elem = file.findElementAt(_start); elem != null && elem.getTextOffset() <= end && (alreadyChecked == null || !alreadyChecked .contains(elem.getTextRange())); elem = elem.getNextSibling()) {
|
||||
for (PsiElement _elem = elem; _elem.getTextOffset() >= _start; _elem = _elem.getParent()) {
|
||||
alreadyChecked = _elem.getTextRange();
|
||||
|
||||
if (_elem instanceof PsiDeclarationStatement) {
|
||||
final PsiElement[] declared = ((PsiDeclarationStatement)_elem).getDeclaredElements();
|
||||
for (PsiElement declaredElement : declared) {
|
||||
if (declaredElement instanceof PsiVariable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_elem instanceof PsiJavaCodeReferenceElement) {
|
||||
final PsiElement resolved = ((PsiJavaCodeReferenceElement)_elem).resolve();
|
||||
if (resolved instanceof PsiVariable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Pair<Set<String>, Set<TextWithImports>> findReferencedVars(final Set<String> visibleVars,
|
||||
final SourcePosition position,
|
||||
EvaluationContextImpl evalContext) {
|
||||
final int line = position.getLine();
|
||||
if (line < 0) {
|
||||
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
final PsiFile positionFile = position.getFile();
|
||||
if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
|
||||
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
|
||||
final VirtualFile vFile = positionFile.getVirtualFile();
|
||||
final Document doc = vFile != null ? FileDocumentManager.getInstance().getDocument(vFile) : null;
|
||||
if (doc == null || doc.getLineCount() == 0 || line > (doc.getLineCount() - 1)) {
|
||||
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
|
||||
final TextRange limit = calculateLimitRange(positionFile, doc, line);
|
||||
|
||||
int startLine = Math.max(limit.getStartOffset(), line - 1);
|
||||
startLine = Math.min(startLine, limit.getEndOffset());
|
||||
while (startLine > limit.getStartOffset() && shouldSkipLine(positionFile, doc, startLine)) {
|
||||
startLine--;
|
||||
}
|
||||
final int startOffset = doc.getLineStartOffset(startLine);
|
||||
|
||||
int endLine = Math.min(line + 2, limit.getEndOffset());
|
||||
while (endLine < limit.getEndOffset() && shouldSkipLine(positionFile, doc, endLine)) {
|
||||
endLine++;
|
||||
}
|
||||
final int endOffset = doc.getLineEndOffset(endLine);
|
||||
|
||||
final TextRange lineRange = new TextRange(startOffset, endOffset);
|
||||
if (!lineRange.isEmpty()) {
|
||||
final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), doc.getLineStartOffset(line), " \t");
|
||||
PsiElement element = positionFile.findElementAt(offset);
|
||||
if (element != null) {
|
||||
PsiMethod method = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class);
|
||||
if (method != null) {
|
||||
element = method;
|
||||
}
|
||||
else {
|
||||
PsiField field = PsiTreeUtil.getNonStrictParentOfType(element, PsiField.class);
|
||||
if (field != null) {
|
||||
element = field;
|
||||
}
|
||||
else {
|
||||
final PsiClassInitializer initializer = PsiTreeUtil.getNonStrictParentOfType(element, PsiClassInitializer.class);
|
||||
if (initializer != null) {
|
||||
element = initializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
if (element instanceof PsiCompiledElement) {
|
||||
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
else {
|
||||
final Set<String> vars = new HashSet<String>();
|
||||
final Set<TextWithImports> expressions = new HashSet<TextWithImports>();
|
||||
final PsiElementVisitor variablesCollector = new VariablesCollector(visibleVars, adjustRange(element, lineRange), expressions, vars, position, evalContext);
|
||||
element.accept(variablesCollector);
|
||||
|
||||
return Pair.create(vars, expressions);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
|
||||
private static TextRange calculateLimitRange(final PsiFile file, final Document doc, final int line) {
|
||||
final int offset = doc.getLineStartOffset(line);
|
||||
if (offset > 0) {
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(file.findElementAt(offset), PsiMethod.class, false);
|
||||
if (method != null) {
|
||||
final TextRange elemRange = method.getTextRange();
|
||||
return new TextRange(doc.getLineNumber(elemRange.getStartOffset()), doc.getLineNumber(elemRange.getEndOffset()));
|
||||
}
|
||||
}
|
||||
return new TextRange(0, doc.getLineCount() - 1);
|
||||
}
|
||||
|
||||
private static TextRange adjustRange(final PsiElement element, final TextRange originalRange) {
|
||||
final Ref<TextRange> rangeRef = new Ref<TextRange>(originalRange);
|
||||
element.accept(new JavaRecursiveElementVisitor() {
|
||||
@Override public void visitExpressionStatement(final PsiExpressionStatement statement) {
|
||||
final TextRange stRange = statement.getTextRange();
|
||||
if (originalRange.intersects(stRange)) {
|
||||
final TextRange currentRange = rangeRef.get();
|
||||
final int start = Math.min(currentRange.getStartOffset(), stRange.getStartOffset());
|
||||
final int end = Math.max(currentRange.getEndOffset(), stRange.getEndOffset());
|
||||
rangeRef.set(new TextRange(start, end));
|
||||
}
|
||||
}
|
||||
});
|
||||
return rangeRef.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,614 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Class FrameDebuggerTree
|
||||
* @author Jeka
|
||||
*/
|
||||
package com.intellij.debugger.ui.impl;
|
||||
|
||||
import com.intellij.debugger.DebuggerBundle;
|
||||
import com.intellij.debugger.DebuggerInvocationUtil;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.DebuggerUtils;
|
||||
import com.intellij.debugger.engine.SuspendManager;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImports;
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
|
||||
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||
import com.intellij.debugger.impl.DebuggerSession;
|
||||
import com.intellij.debugger.jdi.*;
|
||||
import com.intellij.debugger.settings.ViewsGeneralSettings;
|
||||
import com.intellij.debugger.ui.impl.watch.*;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import com.intellij.util.ui.tree.TreeModelAdapter;
|
||||
import com.intellij.xdebugger.XDebuggerBundle;
|
||||
import com.intellij.xdebugger.frame.XStackFrame;
|
||||
import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
|
||||
import com.sun.jdi.AbsentInformationException;
|
||||
import com.sun.jdi.ObjectCollectedException;
|
||||
import com.sun.jdi.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.event.TreeModelEvent;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.util.*;
|
||||
|
||||
public class FrameVariablesTree extends DebuggerTree {
|
||||
private static final Logger LOG = Logger.getInstance(FrameVariablesTree.class);
|
||||
|
||||
private boolean myAnyNewLocals;
|
||||
private boolean myAutoWatchMode = false;
|
||||
|
||||
private final VariablesPanel myVariablesPanel;
|
||||
|
||||
public FrameVariablesTree(@NotNull Project project) {
|
||||
this(project, null);
|
||||
}
|
||||
|
||||
public FrameVariablesTree(@NotNull Project project, @Nullable VariablesPanel variablesPanel) {
|
||||
super(project);
|
||||
|
||||
getEmptyText().setText(XDebuggerBundle.message("debugger.variables.not.available"));
|
||||
myVariablesPanel = variablesPanel;
|
||||
}
|
||||
|
||||
public boolean isAutoWatchMode() {
|
||||
return myAutoWatchMode;
|
||||
}
|
||||
|
||||
public void setAutoVariablesMode(final boolean autoWatchMode) {
|
||||
final boolean valueChanged = myAutoWatchMode != autoWatchMode;
|
||||
myAutoWatchMode = autoWatchMode;
|
||||
if (valueChanged) {
|
||||
rebuild(getDebuggerContext());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void build(DebuggerContextImpl context) {
|
||||
myAnyNewLocals = false;
|
||||
buildWhenPaused(context, new RefreshFrameTreeCommand(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreNodeState(DebuggerTreeNodeImpl node) {
|
||||
if (myAnyNewLocals) {
|
||||
final NodeDescriptorImpl descriptor = node.getDescriptor();
|
||||
final boolean isLocalVar = descriptor instanceof LocalVariableDescriptorImpl;
|
||||
descriptor.myIsSelected &= isLocalVar;
|
||||
// override this setting so that tree will scroll to new locals
|
||||
descriptor.myIsVisible = isLocalVar && descriptor.myIsSelected;
|
||||
if (!descriptor.myIsVisible) {
|
||||
descriptor.putUserData(VISIBLE_RECT, null);
|
||||
}
|
||||
}
|
||||
super.restoreNodeState(node);
|
||||
if (myAnyNewLocals && node.getDescriptor().myIsExpanded) {
|
||||
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getMutableModel().getRoot();
|
||||
scrollToVisible(root);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected DebuggerCommandImpl getBuildNodeCommand(@NotNull DebuggerTreeNodeImpl node) {
|
||||
if (node.getDescriptor() instanceof StackFrameDescriptorImpl) {
|
||||
return new BuildFrameTreeVariablesCommand(node);
|
||||
}
|
||||
return super.getBuildNodeCommand(node);
|
||||
}
|
||||
|
||||
private class BuildFrameTreeVariablesCommand extends BuildStackFrameCommand {
|
||||
public BuildFrameTreeVariablesCommand(DebuggerTreeNodeImpl stackNode) {
|
||||
super(stackNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadAction() {
|
||||
if (myVariablesPanel != null) {
|
||||
StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)getNode().getDescriptor();
|
||||
XStackFrame xStackFrame = stackDescriptor.getXStackFrame();
|
||||
myVariablesPanel.stackChanged(xStackFrame);
|
||||
if (xStackFrame != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.threadAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildVariables(final StackFrameDescriptorImpl stackDescriptor, final EvaluationContextImpl evaluationContext) throws EvaluateException {
|
||||
final DebuggerContextImpl debuggerContext = getDebuggerContext();
|
||||
final SourcePosition sourcePosition = debuggerContext.getSourcePosition();
|
||||
if (sourcePosition == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!XDebuggerSettingsManager.getInstance().getDataViewSettings().isAutoExpressions() && !myAutoWatchMode) {
|
||||
// optimization
|
||||
super.buildVariables(stackDescriptor, evaluationContext);
|
||||
}
|
||||
else {
|
||||
final Map<String, LocalVariableProxyImpl> visibleVariables = getVisibleVariables(stackDescriptor.getFrameProxy());
|
||||
final EvaluationContextImpl evalContext = debuggerContext.createEvaluationContext();
|
||||
final Pair<Set<String>, Set<TextWithImports>> usedVars =
|
||||
ApplicationManager.getApplication().runReadAction(new Computable<Pair<Set<String>, Set<TextWithImports>>>() {
|
||||
@Override
|
||||
public Pair<Set<String>, Set<TextWithImports>> compute() {
|
||||
return findReferencedVars(visibleVariables.keySet(), sourcePosition, evalContext);
|
||||
}
|
||||
});
|
||||
// add locals
|
||||
if (myAutoWatchMode) {
|
||||
for (String var : usedVars.first) {
|
||||
final LocalVariableDescriptorImpl descriptor = myNodeManager.getLocalVariableDescriptor(stackDescriptor, visibleVariables.get(var));
|
||||
myChildren.add(myNodeManager.createNode(descriptor, evaluationContext));
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.buildVariables(stackDescriptor, evaluationContext);
|
||||
}
|
||||
// add expressions
|
||||
final EvaluationContextImpl evalContextCopy = evaluationContext.createEvaluationContext(evaluationContext.getThisObject());
|
||||
evalContextCopy.setAutoLoadClasses(false);
|
||||
for (TextWithImports text : usedVars.second) {
|
||||
myChildren.add(myNodeManager.createNode(myNodeManager.getWatchItemDescriptor(stackDescriptor, text, null), evalContextCopy));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
if (e.getCause() instanceof AbsentInformationException) {
|
||||
final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
|
||||
|
||||
final Collection<Value> argValues = frame.getArgumentValues();
|
||||
int index = 0;
|
||||
for (Value argValue : argValues) {
|
||||
final ArgumentValueDescriptorImpl descriptor =
|
||||
myNodeManager.getArgumentValueDescriptor(stackDescriptor, index++, argValue, false);
|
||||
final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(descriptor, evaluationContext);
|
||||
myChildren.add(variableNode);
|
||||
}
|
||||
myChildren.add(myNodeManager.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE));
|
||||
// trying to collect values from variable slots
|
||||
try {
|
||||
Map<DecompiledLocalVariable, Value> values = LocalVariablesUtil.fetchValues(frame);
|
||||
for (Map.Entry<DecompiledLocalVariable, Value> entry : values.entrySet()) {
|
||||
DecompiledLocalVariable var = entry.getKey();
|
||||
final ArgumentValueDescriptorImpl descriptor =
|
||||
myNodeManager.getArgumentValueDescriptor(stackDescriptor, var.getSlot(), entry.getValue(), var.isParam());
|
||||
final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(descriptor, evaluationContext);
|
||||
myChildren.add(variableNode);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.info(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, LocalVariableProxyImpl> getVisibleVariables(final StackFrameProxyImpl frame) throws EvaluateException {
|
||||
final Map<String, LocalVariableProxyImpl> vars = new HashMap<String, LocalVariableProxyImpl>();
|
||||
for (LocalVariableProxyImpl localVariableProxy : frame.visibleVariables()) {
|
||||
vars.put(localVariableProxy.name(), localVariableProxy);
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
private static boolean shouldSkipLine(final PsiFile file, Document doc, int line) {
|
||||
final int start = doc.getLineStartOffset(line);
|
||||
final int end = doc.getLineEndOffset(line);
|
||||
final int _start = CharArrayUtil.shiftForward(doc.getCharsSequence(), start, " \n\t");
|
||||
if (_start >= end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TextRange alreadyChecked = null;
|
||||
for (PsiElement elem = file.findElementAt(_start); elem != null && elem.getTextOffset() <= end && (alreadyChecked == null || !alreadyChecked .contains(elem.getTextRange())); elem = elem.getNextSibling()) {
|
||||
for (PsiElement _elem = elem; _elem.getTextOffset() >= _start; _elem = _elem.getParent()) {
|
||||
alreadyChecked = _elem.getTextRange();
|
||||
|
||||
if (_elem instanceof PsiDeclarationStatement) {
|
||||
final PsiElement[] declared = ((PsiDeclarationStatement)_elem).getDeclaredElements();
|
||||
for (PsiElement declaredElement : declared) {
|
||||
if (declaredElement instanceof PsiVariable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_elem instanceof PsiJavaCodeReferenceElement) {
|
||||
final PsiElement resolved = ((PsiJavaCodeReferenceElement)_elem).resolve();
|
||||
if (resolved instanceof PsiVariable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Pair<Set<String>, Set<TextWithImports>> findReferencedVars(final Set<String> visibleVars,
|
||||
final SourcePosition position,
|
||||
EvaluationContextImpl evalContext) {
|
||||
final int line = position.getLine();
|
||||
if (line < 0) {
|
||||
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
final PsiFile positionFile = position.getFile();
|
||||
if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
|
||||
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
|
||||
final VirtualFile vFile = positionFile.getVirtualFile();
|
||||
final Document doc = vFile != null? FileDocumentManager.getInstance().getDocument(vFile) : null;
|
||||
if (doc == null || doc.getLineCount() == 0 || line > (doc.getLineCount() - 1)) {
|
||||
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
|
||||
final TextRange limit = calculateLimitRange(positionFile, doc, line);
|
||||
|
||||
int startLine = Math.max(limit.getStartOffset(), line - 1);
|
||||
startLine = Math.min(startLine, limit.getEndOffset());
|
||||
while (startLine > limit.getStartOffset() && shouldSkipLine(positionFile, doc, startLine)) {
|
||||
startLine--;
|
||||
}
|
||||
final int startOffset = doc.getLineStartOffset(startLine);
|
||||
|
||||
int endLine = Math.min(line + 2, limit.getEndOffset());
|
||||
while (endLine < limit.getEndOffset() && shouldSkipLine(positionFile, doc, endLine)) {
|
||||
endLine++;
|
||||
}
|
||||
final int endOffset = doc.getLineEndOffset(endLine);
|
||||
|
||||
final TextRange lineRange = new TextRange(startOffset, endOffset);
|
||||
if (!lineRange.isEmpty()) {
|
||||
final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), doc.getLineStartOffset(line), " \t");
|
||||
PsiElement element = positionFile.findElementAt(offset);
|
||||
if (element != null) {
|
||||
PsiMethod method = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class);
|
||||
if (method != null) {
|
||||
element = method;
|
||||
}
|
||||
else {
|
||||
PsiField field = PsiTreeUtil.getNonStrictParentOfType(element, PsiField.class);
|
||||
if (field != null) {
|
||||
element = field;
|
||||
}
|
||||
else {
|
||||
final PsiClassInitializer initializer = PsiTreeUtil.getNonStrictParentOfType(element, PsiClassInitializer.class);
|
||||
if (initializer != null) {
|
||||
element = initializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
if (element instanceof PsiCompiledElement) {
|
||||
return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
else {
|
||||
final Set<String> vars = new HashSet<String>();
|
||||
final Set<TextWithImports> expressions = new HashSet<TextWithImports>();
|
||||
final PsiElementVisitor variablesCollector = new VariablesCollector(visibleVars, adjustRange(element, lineRange), expressions, vars, position, evalContext);
|
||||
element.accept(variablesCollector);
|
||||
|
||||
return Pair.create(vars, expressions);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
|
||||
}
|
||||
|
||||
private static TextRange calculateLimitRange(final PsiFile file, final Document doc, final int line) {
|
||||
final int offset = doc.getLineStartOffset(line);
|
||||
if (offset > 0) {
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(file.findElementAt(offset), PsiMethod.class, false);
|
||||
if (method != null) {
|
||||
final TextRange elemRange = method.getTextRange();
|
||||
return new TextRange(doc.getLineNumber(elemRange.getStartOffset()), doc.getLineNumber(elemRange.getEndOffset()));
|
||||
}
|
||||
}
|
||||
return new TextRange(0, doc.getLineCount() - 1);
|
||||
}
|
||||
|
||||
private static TextRange adjustRange(final PsiElement element, final TextRange originalRange) {
|
||||
final Ref<TextRange> rangeRef = new Ref<TextRange>(originalRange);
|
||||
element.accept(new JavaRecursiveElementVisitor() {
|
||||
@Override public void visitExpressionStatement(final PsiExpressionStatement statement) {
|
||||
final TextRange stRange = statement.getTextRange();
|
||||
if (originalRange.intersects(stRange)) {
|
||||
final TextRange currentRange = rangeRef.get();
|
||||
final int start = Math.min(currentRange.getStartOffset(), stRange.getStartOffset());
|
||||
final int end = Math.max(currentRange.getEndOffset(), stRange.getEndOffset());
|
||||
rangeRef.set(new TextRange(start, end));
|
||||
}
|
||||
}
|
||||
});
|
||||
return rangeRef.get();
|
||||
}
|
||||
|
||||
private class RefreshFrameTreeCommand extends RefreshDebuggerTreeCommand {
|
||||
public RefreshFrameTreeCommand(DebuggerContextImpl context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextAction() throws Exception {
|
||||
DebuggerTreeNodeImpl rootNode;
|
||||
|
||||
final DebuggerContextImpl debuggerContext = getDebuggerContext();
|
||||
final ThreadReferenceProxyImpl currentThread = debuggerContext.getThreadProxy();
|
||||
if (currentThread == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
StackFrameProxyImpl frame = debuggerContext.getFrameProxy();
|
||||
if (frame != null) {
|
||||
NodeManagerImpl nodeManager = getNodeFactory();
|
||||
rootNode = nodeManager.createNode(nodeManager.getStackFrameDescriptor(null, frame), debuggerContext.createEvaluationContext());
|
||||
}
|
||||
else {
|
||||
rootNode = getNodeFactory().getDefaultNode();
|
||||
SuspendManager suspendManager = getSuspendContext().getDebugProcess().getSuspendManager();
|
||||
try {
|
||||
if (suspendManager.isSuspended(currentThread)) {
|
||||
try {
|
||||
if (currentThread.frameCount() == 0) {
|
||||
rootNode.add(MessageDescriptor.THREAD_IS_EMPTY);
|
||||
}
|
||||
else {
|
||||
rootNode.add(MessageDescriptor.DEBUG_INFO_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
rootNode.add(new MessageDescriptor(e.getMessage()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
rootNode.add(MessageDescriptor.THREAD_IS_RUNNING);
|
||||
}
|
||||
}
|
||||
catch (ObjectCollectedException ignored) {
|
||||
rootNode.add(new MessageDescriptor(DebuggerBundle.message("label.thread.node.thread.collected", currentThread.name())));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(ex);
|
||||
}
|
||||
rootNode = getNodeFactory().getDefaultNode();
|
||||
rootNode.add(MessageDescriptor.DEBUG_INFO_UNAVAILABLE);
|
||||
}
|
||||
|
||||
final DebuggerTreeNodeImpl rootNode1 = rootNode;
|
||||
DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getMutableModel().setRoot(rootNode1);
|
||||
treeChanged();
|
||||
|
||||
final TreeModel model = getModel();
|
||||
model.addTreeModelListener(new TreeModelAdapter() {
|
||||
@Override
|
||||
public void treeStructureChanged(TreeModelEvent e) {
|
||||
final Object[] path = e.getPath();
|
||||
if (path.length > 0 && path[path.length - 1] == rootNode1) {
|
||||
// wait until rootNode1 (the root just set) becomes the root
|
||||
model.removeTreeModelListener(this);
|
||||
if (ViewsGeneralSettings.getInstance().AUTOSCROLL_TO_NEW_LOCALS) {
|
||||
autoscrollToNewLocals(rootNode1);
|
||||
}
|
||||
else {
|
||||
// should clear this flag, otherwise, if AUTOSCROLL_TO_NEW_LOCALS option turned
|
||||
// to true during the debug process, all these variables will be considered 'new'
|
||||
for (Enumeration children = rootNode1.rawChildren(); children.hasMoreElements();) {
|
||||
final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)children.nextElement();
|
||||
final NodeDescriptorImpl descriptor = child.getDescriptor();
|
||||
if (descriptor instanceof LocalVariableDescriptorImpl) {
|
||||
((LocalVariableDescriptorImpl)descriptor).setNewLocal(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void autoscrollToNewLocals(DebuggerTreeNodeImpl frameNode) {
|
||||
final DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
|
||||
final boolean isSteppingThrough = debuggerSession != null && debuggerSession.isSteppingThrough(debuggerContext.getThreadProxy());
|
||||
final List<DebuggerTreeNodeImpl> toClear = new ArrayList<DebuggerTreeNodeImpl>();
|
||||
final List<DebuggerTreeNodeImpl> newLocalsToSelect = new ArrayList<DebuggerTreeNodeImpl>();
|
||||
|
||||
for (Enumeration e = frameNode.rawChildren(); e.hasMoreElements();) {
|
||||
final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)e.nextElement();
|
||||
final NodeDescriptorImpl descriptor = child.getDescriptor();
|
||||
if (!(descriptor instanceof LocalVariableDescriptorImpl)) {
|
||||
continue;
|
||||
}
|
||||
final LocalVariableDescriptorImpl localVariableDescriptor = (LocalVariableDescriptorImpl)descriptor;
|
||||
if (isSteppingThrough && localVariableDescriptor.isNewLocal()) {
|
||||
myAnyNewLocals = true;
|
||||
newLocalsToSelect.add(child);
|
||||
}
|
||||
else {
|
||||
toClear.add(child);
|
||||
}
|
||||
localVariableDescriptor.setNewLocal(false);
|
||||
}
|
||||
|
||||
if (!newLocalsToSelect.isEmpty()) {
|
||||
for (DebuggerTreeNodeImpl child : toClear) {
|
||||
removeSelectionPath(new TreePath(child.getPath()));
|
||||
child.getDescriptor().myIsSelected = false;
|
||||
}
|
||||
for (DebuggerTreeNodeImpl child : newLocalsToSelect) {
|
||||
addSelectionPath(new TreePath(child.getPath()));
|
||||
child.getDescriptor().myIsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class VariablesCollector extends JavaRecursiveElementVisitor {
|
||||
private final Set<String> myVisibleLocals;
|
||||
private final TextRange myLineRange;
|
||||
private final Set<TextWithImports> myExpressions;
|
||||
private final Set<String> myVars;
|
||||
private final SourcePosition myPosition;
|
||||
private final EvaluationContextImpl myEvalContext;
|
||||
private final boolean myCollectExpressions;
|
||||
|
||||
public VariablesCollector(final Set<String> visibleLocals,
|
||||
final TextRange lineRange,
|
||||
final Set<TextWithImports> expressions,
|
||||
final Set<String> vars,
|
||||
SourcePosition position, EvaluationContextImpl evalContext) {
|
||||
myVisibleLocals = visibleLocals;
|
||||
myLineRange = lineRange;
|
||||
myExpressions = expressions;
|
||||
myVars = vars;
|
||||
myPosition = position;
|
||||
myEvalContext = evalContext;
|
||||
myCollectExpressions = XDebuggerSettingsManager.getInstance().getDataViewSettings().isAutoExpressions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitElement(final PsiElement element) {
|
||||
if (myLineRange.intersects(element.getTextRange())) {
|
||||
super.visitElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(final PsiMethodCallExpression expression) {
|
||||
if (myCollectExpressions) {
|
||||
final PsiMethod psiMethod = expression.resolveMethod();
|
||||
if (psiMethod != null && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
|
||||
myExpressions.add(new TextWithImportsImpl(expression));
|
||||
}
|
||||
}
|
||||
super.visitMethodCallExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(final PsiReferenceExpression reference) {
|
||||
if (myLineRange.intersects(reference.getTextRange())) {
|
||||
final PsiElement psiElement = reference.resolve();
|
||||
if (psiElement instanceof PsiVariable) {
|
||||
final PsiVariable var = (PsiVariable)psiElement;
|
||||
if (var instanceof PsiField) {
|
||||
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(reference, myVisibleLocals)) {
|
||||
/*
|
||||
if (var instanceof PsiEnumConstant && reference.getQualifier() == null) {
|
||||
final PsiClass enumClass = ((PsiEnumConstant)var).getContainingClass();
|
||||
if (enumClass != null) {
|
||||
final PsiExpression expression = JavaPsiFacade.getInstance(var.getProject()).getParserFacade().createExpressionFromText(enumClass.getName() + "." + var.getName(), var);
|
||||
final PsiReference ref = expression.getReference();
|
||||
if (ref != null) {
|
||||
ref.bindToElement(var);
|
||||
myExpressions.add(new TextWithImportsImpl(expression));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
myExpressions.add(new TextWithImportsImpl(reference));
|
||||
}
|
||||
*/
|
||||
final PsiModifierList modifierList = var.getModifierList();
|
||||
boolean isConstant = (var instanceof PsiEnumConstant) ||
|
||||
(modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC) && modifierList.hasModifierProperty(PsiModifier.FINAL));
|
||||
if (!isConstant) {
|
||||
myExpressions.add(new TextWithImportsImpl(reference));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (myVisibleLocals.contains(var.getName())) {
|
||||
myVars.add(var.getName());
|
||||
}
|
||||
else {
|
||||
// fix for variables used in inner classes
|
||||
if (!Comparing.equal(PsiTreeUtil.getParentOfType(reference, PsiClass.class),
|
||||
PsiTreeUtil.getParentOfType(var, PsiClass.class))) {
|
||||
myExpressions.add(new TextWithImportsImpl(reference));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitReferenceExpression(reference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
|
||||
if (myCollectExpressions && !DebuggerUtils.hasSideEffectsOrReferencesMissingVars(expression, myVisibleLocals)) {
|
||||
myExpressions.add(new TextWithImportsImpl(expression));
|
||||
}
|
||||
super.visitArrayAccessExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParameter(final PsiParameter parameter) {
|
||||
processVariable(parameter);
|
||||
super.visitParameter(parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(final PsiLocalVariable variable) {
|
||||
processVariable(variable);
|
||||
super.visitLocalVariable(variable);
|
||||
}
|
||||
|
||||
private void processVariable(final PsiVariable variable) {
|
||||
if (myLineRange.intersects(variable.getTextRange()) && myVisibleLocals.contains(variable.getName())) {
|
||||
myVars.add(variable.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(final PsiClass aClass) {
|
||||
// Do not step in to local and anonymous classes...
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.actions.DebuggerAction;
|
||||
import com.intellij.debugger.actions.DebuggerActions;
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||
import com.intellij.debugger.impl.DebuggerSession;
|
||||
import com.intellij.debugger.impl.DebuggerStateManager;
|
||||
import com.intellij.debugger.ui.impl.watch.DebuggerTree;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.AppUIUtil;
|
||||
import com.intellij.ui.ScrollPaneFactory;
|
||||
import com.intellij.util.SystemProperties;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.evaluation.EvaluationMode;
|
||||
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
|
||||
import com.intellij.xdebugger.frame.XStackFrame;
|
||||
import com.intellij.xdebugger.impl.frame.XVariablesViewBase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
public class VariablesPanel extends DebuggerTreePanel implements DataProvider {
|
||||
@NonNls private static final String HELP_ID = "debugging.debugFrame";
|
||||
|
||||
private static final String TREE = "tree";
|
||||
private static final String X_TREE = "xTree";
|
||||
private final JPanel myCards;
|
||||
private final MyXVariablesView myXTree;
|
||||
|
||||
public VariablesPanel(Project project, DebuggerStateManager stateManager, Disposable parent) {
|
||||
super(project, stateManager);
|
||||
|
||||
setBorder(null);
|
||||
|
||||
final FrameVariablesTree frameTree = getFrameTree();
|
||||
|
||||
myCards = new JPanel(new CardLayout());
|
||||
myCards.add(frameTree, TREE);
|
||||
|
||||
myXTree = new MyXVariablesView(project);
|
||||
registerDisposable(myXTree);
|
||||
myCards.add(myXTree.getTree(), X_TREE);
|
||||
|
||||
JScrollPane pane = ScrollPaneFactory.createScrollPane(myCards);
|
||||
pane.getVerticalScrollBar().setUnitIncrement(10);
|
||||
add(pane, BorderLayout.CENTER);
|
||||
registerDisposable(DebuggerAction.installEditAction(frameTree, DebuggerActions.EDIT_NODE_SOURCE));
|
||||
|
||||
overrideShortcut(frameTree, DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy());
|
||||
overrideShortcut(frameTree, DebuggerActions.SET_VALUE, new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)));
|
||||
|
||||
new ValueNodeDnD(myTree, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerTree createTreeView() {
|
||||
return new FrameVariablesTree(getProject(), SystemProperties.getBooleanProperty("java.debugger.xTree", true) ? this : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void changeEvent(DebuggerContextImpl newContext, DebuggerSession.Event event) {
|
||||
if (event != DebuggerSession.Event.THREADS_REFRESH) {
|
||||
super.changeEvent(newContext, event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActionPopupMenu createPopupMenu() {
|
||||
ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(DebuggerActions.FRAME_PANEL_POPUP);
|
||||
return ActionManager.getInstance().createActionPopupMenu(DebuggerActions.FRAME_PANEL_POPUP, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getData(String dataId) {
|
||||
if (PlatformDataKeys.HELP_ID.is(dataId)) {
|
||||
return HELP_ID;
|
||||
}
|
||||
return super.getData(dataId);
|
||||
}
|
||||
|
||||
public FrameVariablesTree getFrameTree() {
|
||||
return (FrameVariablesTree)getTree();
|
||||
}
|
||||
|
||||
public void stackChanged(@Nullable final XStackFrame xStackFrame) {
|
||||
AppUIUtil.invokeOnEdt(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myXTree.stackChanged(xStackFrame);
|
||||
((CardLayout)(myCards.getLayout())).show(myCards, xStackFrame == null ? TREE : X_TREE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final class MyXVariablesView extends XVariablesViewBase {
|
||||
private XStackFrame myCurrentXStackFrame;
|
||||
|
||||
public MyXVariablesView(@NotNull Project project) {
|
||||
super(project, new XDebuggerEditorsProvider() {
|
||||
@NotNull
|
||||
@Override
|
||||
public FileType getFileType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Document createDocument(@NotNull Project project, @NotNull String text, @Nullable XSourcePosition sourcePosition, @NotNull EvaluationMode mode) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
public void stackChanged(@Nullable XStackFrame stackFrame) {
|
||||
if (myCurrentXStackFrame != null) {
|
||||
saveCurrentTreeState(stackFrame);
|
||||
}
|
||||
|
||||
myCurrentXStackFrame = stackFrame;
|
||||
if (stackFrame == null) {
|
||||
getTree().setSourcePosition(null);
|
||||
}
|
||||
else {
|
||||
buildTreeAndRestoreState(stackFrame);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processSessionEvent(@NotNull SessionEvent event) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.TextWithImports;
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||
import com.intellij.debugger.ui.impl.watch.DebuggerTree;
|
||||
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
|
||||
import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.xdebugger.XDebuggerBundle;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class WatchDebuggerTree extends DebuggerTree {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.WatchDebuggerTree");
|
||||
|
||||
public WatchDebuggerTree(Project project) {
|
||||
super(project);
|
||||
getEmptyText().setText(XDebuggerBundle.message("debugger.no.watches"));
|
||||
}
|
||||
|
||||
public DebuggerTreeNodeImpl[] getWatches() {
|
||||
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
|
||||
DebuggerTreeNodeImpl[] watches = new DebuggerTreeNodeImpl[root.getChildCount()];
|
||||
|
||||
final Enumeration e = root.children();
|
||||
int i = 0;
|
||||
while(e.hasMoreElements()) {
|
||||
watches[i++] = (DebuggerTreeNodeImpl)e.nextElement();
|
||||
}
|
||||
|
||||
return watches;
|
||||
}
|
||||
|
||||
public int getWatchCount() {
|
||||
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
|
||||
return root != null ? root.getChildCount() : 0;
|
||||
}
|
||||
|
||||
public DebuggerTreeNodeImpl addWatch(WatchItemDescriptor descriptor) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
|
||||
WatchItemDescriptor watchDescriptor = new WatchItemDescriptor(getProject(), descriptor.getEvaluationText());
|
||||
watchDescriptor.displayAs(descriptor);
|
||||
|
||||
final DebuggerTreeNodeImpl node = DebuggerTreeNodeImpl.createNodeNoUpdate(this, watchDescriptor);
|
||||
root.add(node);
|
||||
|
||||
treeChanged();
|
||||
getSelectionModel().setSelectionPath(new TreePath(node.getPath()));
|
||||
|
||||
//node.calcValue();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
public DebuggerTreeNodeImpl addWatch(TextWithImports text, @Nullable String customName) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
|
||||
final WatchItemDescriptor descriptor = new WatchItemDescriptor(getProject(), text, customName);
|
||||
DebuggerTreeNodeImpl node = DebuggerTreeNodeImpl.createNodeNoUpdate(this, descriptor);
|
||||
root.add(node);
|
||||
|
||||
treeChanged();
|
||||
final TreePath path = new TreePath(node.getPath());
|
||||
getSelectionModel().setSelectionPath(path);
|
||||
scrollPathToVisible(path);
|
||||
return node;
|
||||
}
|
||||
|
||||
public void removeWatch(DebuggerTreeNodeImpl node) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
LOG.assertTrue(node.getDescriptor() instanceof WatchItemDescriptor);
|
||||
|
||||
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getModel().getRoot();
|
||||
DebuggerTreeNodeImpl nodeToSelect = (DebuggerTreeNodeImpl) node.getNextSibling();
|
||||
|
||||
getMutableModel().removeNodeFromParent(node);
|
||||
treeChanged();
|
||||
|
||||
if(nodeToSelect == null && root.getChildCount() > 0) {
|
||||
nodeToSelect = (DebuggerTreeNodeImpl) root.getChildAt(root.getChildCount() - 1);
|
||||
}
|
||||
|
||||
if(nodeToSelect != null) {
|
||||
setSelectionPath(new TreePath(nodeToSelect.getPath()));
|
||||
}
|
||||
}
|
||||
|
||||
protected void build(DebuggerContextImpl context) {
|
||||
for (DebuggerTreeNodeImpl node : getWatches()) {
|
||||
node.calcValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setWatchNodeText(final DebuggerTreeNodeImpl node, TextWithImports text) {
|
||||
((WatchItemDescriptor)node.getDescriptor()).setEvaluationText(text);
|
||||
node.calcValue();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user