diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetBundle.properties index 1ebb0cf7f85..799ec23d69b 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -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 diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/surroundWith/expression/KotlinExpressionSurroundDescriptor.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/surroundWith/expression/KotlinExpressionSurroundDescriptor.java index 1ab0eefe7c2..4afef1899bd 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/surroundWith/expression/KotlinExpressionSurroundDescriptor.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/surroundWith/expression/KotlinExpressionSurroundDescriptor.java @@ -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; diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/surroundWith/expression/KotlinRuntimeTypeCastSurrounder.kt b/idea/src/org/jetbrains/jet/plugin/codeInsight/surroundWith/expression/KotlinRuntimeTypeCastSurrounder.kt new file mode 100644 index 00000000000..687e15e5f1b --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/surroundWith/expression/KotlinRuntimeTypeCastSurrounder.kt @@ -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(project, CodeInsightBundle.message("command.name.surround.with.runtime.cast")) { + override fun run(result: Result) { + 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()) + } + + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinRuntimeTypeEvaluator.kt b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinRuntimeTypeEvaluator.kt new file mode 100644 index 00000000000..af605c124fc --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinRuntimeTypeEvaluator.kt @@ -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(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(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 + } + } +} +