deleted FrameVariableTree

This commit is contained in:
Egor.Ushakov
2015-09-15 15:22:30 +03:00
parent 6e209f5cda
commit 358b39ebae
4 changed files with 277 additions and 899 deletions
@@ -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();
}
}