diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java index 2830f3a28848..62e317826a86 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java @@ -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 visibleVariables = FrameVariablesTree.getVisibleVariables(getStackFrameProxy()); - final EvaluationContextImpl evalContext = evaluationContext; + final Map visibleVariables = getVisibleVariables(getStackFrameProxy()); final Pair, Set> usedVars = ApplicationManager.getApplication().runReadAction(new Computable, Set>>() { @Override public Pair, Set> 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 extraVars = computeExtraVars(usedVars, sourcePosition, evalContext); + final Set 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 myVisibleLocals; + private final TextRange myLineRange; + private final Set myExpressions; + private final Set myVars; + private final SourcePosition myPosition; + private final EvaluationContextImpl myEvalContext; + private final boolean myCollectExpressions; + + public VariablesCollector(final Set visibleLocals, + final TextRange lineRange, + final Set expressions, + final Set 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 getVisibleVariables(final StackFrameProxyImpl frame) throws EvaluateException { + final Map vars = new HashMap(); + 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> findReferencedVars(final Set visibleVars, + final SourcePosition position, + EvaluationContextImpl evalContext) { + final int line = position.getLine(); + if (line < 0) { + return Pair.create(Collections.emptySet(), Collections.emptySet()); + } + final PsiFile positionFile = position.getFile(); + if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) { + return Pair.create(visibleVars, Collections.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.emptySet(), Collections.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.emptySet()); + } + else { + final Set vars = new HashSet(); + final Set expressions = new HashSet(); + 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.emptySet(), Collections.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 rangeRef = new Ref(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(); + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java deleted file mode 100644 index 3bdbe4d2b851..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java +++ /dev/null @@ -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 visibleVariables = getVisibleVariables(stackDescriptor.getFrameProxy()); - final EvaluationContextImpl evalContext = debuggerContext.createEvaluationContext(); - final Pair, Set> usedVars = - ApplicationManager.getApplication().runReadAction(new Computable, Set>>() { - @Override - public Pair, Set> 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 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 values = LocalVariablesUtil.fetchValues(frame); - for (Map.Entry 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 getVisibleVariables(final StackFrameProxyImpl frame) throws EvaluateException { - final Map vars = new HashMap(); - 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> findReferencedVars(final Set visibleVars, - final SourcePosition position, - EvaluationContextImpl evalContext) { - final int line = position.getLine(); - if (line < 0) { - return Pair.create(Collections.emptySet(), Collections.emptySet()); - } - final PsiFile positionFile = position.getFile(); - if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) { - return Pair.create(visibleVars, Collections.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.emptySet(), Collections.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.emptySet()); - } - else { - final Set vars = new HashSet(); - final Set expressions = new HashSet(); - 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.emptySet(), Collections.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 rangeRef = new Ref(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 toClear = new ArrayList(); - final List newLocalsToSelect = new ArrayList(); - - 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 myVisibleLocals; - private final TextRange myLineRange; - private final Set myExpressions; - private final Set myVars; - private final SourcePosition myPosition; - private final EvaluationContextImpl myEvalContext; - private final boolean myCollectExpressions; - - public VariablesCollector(final Set visibleLocals, - final TextRange lineRange, - final Set expressions, - final Set 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... - } - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java deleted file mode 100644 index 96e837559c38..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java +++ /dev/null @@ -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) { - } - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchDebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchDebuggerTree.java deleted file mode 100644 index 12e5b1c7fa9b..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchDebuggerTree.java +++ /dev/null @@ -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(); - } - -}