From ec3c667f38fcb4e3edd04596b00a369ab27d491a Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Thu, 16 Jul 2015 13:09:26 +0300 Subject: [PATCH] Debugger: allow Evaluate Expression in context of File (for some synthetic methods or when file was changed during debug and breakpoint line no longer match the actual line) --- .../kotlin/resolve/util/codeFragmentResolveUtils.kt | 4 ++++ .../idea/debugger/KotlinEditorTextProvider.kt | 12 +++++++++--- .../debugger/evaluate/KotlinCodeFragmentFactory.kt | 5 +++-- .../evaluate/extractFunctionForDebuggerUtil.kt | 13 +++++++------ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt index 11b88ec2736..b9d79298474 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt @@ -78,6 +78,10 @@ public fun JetCodeFragment.getScopeAndDataFlowForAnalyzeFragment( scopeForContextElement = contextForElement[BindingContext.RESOLUTION_SCOPE, correctedContext] dataFlowInfo = contextForElement.getDataFlowInfo(correctedContext) } + is JetFile -> { + scopeForContextElement = resolveSession.getFileScopeProvider().getFileScope(context) + dataFlowInfo = DataFlowInfo.EMPTY + } else -> return null } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt index 39ed62caaab..15d8e16a975 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt @@ -56,9 +56,7 @@ class KotlinEditorTextProvider : EditorTextProvider { } fun findExpressionInner(element: PsiElement, allowMethodCalls: Boolean): JetExpression? { - if (PsiTreeUtil.getParentOfType(element, javaClass(), javaClass(), javaClass()) != null) { - return null - } + if (!isAcceptedAsCodeFragmentContext(element)) return null val jetElement = PsiTreeUtil.getParentOfType(element, javaClass()) if (jetElement == null) return null @@ -116,6 +114,14 @@ class KotlinEditorTextProvider : EditorTextProvider { } } + + private val NOT_ACCEPTED_AS_CONTEXT_TYPES = + arrayOf(javaClass(), javaClass(), javaClass()) + + fun isAcceptedAsCodeFragmentContext(element: PsiElement): Boolean { + return element.javaClass !in NOT_ACCEPTED_AS_CONTEXT_TYPES && + PsiTreeUtil.getParentOfType(element, *NOT_ACCEPTED_AS_CONTEXT_TYPES) == null + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt index 0a824a0ac91..02a7f9ba129 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt @@ -131,10 +131,11 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() { } val expressionAtOffset = PsiTreeUtil.findElementOfClassAtOffset(elementAt.getContainingFile()!!, elementAt.getTextOffset(), javaClass(), false) - if (expressionAtOffset != null) { + if (expressionAtOffset != null && KotlinEditorTextProvider.isAcceptedAsCodeFragmentContext(elementAt)) { return expressionAtOffset } - return KotlinEditorTextProvider.findExpressionInner(elementAt, true) + + return KotlinEditorTextProvider.findExpressionInner(elementAt, true) ?: elementAt.getContainingFile() } //internal for tests diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt index 39be2c91a05..3a2a85a80aa 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt @@ -77,11 +77,6 @@ fun getFunctionForExtractedFragment( val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getContext(), breakpointLine) if (contextElement == null) return null - // Don't evaluate smth when breakpoint is on package directive (ex. for package classes) - if (contextElement is JetFile) { - throw EvaluateExceptionUtil.createEvaluateException("Cannot perform an action at this breakpoint ${breakpointFile.getName()}:${breakpointLine}") - } - addImportsToFile(codeFragment.importsAsImportList(), tmpFile) val newDebugExpressions = addDebugExpressionBeforeContextElement(codeFragment, contextElement) @@ -158,7 +153,7 @@ private fun getExpressionToAddDebugExpressionBefore(tmpFile: JetFile, contextEle return getExpressionToAddDebugExpressionBefore(tmpFile, containingFile.getContext(), line) } - fun shouldStop(el: PsiElement?, p: PsiElement?) = p is JetBlockExpression || el is JetDeclaration + fun shouldStop(el: PsiElement?, p: PsiElement?) = p is JetBlockExpression || el is JetDeclaration || el is JetFile var elementAt = tmpFile.getElementInCopy(contextElement) @@ -192,6 +187,12 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment } val elementBefore = when { + contextElement is JetFile -> { + val fakeFunction = psiFactory.createFunction("fun _debug_fun_() {}") + contextElement.add(psiFactory.createNewLine()) + val newFakeFun = contextElement.add(fakeFunction) as JetNamedFunction + newFakeFun.getBodyExpression()!!.getLastChild() + } contextElement is JetProperty && !contextElement.isLocal() -> { val delegateExpressionOrInitializer = contextElement.getDelegateExpressionOrInitializer() if (delegateExpressionOrInitializer != null) {