Add surrounder for cast to runtime type

This commit is contained in:
Natalia Ukhorskaya
2014-11-27 16:41:15 +03:00
parent 99ec8952e1
commit 86af5ded76
4 changed files with 238 additions and 1 deletions
@@ -193,6 +193,7 @@ choose.target.class.or.trait.title=Choose target class or trait
surround.with=Surround with
surround.with.string.template="${expr}"
surround.with.when.template=when (expr) {}
surround.with.runtime.type.cast.template=(expr as RuntimeType)
surround.with.function.template={ }
surround.with.cannot.perform.action=Cannot perform Surround With action to the current contextsurround.with.function.template={ }
remove.variable.family.name=Remove variable
@@ -30,9 +30,11 @@ public class KotlinExpressionSurroundDescriptor implements SurroundDescriptor {
new KotlinNotSurrounder(),
new KotlinStringTemplateSurrounder(),
new KotlinParenthesesSurrounder(),
new KotlinWhenSurrounder()
new KotlinWhenSurrounder() ,
new KotlinRuntimeTypeCastSurrounder()
};
@Override
@NotNull
public PsiElement[] getElementsToSurround(PsiFile file, int startOffset, int endOffset) {
JetExpression expression = CodeInsightUtils.findExpression(file, startOffset, endOffset);
@@ -42,6 +44,7 @@ public class KotlinExpressionSurroundDescriptor implements SurroundDescriptor {
return new PsiElement[] {expression};
}
@Override
@NotNull
public Surrounder[] getSurrounders() {
return SURROUNDERS;
@@ -0,0 +1,118 @@
/*
* Copyright 2010-2014 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 org.jetbrains.jet.plugin.codeInsight.surroundWith.expression
import com.intellij.codeInsight.CodeInsightBundle
import com.intellij.debugger.DebuggerBundle
import com.intellij.debugger.DebuggerInvocationUtil
import com.intellij.debugger.DebuggerManagerEx
import com.intellij.debugger.impl.DebuggerContextImpl
import com.intellij.openapi.application.Result
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.util.ProgressWindowWithNotification
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetCodeFragment
import org.jetbrains.jet.plugin.debugger.evaluate.KotlinRuntimeTypeEvaluator
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.psi.JetBinaryExpressionWithTypeRHS
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
import org.jetbrains.jet.lang.psi.JetParenthesizedExpression
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.plugin.caches.resolve.analyzeFully
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import org.jetbrains.jet.plugin.JetBundle
public class KotlinRuntimeTypeCastSurrounder: KotlinExpressionSurrounder() {
override fun isApplicable(expression: JetExpression): Boolean {
if (!expression.isPhysical()) return false
val file = expression.getContainingFile()
if (file !is JetCodeFragment) return false
val context = file.analyzeFully()
val type = context[BindingContext.EXPRESSION_TYPE, expression]
if (type == null) return false
return TypeUtils.canHaveSubtypes(JetTypeChecker.DEFAULT, type)
}
override fun surroundExpression(project: Project, editor: Editor, expression: JetExpression): TextRange? {
val debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext()
val debuggerSession = debuggerContext.getDebuggerSession()
if (debuggerSession != null) {
val progressWindow = ProgressWindowWithNotification(true, expression.getProject())
val worker = SurroundWithCastWorker(editor, expression, debuggerContext, progressWindow)
progressWindow.setTitle(DebuggerBundle.message("title.evaluating"))
debuggerContext.getDebugProcess()?.getManagerThread()?.startProgress(worker, progressWindow)
}
return null
}
override fun getTemplateDescription(): String {
return JetBundle.message("surround.with.runtime.type.cast.template")
}
private inner class SurroundWithCastWorker(
private val myEditor: Editor,
expression: JetExpression,
context: DebuggerContextImpl,
indicator: ProgressIndicator
): KotlinRuntimeTypeEvaluator(myEditor, expression, context, indicator) {
override fun typeCalculationFinished(type: JetType?) {
if (type == null) return
hold()
val project = myEditor.getProject()
DebuggerInvocationUtil.invokeLater(project, Runnable {
object : WriteCommandAction<Any>(project, CodeInsightBundle.message("command.name.surround.with.runtime.cast")) {
override fun run(result: Result<Any>) {
try {
val factory = JetPsiFactory(myElement.getProject())
val fqName = DescriptorUtils.getFqName(type.getConstructor().getDeclarationDescriptor())
val parentCast = factory.createExpression("(expr as " + fqName.asString() + ")") as JetParenthesizedExpression
val cast = parentCast.getExpression() as JetBinaryExpressionWithTypeRHS
cast.getLeft().replace(myElement)
val expr = myElement.replace(parentCast) as JetExpression
ShortenReferences.process(expr)
val range = expr.getTextRange()
myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset())
myEditor.getCaretModel().moveToOffset(range.getEndOffset())
myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE)
}
finally {
release()
}
}
}.execute()
}, myProgressIndicator.getModalityState())
}
}
}
@@ -0,0 +1,115 @@
/*
* Copyright 2010-2014 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 org.jetbrains.jet.plugin.debugger.evaluate
import com.intellij.debugger.DebuggerBundle
import com.intellij.debugger.DebuggerInvocationUtil
import com.intellij.debugger.EvaluatingComputable
import com.intellij.debugger.engine.ContextUtil
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator
import com.intellij.debugger.impl.DebuggerContextImpl
import com.intellij.debugger.ui.EditorEvaluationCommand
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.psi.CommonClassNames
import com.intellij.psi.PsiElement
import com.sun.jdi.ClassType
import com.sun.jdi.Value
import org.jetbrains.eval4j.jdi.*
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.org.objectweb.asm.Type as AsmType
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.resolve.java.JvmAbi
public abstract class KotlinRuntimeTypeEvaluator(
editor: Editor?,
expression: JetExpression,
context: DebuggerContextImpl,
indicator: ProgressIndicator
) : EditorEvaluationCommand<JetType>(editor, expression, context, indicator) {
override fun threadAction() {
var type: JetType? = null
try {
type = evaluate()
}
catch (ignored: ProcessCanceledException) {
}
catch (ignored: EvaluateException) {
}
finally {
typeCalculationFinished(type)
}
}
protected abstract fun typeCalculationFinished(type: JetType?)
override fun evaluate(evaluationContext: EvaluationContextImpl): JetType? {
val project = evaluationContext.getProject()
val evaluator = DebuggerInvocationUtil.commitAndRunReadAction<ExpressionEvaluator>(project, EvaluatingComputable {
val codeFragment = JetPsiFactory(myElement.getProject()).createExpressionCodeFragment(
myElement.getText(), myElement.getContainingFile().getContext())
KotlinEvaluationBuilder.build(codeFragment, ContextUtil.getSourcePosition(evaluationContext))
})
val value = evaluator.evaluate(evaluationContext)
if (value != null) {
return getCastableRuntimeType(project, value)
}
throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.surrounded.expression.null"))
}
class object {
private fun getCastableRuntimeType(project: Project, value: Value): JetType? {
val myValue = value.asValue()
var psiClass = myValue.asmType.getClassDescriptor(project)
if (psiClass != null) {
return psiClass!!.getDefaultType()
}
val type = value.type()
if (type is ClassType) {
val superclass = type.superclass()
if (superclass != null && CommonClassNames.JAVA_LANG_OBJECT != superclass.name()) {
psiClass = AsmType.getType(superclass.signature()).getClassDescriptor(project)
if (psiClass != null) {
return psiClass!!.getDefaultType()
}
}
for (interfaceType in type.interfaces()) {
if (JvmAbi.K_OBJECT.asString() == interfaceType.name()) continue
psiClass = AsmType.getType(interfaceType.signature()).getClassDescriptor(project)
if (psiClass != null) {
return psiClass!!.getDefaultType()
}
}
}
return null
}
}
}