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)

This commit is contained in:
Natalia Ukhorskaya
2015-07-16 13:09:26 +03:00
parent 75edfa6527
commit ec3c667f38
4 changed files with 23 additions and 11 deletions
@@ -78,6 +78,10 @@ public fun JetCodeFragment.getScopeAndDataFlowForAnalyzeFragment(
scopeForContextElement = contextForElement[BindingContext.RESOLUTION_SCOPE, correctedContext] scopeForContextElement = contextForElement[BindingContext.RESOLUTION_SCOPE, correctedContext]
dataFlowInfo = contextForElement.getDataFlowInfo(correctedContext) dataFlowInfo = contextForElement.getDataFlowInfo(correctedContext)
} }
is JetFile -> {
scopeForContextElement = resolveSession.getFileScopeProvider().getFileScope(context)
dataFlowInfo = DataFlowInfo.EMPTY
}
else -> return null else -> return null
} }
@@ -56,9 +56,7 @@ class KotlinEditorTextProvider : EditorTextProvider {
} }
fun findExpressionInner(element: PsiElement, allowMethodCalls: Boolean): JetExpression? { fun findExpressionInner(element: PsiElement, allowMethodCalls: Boolean): JetExpression? {
if (PsiTreeUtil.getParentOfType(element, javaClass<JetUserType>(), javaClass<JetImportDirective>(), javaClass<JetPackageDirective>()) != null) { if (!isAcceptedAsCodeFragmentContext(element)) return null
return null
}
val jetElement = PsiTreeUtil.getParentOfType(element, javaClass<JetElement>()) val jetElement = PsiTreeUtil.getParentOfType(element, javaClass<JetElement>())
if (jetElement == null) return null if (jetElement == null) return null
@@ -116,6 +114,14 @@ class KotlinEditorTextProvider : EditorTextProvider {
} }
} }
private val NOT_ACCEPTED_AS_CONTEXT_TYPES =
arrayOf(javaClass<JetUserType>(), javaClass<JetImportDirective>(), javaClass<JetPackageDirective>())
fun isAcceptedAsCodeFragmentContext(element: PsiElement): Boolean {
return element.javaClass !in NOT_ACCEPTED_AS_CONTEXT_TYPES &&
PsiTreeUtil.getParentOfType(element, *NOT_ACCEPTED_AS_CONTEXT_TYPES) == null
}
} }
} }
@@ -131,10 +131,11 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
} }
val expressionAtOffset = PsiTreeUtil.findElementOfClassAtOffset(elementAt.getContainingFile()!!, elementAt.getTextOffset(), javaClass<JetExpression>(), false) val expressionAtOffset = PsiTreeUtil.findElementOfClassAtOffset(elementAt.getContainingFile()!!, elementAt.getTextOffset(), javaClass<JetExpression>(), false)
if (expressionAtOffset != null) { if (expressionAtOffset != null && KotlinEditorTextProvider.isAcceptedAsCodeFragmentContext(elementAt)) {
return expressionAtOffset return expressionAtOffset
} }
return KotlinEditorTextProvider.findExpressionInner(elementAt, true)
return KotlinEditorTextProvider.findExpressionInner(elementAt, true) ?: elementAt.getContainingFile()
} }
//internal for tests //internal for tests
@@ -77,11 +77,6 @@ fun getFunctionForExtractedFragment(
val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getContext(), breakpointLine) val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getContext(), breakpointLine)
if (contextElement == null) return null 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) addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
val newDebugExpressions = addDebugExpressionBeforeContextElement(codeFragment, contextElement) val newDebugExpressions = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
@@ -158,7 +153,7 @@ private fun getExpressionToAddDebugExpressionBefore(tmpFile: JetFile, contextEle
return getExpressionToAddDebugExpressionBefore(tmpFile, containingFile.getContext(), line) 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) var elementAt = tmpFile.getElementInCopy(contextElement)
@@ -192,6 +187,12 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment
} }
val elementBefore = when { 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() -> { contextElement is JetProperty && !contextElement.isLocal() -> {
val delegateExpressionOrInitializer = contextElement.getDelegateExpressionOrInitializer() val delegateExpressionOrInitializer = contextElement.getDelegateExpressionOrInitializer()
if (delegateExpressionOrInitializer != null) { if (delegateExpressionOrInitializer != null) {