Merge master into idea14

Conflicts:
	.idea/runConfigurations/All_Tests.xml
	idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt
	idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/AbstractJetExtractionTest.kt
	idea/tests/org/jetbrains/jet/shortenRefs/AbstractShortenRefsTest.kt
This commit is contained in:
Nikolay Krasko
2014-06-25 16:26:01 +04:00
978 changed files with 15161 additions and 4205 deletions
+4
View File
@@ -155,6 +155,9 @@
<projectService serviceInterface="org.jetbrains.jet.asJava.KotlinLightClassForPackage$FileStubCache"
serviceImplementation="org.jetbrains.jet.asJava.KotlinLightClassForPackage$FileStubCache"/>
<projectService serviceInterface="org.jetbrains.jet.plugin.debugger.evaluate.KotlinEvaluateExpressionCache"
serviceImplementation="org.jetbrains.jet.plugin.debugger.evaluate.KotlinEvaluateExpressionCache"/>
<errorHandler implementation="org.jetbrains.jet.plugin.reporter.KotlinReportSubmitter"/>
<internalFileTemplate name="Kotlin File"/>
@@ -287,6 +290,7 @@
<typedHandler implementation="org.jetbrains.jet.plugin.editor.KotlinTypedHandler"/>
<enterHandlerDelegate implementation="org.jetbrains.jet.plugin.editor.KotlinEnterHandler"
id="KotlinEnterHandler" order="before EnterBetweenBracesHandler"/>
<lang.smartEnterProcessor language="jet" implementationClass="org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler"/>
<backspaceHandlerDelegate implementation="org.jetbrains.jet.plugin.editor.KotlinBackspaceHandler"/>
<copyPastePostProcessor implementation="org.jetbrains.jet.plugin.conversion.copy.ConvertJavaCopyPastePostProcessor"/>
@@ -16,7 +16,6 @@
package org.jetbrains.jet.plugin.actions;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
@@ -25,10 +24,11 @@ import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.j2k.Converter;
import org.jetbrains.jet.j2k.ConverterSettings;
import org.jetbrains.jet.j2k.FilesConversionScope;
import java.util.List;
@@ -43,7 +43,7 @@ public class JavaToKotlinAction extends AnAction {
assert virtualFiles != null;
final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
assert project != null;
final List<PsiFile> selectedJavaFiles = getAllJavaFiles(virtualFiles, project);
final List<PsiJavaFile> selectedJavaFiles = getAllJavaFiles(virtualFiles, project);
if (selectedJavaFiles.isEmpty()) {
return;
}
@@ -52,7 +52,7 @@ public class JavaToKotlinAction extends AnAction {
return;
}
final Converter converter = prepareConverter(project, selectedJavaFiles);
final Converter converter = Converter.object$.create(project, ConverterSettings.defaultSettings, new FilesConversionScope(selectedJavaFiles));
CommandProcessor.getInstance().executeCommand(
project,
new Runnable() {
@@ -76,18 +76,6 @@ public class JavaToKotlinAction extends AnAction {
);
}
@NotNull
private static Converter prepareConverter(@NotNull Project project, @NotNull List<PsiFile> selectedJavaFiles) {
Converter converter = new Converter(project, ConverterSettings.defaultSettings);
converter.clearClassIdentifiers();
for (PsiFile f : selectedJavaFiles) {
if (f.getFileType() instanceof JavaFileType) {
setClassIdentifiers(converter, f);
}
}
return converter;
}
private static enum DialogResult {
BACKUP_FILES,
DELETE_FILES,
@@ -16,7 +16,6 @@
package org.jetbrains.jet.plugin.actions;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ex.MessagesEx;
@@ -29,19 +28,11 @@ import com.intellij.psi.codeStyle.CodeStyleManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.j2k.Converter;
import org.jetbrains.jet.j2k.visitors.ClassVisitor;
import java.io.IOException;
import java.util.*;
public class JavaToKotlinActionUtil {
static void setClassIdentifiers(@NotNull Converter converter, @NotNull PsiFile psiFile) {
ClassVisitor c = new ClassVisitor();
psiFile.accept(c);
converter.setClassIdentifiers(new HashSet<String>(c.getClassIdentifiers()));
}
@NotNull
private static List<VirtualFile> getChildrenRecursive(@Nullable VirtualFile baseDir) {
List<VirtualFile> result = new LinkedList<VirtualFile>();
@@ -53,14 +44,14 @@ public class JavaToKotlinActionUtil {
}
@NotNull
/*package*/ static List<PsiFile> getAllJavaFiles(@NotNull VirtualFile[] vFiles, Project project) {
/*package*/ static List<PsiJavaFile> getAllJavaFiles(@NotNull VirtualFile[] vFiles, Project project) {
Set<VirtualFile> filesSet = allVirtualFiles(vFiles);
PsiManager manager = PsiManager.getInstance(project);
List<PsiFile> res = new ArrayList<PsiFile>();
List<PsiJavaFile> res = new ArrayList<PsiJavaFile>();
for (VirtualFile file : filesSet) {
PsiFile psiFile = manager.findFile(file);
if (psiFile != null && psiFile.getFileType() instanceof JavaFileType) {
res.add(psiFile);
if (psiFile != null && psiFile instanceof PsiJavaFile) {
res.add((PsiJavaFile)psiFile);
}
}
return res;
@@ -92,7 +83,7 @@ public class JavaToKotlinActionUtil {
}
@NotNull
static List<VirtualFile> convertFiles(final Converter converter, List<PsiFile> allJavaFilesNear) {
static List<VirtualFile> convertFiles(final Converter converter, List<PsiJavaFile> allJavaFilesNear) {
final List<VirtualFile> result = new LinkedList<VirtualFile>();
for (final PsiFile f : allJavaFilesNear) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@@ -108,7 +99,7 @@ public class JavaToKotlinActionUtil {
return result;
}
static void deleteFiles(List<PsiFile> allJavaFilesNear) {
static void deleteFiles(List<PsiJavaFile> allJavaFilesNear) {
for (final PsiFile f : allJavaFilesNear) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
@@ -133,7 +124,7 @@ public class JavaToKotlinActionUtil {
if (psiFile instanceof PsiJavaFile && virtualFile != null) {
String result = "";
try {
result = converter.convertFile((PsiJavaFile) psiFile).toKotlin();
result = converter.elementToKotlin(psiFile);
} catch (Exception e) {
//noinspection CallToPrintStackTrace
e.printStackTrace();
@@ -150,7 +141,7 @@ public class JavaToKotlinActionUtil {
return null;
}
static void renameFiles(@NotNull List<PsiFile> psiFiles) {
static void renameFiles(@NotNull List<PsiJavaFile> psiFiles) {
for (final PsiFile f : psiFiles) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
@@ -97,7 +97,7 @@ public class CodeInsightUtils {
}
if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY;
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
List<PsiElement> array = new ArrayList<PsiElement>();
PsiElement stopElement = element2.getNextSibling();
for (PsiElement currentElement = element1; currentElement != stopElement; currentElement = currentElement.getNextSibling()) {
if (!(currentElement instanceof PsiWhiteSpace)) {
@@ -33,9 +33,11 @@ import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPropertyDescriptor
import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyPackageFragmentForJavaClass
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaMethodDescriptor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.SmartPointerManager
import com.intellij.psi.PsiDocumentManager
import org.jetbrains.jet.plugin.caches.resolve.getLazyResolveSession
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.jet.renderer.DescriptorRenderer.FQ_NAMES_IN_TYPES
public object ShortenReferences {
public fun process(element: JetElement) {
@@ -100,6 +102,8 @@ public object ShortenReferences {
private fun process(elements: Iterable<JetElement>, elementFilter: (PsiElement) -> FilterResult) {
for ((file, fileElements) in elements.groupBy { element -> element.getContainingJetFile() }) {
ImportInsertHelper.optimizeImportsIfNeeded(file)
// first resolve all qualified references - optimization
val referenceToContext = JetFileReferencesResolver.resolve(file, fileElements, visitShortNames = false)
@@ -313,7 +317,13 @@ public object ShortenReferences {
private fun resolveState(referenceExpression: JetReferenceExpression, bindingContext: BindingContext): Any? {
val target = bindingContext[BindingContext.REFERENCE_TARGET, referenceExpression]
if (target != null) return target.asString()
if (target != null) {
val resolvedCallKey = (referenceExpression.getParent() as? JetThisExpression) ?: referenceExpression
val resolvedCall = bindingContext[BindingContext.RESOLVED_CALL, resolvedCallKey]
if (resolvedCall != null) return resolvedCall.asString()
return target.asString()
}
val targets = bindingContext[BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression]
if (targets != null) return HashSet(targets.map{it.asString()})
@@ -334,8 +344,12 @@ public object ShortenReferences {
private fun DeclarationDescriptor.asString() = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(this)
private fun ResolvedCall<*>.asString(): String {
return "${getReceiverArgument()}, ${getThisObject()} -> ${getResultingDescriptor()?.let {FQ_NAMES_IN_TYPES.render(it)}}"
}
//TODO: do we need this "IfNeeded" check?
private fun addImportIfNeeded(descriptor: DeclarationDescriptor, file: JetFile) {
ImportInsertHelper.addImportDirectiveIfNeeded(DescriptorUtils.getFqNameSafe(descriptor), file)
ImportInsertHelper.addImportDirectiveIfNeeded(DescriptorUtils.getFqNameSafe(descriptor), file, false)
}
}
@@ -172,7 +172,7 @@ fun createLookupElement(descriptor: DeclarationDescriptor, resolveSession: Resol
return if (descriptor is FunctionDescriptor && descriptor.getValueParameters().isNotEmpty()) element.keepOldArgumentListOnTab() else element
}
fun JetType.isSubtypeOf(expectedType: JetType) = !isError() && JetTypeChecker.INSTANCE.isSubtypeOf(this, expectedType)
fun JetType.isSubtypeOf(expectedType: JetType) = !isError() && JetTypeChecker.DEFAULT.isSubtypeOf(this, expectedType)
fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
fun <T : Any> T?.toSet(): Set<T> = if (this != null) setOf(this) else setOf()
@@ -49,9 +49,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
}
public override fun collectTransferableData(file: PsiFile, editor: Editor, startOffsets: IntArray, endOffsets: IntArray): List<TextBlockTransferableData> {
if (file !is PsiJavaFile) {
return listOf()
}
if (file !is PsiJavaFile) return listOf()
val lightFile = PsiFileFactory.getInstance(file.getProject())!!.createFileFromText(file.getText()!!, file)
return listOf(CopiedCode(lightFile as? PsiJavaFile, startOffsets, endOffsets))
@@ -61,36 +59,33 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
assert(values.size() == 1)
val value = values.first()
if (value !is CopiedCode) return
if (value !is CopiedCode)
return
val sourceFile = value.getFile() ?: return
if (value.getFile() == null)
return
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument())
if (file !is JetFile)
return
val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument())
if (targetFile !is JetFile) return
val jetEditorOptions = JetEditorOptions.getInstance()!!
val needConvert = jetEditorOptions.isEnableJavaToKotlinConversion() && (jetEditorOptions.isDonTShowConversionDialog() || okFromDialog(project))
if (needConvert) {
val text = convertCopiedCodeToKotlin(value, file.getProject())
val text = convertCopiedCodeToKotlin(value, sourceFile)
if (text.isNotEmpty()) {
ApplicationManager.getApplication()!!.runWriteAction {
val startOffset = bounds.getStartOffset()
editor.getDocument().replaceString(bounds.getStartOffset(), bounds.getEndOffset(), text)
val endOffsetAfterCopy = startOffset + text.length()
editor.getCaretModel().moveToOffset(endOffsetAfterCopy)
CodeStyleManager.getInstance(project)!!.reformatText(file, startOffset, endOffsetAfterCopy)
PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument())
CodeStyleManager.getInstance(project)!!.reformatText(targetFile, startOffset, endOffsetAfterCopy)
PsiDocumentManager.getInstance(targetFile.getProject()).commitDocument(editor.getDocument())
}
}
}
}
private fun convertCopiedCodeToKotlin(code: CopiedCode, project: Project): String {
val converter = Converter(project, ConverterSettings.defaultSettings)
private fun convertCopiedCodeToKotlin(code: CopiedCode, file: PsiJavaFile): String {
val converter = Converter.create(file.getProject(), ConverterSettings.defaultSettings, FilesConversionScope(listOf(file)))
val startOffsets = code.getStartOffsets()
val endOffsets = code.getEndOffsets()
assert(startOffsets.size == endOffsets.size) { "Must have the same size" }
@@ -98,7 +93,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
for (i in startOffsets.indices) {
val startOffset = startOffsets[i]
val endOffset = endOffsets[i]
result.append(convertRangeToKotlin(code.getFile()!!, TextRange(startOffset, endOffset), converter))
result.append(convertRangeToKotlin(file, TextRange(startOffset, endOffset), converter))
}
return StringUtil.convertLineSeparators(result.toString())
}
@@ -50,7 +50,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
override fun isContextAccepted(contextElement: PsiElement?): Boolean {
if (contextElement is PsiCodeBlock) {
return contextElement.getContext()?.getContext()?.getLanguage() == JetFileType.INSTANCE.getLanguage()
return isContextAccepted(contextElement.getContext())
}
return contextElement?.getLanguage() == JetFileType.INSTANCE.getLanguage()
}
@@ -63,6 +63,10 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
fun getContextElement(elementAt: PsiElement?): PsiElement? {
if (elementAt == null) return null
if (elementAt is PsiCodeBlock) {
return getContextElement(elementAt.getContext())
}
val expressionAtOffset = PsiTreeUtil.findElementOfClassAtOffset(elementAt.getContainingFile()!!, elementAt.getTextOffset(), javaClass<JetExpression>(), false)
if (expressionAtOffset != null) {
return expressionAtOffset
@@ -0,0 +1,123 @@
/*
* 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.openapi.project.Project
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.jet.lang.psi.JetCodeFragment
import com.intellij.debugger.SourcePosition
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import java.util.ArrayList
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.org.objectweb.asm.Type
import com.intellij.util.containers.MultiMap
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.psi.JavaPsiFacade
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.plugin.caches.resolve.JavaResolveExtension
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaClassImpl
import org.jetbrains.jet.lang.resolve.java.JvmClassName
import org.jetbrains.jet.codegen.AsmUtil
import org.apache.log4j.Logger
class KotlinEvaluateExpressionCache(val project: Project) {
private val cachedCompiledData = CachedValuesManager.getManager(project).createCachedValue(
{
CachedValueProvider.Result<MultiMap<String, CompiledDataDescriptor>>(
MultiMap.create(), PsiModificationTracker.MODIFICATION_COUNT)
}, false)
class object {
private val LOG = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
fun getInstance(project: Project) = ServiceManager.getService(project, javaClass<KotlinEvaluateExpressionCache>())!!
fun getOrCreateCompiledData(
codeFragment: JetCodeFragment,
sourcePosition: SourcePosition,
evaluationContext: EvaluationContextImpl,
create: (JetCodeFragment, SourcePosition) -> CompiledDataDescriptor
): CompiledDataDescriptor {
val evaluateExpressionCache = getInstance(codeFragment.getProject())
return synchronized(evaluateExpressionCache.cachedCompiledData) {
(): CompiledDataDescriptor ->
val cache = evaluateExpressionCache.cachedCompiledData.getValue()!!
val text = "${codeFragment.importsToString()}\n${codeFragment.getText()}"
val answer = cache[text].firstOrNull {
it.sourcePosition == sourcePosition || evaluateExpressionCache.canBeEvaluatedInThisContext(it, evaluationContext)
}
if (answer != null) return@synchronized answer
val newCompiledData = create(codeFragment, sourcePosition)
LOG.debug("Compile bytecode for ${codeFragment.getText()}")
cache.putValue(text, newCompiledData)
return@synchronized newCompiledData
}
}
}
private fun canBeEvaluatedInThisContext(compiledData: CompiledDataDescriptor, context: EvaluationContextImpl): Boolean {
return compiledData.parameters.all { (p): Boolean ->
val (name, jetType) = p
val value = context.getFrameProxy()?.getStackFrame()?.findLocalVariable(name, failIfNotFound = false)
if (value == null) return@all false
val thisDescriptor = value.asmType.getClassDescriptor()
val superClassDescriptor = jetType.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
return@all thisDescriptor != null && superClassDescriptor != null && DescriptorUtils.isSubclass(thisDescriptor, superClassDescriptor)
}
}
private fun Type.getClassDescriptor(): ClassDescriptor? {
if (AsmUtil.isPrimitive(this)) return null
val jvmName = JvmClassName.byInternalName(getInternalName()).getFqNameForClassNameWithoutDollars()
val platformClasses = JavaToKotlinClassMap.getInstance().mapPlatformClass(jvmName)
if (platformClasses.notEmpty) return platformClasses.first()
return ApplicationManager.getApplication()?.runReadAction<ClassDescriptor> {
val classes = JavaPsiFacade.getInstance(project).findClasses(jvmName.asString(), GlobalSearchScope.allScope(project))
if (classes.isEmpty()) null else JavaResolveExtension[project].resolveClass(JavaClassImpl(classes.first()))
}
}
data class CompiledDataDescriptor(val bytecodes: ByteArray, val sourcePosition: SourcePosition, val funName: String, val parameters: ParametersDescriptor)
class ParametersDescriptor : Iterable<Pair<String, JetType>> {
private val list = ArrayList<Pair<String, JetType>>()
fun add(name: String, jetType: JetType) {
list.add(name to jetType)
}
fun getParameterNames() = list.map { it.first }
override fun iterator() = list.iterator()
}
}
@@ -43,35 +43,24 @@ import org.jetbrains.eval4j.jdi.asJdiValue
import org.jetbrains.eval4j.jdi.makeInitialFrame
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils
import org.jetbrains.jet.lang.resolve.name.FqName
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.jet.plugin.refactoring.createTempCopy
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
import org.jetbrains.jet.plugin.refactoring.extractFunction.performAnalysis
import org.jetbrains.jet.plugin.refactoring.extractFunction.validate
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction
import org.jetbrains.jet.lang.psi.JetNamedFunction
import com.intellij.psi.PsiFile
import org.jetbrains.jet.codegen.ClassFileFactory
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils
import org.jetbrains.jet.OutputFileCollection
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
import org.jetbrains.jet.lang.psi.JetCodeFragment
import org.jetbrains.jet.lang.psi.JetImportList
import org.jetbrains.jet.lang.psi.codeFragmentUtil.skipVisibilityCheck
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.jet.codegen.CompilationErrorHandler
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.ErrorMessage
import org.jetbrains.jet.lang.diagnostics.Severity
import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages
import com.sun.jdi.request.EventRequest
import com.sun.jdi.ObjectReference
import com.intellij.debugger.engine.SuspendContext
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionOptions
import org.jetbrains.jet.lang.diagnostics.DiagnosticHolder
import org.jetbrains.jet.plugin.debugger.evaluate.KotlinEvaluateExpressionCache.*
import org.jetbrains.jet.lang.resolve.BindingContext
import com.sun.jdi.StackFrame
import com.sun.jdi.VirtualMachine
object KotlinEvaluationBuilder: EvaluatorBuilder {
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
@@ -98,52 +87,24 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
val sourcePosition: SourcePosition
) : Evaluator {
override fun evaluate(context: EvaluationContextImpl): Any? {
var isCompiledDataFromCache = true
try {
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
if (extractedFunction == null) {
throw IllegalStateException("Code fragment cannot be extracted to function: ${sourcePosition.getFile().getText()}:${sourcePosition.getLine()},\ncodeFragment = ${codeFragment.getText()}")
val compiledData = KotlinEvaluateExpressionCache.getOrCreateCompiledData(codeFragment, sourcePosition, context) {
fragment, position ->
isCompiledDataFromCache = false
extractAndCompile(fragment, position)
}
val classFileFactory = createClassFileFactory(extractedFunction)
// KT-4509
val outputFiles = (classFileFactory : OutputFileCollection).asList().filter { it.relativePath != "$packageInternalName.class" }
if (outputFiles.size() != 1) exception("Expression compiles to more than one class file. Note that lambdas, classes and objects are unsupported yet. List of files: ${outputFiles.makeString(",")}")
val args = context.getArgumentsByNames(compiledData.parameters.getParameterNames())
val result = runEval4j(context, compiledData, args)
val virtualMachine = context.getDebugProcess().getVirtualMachineProxy().getVirtualMachine()
var resultValue: Value? = null
ClassReader(outputFiles.first().asByteArray()).accept(object : ClassVisitor(ASM5) {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
if (name == extractedFunction.getName()) {
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
override fun visitEnd() {
val value = interpreterLoop(
this,
makeInitialFrame(this, context.getArgumentsByNames(extractedFunction.getParameterNamesForDebugger())),
JDIEval(virtualMachine,
context.getClassLoader()!!,
context.getSuspendContext().getThread()?.getThreadReference()!!, context.getSuspendContext().getInvokePolicy())
)
resultValue = when (value) {
is ValueReturned -> value.result
is ExceptionThrown -> exception(value.exception.toString())
is AbnormalTermination -> exception(value.message)
else -> throw IllegalStateException("Unknown result value produced by eval4j")
}
}
}
}
return super.visitMethod(access, name, desc, signature, exceptions)
}
}, 0)
if (resultValue == null) {
throw IllegalStateException("resultValue is null: cannot find method ${extractedFunction.getName()} in ${outputFiles.first().relativePath}")
// If bytecode was taken from cache and exception was thrown - recompile bytecode and run eval4j again
if (isCompiledDataFromCache && result is ExceptionThrown && result.kind == ExceptionThrown.ExceptionKind.BROKEN_CODE) {
return runEval4j(context, extractAndCompile(codeFragment, sourcePosition), args).toJdiValue(virtualMachine)
}
return resultValue!!.asJdiValue(virtualMachine, resultValue!!.asmType)
return result.toJdiValue(virtualMachine)
}
catch(e: EvaluateException) {
throw e
@@ -159,85 +120,145 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
return null
}
private fun SuspendContext.getInvokePolicy(): Int {
return if (getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD) ObjectReference.INVOKE_SINGLE_THREADED else 0
}
private fun JetNamedFunction.getParameterNamesForDebugger(): List<String> {
val result = arrayListOf<String>()
if (getReceiverTypeRef() != null) {
result.add("this")
}
for (param in getValueParameters()) {
result.add(param.getName()!!)
}
return result
}
private fun EvaluationContextImpl.getArgumentsByNames(parameterNames: List<String>): List<Value> {
val frames = getFrameProxy()?.getStackFrame()
if (frames != null) {
fun getValue(name: String): Value {
return try {
when (name) {
"this" -> frames.thisObject().asValue()
else -> frames.getValue(frames.visibleVariableByName(name)).asValue()
}
}
catch(e: Exception) {
exception("Cannot get parameter value from local variables table: parameterName = ${name}. Note that captured parameters are unsupported yet.")
}
class object {
private fun extractAndCompile(codeFragment: JetCodeFragment, sourcePosition: SourcePosition): CompiledDataDescriptor {
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
if (extractedFunction == null) {
throw IllegalStateException("Code fragment cannot be extracted to function: ${sourcePosition.getFile().getText()}:${sourcePosition.getLine()},\ncodeFragment = ${codeFragment.getText()}")
}
return parameterNames.map { getValue(it) }
val classFileFactory = createClassFileFactory(codeFragment, extractedFunction)
// KT-4509
val outputFiles = (classFileFactory : OutputFileCollection).asList().filter { it.relativePath != "$packageInternalName.class" }
if (outputFiles.size() != 1) exception("Expression compiles to more than one class file. Note that lambdas, classes and objects are unsupported yet. List of files: ${outputFiles.makeString(",")}")
val funName = extractedFunction.getName()
if (funName == null) {
throw IllegalStateException("Extracted function should have a name: ${extractedFunction.getText()}")
}
return CompiledDataDescriptor(outputFiles.first().asByteArray(), sourcePosition, funName, extractedFunction.getParametersForDebugger())
}
return Collections.emptyList()
}
private fun createClassFileFactory(extractedFunction: JetNamedFunction): ClassFileFactory {
return ApplicationManager.getApplication()?.runReadAction(object: Computable<ClassFileFactory> {
override fun compute(): ClassFileFactory? {
val file = createFileForDebugger(codeFragment, extractedFunction)
private fun runEval4j(
context: EvaluationContextImpl,
compiledData: CompiledDataDescriptor,
args: List<Value>
): InterpreterResult {
val virtualMachine = context.getDebugProcess().getVirtualMachineProxy().getVirtualMachine()
checkForSyntacticErrors(file)
var resultValue: InterpreterResult? = null
ClassReader(compiledData.bytecodes).accept(object : ClassVisitor(ASM5) {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
if (name == compiledData.funName) {
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
override fun visitEnd() {
resultValue = interpreterLoop(
this,
makeInitialFrame(this, args),
JDIEval(virtualMachine,
context.getClassLoader()!!,
context.getSuspendContext().getThread()?.getThreadReference()!!,
context.getSuspendContext().getInvokePolicy())
)
}
}
}
val analyzeExhaust = file.getAnalysisResults()
if (analyzeExhaust.isError()) {
exception(analyzeExhaust.getError())
return super.visitMethod(access, name, desc, signature, exceptions)
}
}, 0)
val bindingContext = analyzeExhaust.getBindingContext()
bindingContext.getDiagnostics().forEach {
diagnostic ->
if (diagnostic.getSeverity() == Severity.ERROR) {
exception(DefaultErrorMessages.RENDERER.render(diagnostic))
return resultValue ?: throw IllegalStateException("resultValue is null: cannot find method ${compiledData.funName}")
}
private fun InterpreterResult.toJdiValue(vm: VirtualMachine): com.sun.jdi.Value? {
val jdiValue = when (this) {
is ValueReturned -> result
is ExceptionThrown -> exception(exception.toString())
is AbnormalTermination -> exception(message)
else -> throw IllegalStateException("Unknown result value produced by eval4j")
}
return jdiValue.asJdiValue(vm, jdiValue.asmType)
}
private fun SuspendContext.getInvokePolicy(): Int {
return if (getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD) ObjectReference.INVOKE_SINGLE_THREADED else 0
}
private fun JetNamedFunction.getParametersForDebugger(): ParametersDescriptor {
return ApplicationManager.getApplication()?.runReadAction(Computable {
val parameters = ParametersDescriptor()
val bindingContext = getAnalysisResults().getBindingContext()
val descriptor = bindingContext[BindingContext.FUNCTION, this]
if (descriptor != null) {
val receiver = descriptor.getReceiverParameter()
if (receiver != null) {
parameters.add("this", receiver.getType())
}
descriptor.getValueParameters().forEach {
param ->
parameters.add(param.getName().asString(), param.getType())
}
}
val state = GenerationState(
file.getProject(),
ClassBuilderFactories.BINARIES,
analyzeExhaust.getModuleDescriptor(),
bindingContext,
listOf(file)
)
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
return state.getFactory()
}
})!!
}
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
private fun exception(e: Throwable) {
val message = e.getMessage()
if (message != null) {
exception(message)
parameters
})!!
}
private fun EvaluationContextImpl.getArgumentsByNames(parameterNames: List<String>): List<Value> {
val frames = getFrameProxy()?.getStackFrame()
if (frames != null) {
return parameterNames.map { frames.findLocalVariable(it)!! }
}
return Collections.emptyList()
}
private fun createClassFileFactory(codeFragment: JetCodeFragment, extractedFunction: JetNamedFunction): ClassFileFactory {
return ApplicationManager.getApplication()?.runReadAction(object : Computable<ClassFileFactory> {
override fun compute(): ClassFileFactory? {
val file = createFileForDebugger(codeFragment, extractedFunction)
checkForSyntacticErrors(file)
val analyzeExhaust = file.getAnalysisResults()
if (analyzeExhaust.isError()) {
exception(analyzeExhaust.getError())
}
val bindingContext = analyzeExhaust.getBindingContext()
bindingContext.getDiagnostics().forEach {
diagnostic ->
if (diagnostic.getSeverity() == Severity.ERROR) {
exception(DefaultErrorMessages.RENDERER.render(diagnostic))
}
}
val state = GenerationState(
file.getProject(),
ClassBuilderFactories.BINARIES,
analyzeExhaust.getModuleDescriptor(),
bindingContext,
listOf(file)
)
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
return state.getFactory()
}
})!!
}
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
private fun exception(e: Throwable) {
val message = e.getMessage()
if (message != null) {
exception(message)
}
throw EvaluateExceptionUtil.createEvaluateException(e)
}
throw EvaluateExceptionUtil.createEvaluateException(e)
}
}
@@ -271,38 +292,6 @@ private fun createFileForDebugger(codeFragment: JetCodeFragment,
return jetFile
}
fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
if (newImportList != null) {
val tmpFileImportList = tmpFile.getImportList()
val packageDirective = tmpFile.getPackageDirective()
if (tmpFileImportList == null) {
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
tmpFile.addAfter(newImportList, tmpFile.getPackageDirective())
}
else {
tmpFileImportList.replace(newImportList)
}
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
}
}
fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
val parent = contextElement.getParent()
if (parent == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
val debugExpression = codeFragment.getContentElement()
if (debugExpression == null) return null
val newDebugExpression = parent.addBefore(debugExpression, contextElement)
if (newDebugExpression == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
return newDebugExpression as JetExpression
}
fun checkForSyntacticErrors(file: JetFile) {
try {
AnalyzingUtils.checkForSyntacticErrors(file)
@@ -312,69 +301,22 @@ fun checkForSyntacticErrors(file: JetFile) {
}
}
private fun getFunctionForExtractedFragment(
codeFragment: JetCodeFragment,
breakpointFile: PsiFile,
breakpointLine: Int
): JetNamedFunction? {
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult): String {
return analysisResult.messages.map {
errorMessage ->
val message = when(errorMessage) {
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.getName()}:${breakpointLine}"
ErrorMessage.SUPER_CALL -> "Cannot perform an action for expression with super call"
ErrorMessage.DENOTABLE_TYPES -> "Cannot perform an action because following types are unavailable from debugger scope"
ErrorMessage.MULTIPLE_OUTPUT -> "Cannot perform an action because this code fragment changes more than one variable"
ErrorMessage.DECLARATIONS_OUT_OF_SCOPE,
ErrorMessage.OUTPUT_AND_EXIT_POINT,
ErrorMessage.MULTIPLE_EXIT_POINTS,
ErrorMessage.VARIABLES_ARE_USED_OUTSIDE -> "Cannot perform an action for this expression"
}
if (errorMessage.additionalInfo == null) message else "$message: ${errorMessage.additionalInfo?.makeString(", ")}"
}.makeString(", ")
}
return ApplicationManager.getApplication()?.runReadAction(object: Computable<JetNamedFunction> {
override fun compute(): JetNamedFunction? {
checkForSyntacticErrors(codeFragment)
val originalFile = breakpointFile as JetFile
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
if (lineStart == null) return null
val tmpFile = originalFile.createTempCopy { it }
tmpFile.skipVisibilityCheck = true
val elementAtOffset = tmpFile.findElementAt(lineStart)
if (elementAtOffset == null) return null
val contextElement: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
val newDebugExpression = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
if (newDebugExpression == null) return null
val targetSibling = tmpFile.getDeclarations().firstOrNull()
if (targetSibling == null) return null
val analysisResult = ExtractionData(
tmpFile, Collections.singletonList(newDebugExpression), targetSibling, ExtractionOptions(false)
).performAnalysis()
if (analysisResult.status != Status.SUCCESS) {
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
}
val validationResult = analysisResult.descriptor!!.validate()
if (!validationResult.conflicts.isEmpty()) {
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet()?.map { it.getText() }?.makeString(",")}")
}
return validationResult.descriptor.generateFunction(true)
fun StackFrame.findLocalVariable(name: String, failIfNotFound: Boolean = true): Value? {
return try {
when (name) {
"this" -> thisObject().asValue()
else -> getValue(visibleVariableByName(name)).asValue()
}
})
}
catch(e: Exception) {
if (failIfNotFound) {
throw EvaluateExceptionUtil.createEvaluateException(
"Cannot find local variable: name = ${name}. Note that captured variables are unsupported yet.")
}
else {
return null
}
}
}
@@ -0,0 +1,139 @@
/*
* 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 org.jetbrains.jet.lang.psi.JetCodeFragment
import com.intellij.psi.PsiFile
import org.jetbrains.jet.lang.psi.JetNamedFunction
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.ErrorMessage
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Computable
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils
import org.jetbrains.jet.plugin.refactoring.createTempCopy
import org.jetbrains.jet.lang.psi.codeFragmentUtil.skipVisibilityCheck
import com.intellij.psi.PsiElement
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
import java.util.Collections
import org.jetbrains.jet.plugin.refactoring.extractFunction.performAnalysis
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
import org.jetbrains.jet.plugin.refactoring.extractFunction.validate
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction
import org.jetbrains.jet.lang.psi.JetImportList
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionOptions
fun getFunctionForExtractedFragment(
codeFragment: JetCodeFragment,
breakpointFile: PsiFile,
breakpointLine: Int
): JetNamedFunction? {
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult): String {
return analysisResult.messages.map {
errorMessage ->
val message = when(errorMessage) {
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.getName()}:${breakpointLine}"
ErrorMessage.SUPER_CALL -> "Cannot perform an action for expression with super call"
ErrorMessage.DENOTABLE_TYPES -> "Cannot perform an action because following types are unavailable from debugger scope"
ErrorMessage.MULTIPLE_OUTPUT -> "Cannot perform an action because this code fragment changes more than one variable"
ErrorMessage.DECLARATIONS_OUT_OF_SCOPE,
ErrorMessage.OUTPUT_AND_EXIT_POINT,
ErrorMessage.MULTIPLE_EXIT_POINTS,
ErrorMessage.VARIABLES_ARE_USED_OUTSIDE -> "Cannot perform an action for this expression"
}
if (errorMessage.additionalInfo == null) message else "$message: ${errorMessage.additionalInfo?.makeString(", ")}"
}.makeString(", ")
}
return ApplicationManager.getApplication()?.runReadAction(object: Computable<JetNamedFunction> {
override fun compute(): JetNamedFunction? {
checkForSyntacticErrors(codeFragment)
val originalFile = breakpointFile as JetFile
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
if (lineStart == null) return null
val tmpFile = originalFile.createTempCopy { it }
tmpFile.skipVisibilityCheck = true
val elementAtOffset = tmpFile.findElementAt(lineStart)
if (elementAtOffset == null) return null
val contextElement: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
val newDebugExpression = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
if (newDebugExpression == null) return null
val targetSibling = tmpFile.getDeclarations().firstOrNull()
if (targetSibling == null) return null
val analysisResult = ExtractionData(
tmpFile, Collections.singletonList(newDebugExpression), targetSibling, ExtractionOptions(false)
).performAnalysis()
if (analysisResult.status != Status.SUCCESS) {
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
}
val validationResult = analysisResult.descriptor!!.validate()
if (!validationResult.conflicts.isEmpty()) {
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet()?.map { it.getText() }?.makeString(",")}")
}
return validationResult.descriptor.generateFunction(true)
}
})
}
private fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
if (newImportList != null) {
val tmpFileImportList = tmpFile.getImportList()
val packageDirective = tmpFile.getPackageDirective()
if (tmpFileImportList == null) {
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
tmpFile.addAfter(newImportList, tmpFile.getPackageDirective())
}
else {
tmpFileImportList.replace(newImportList)
}
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
}
}
private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
val parent = contextElement.getParent()
if (parent == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
val debugExpression = codeFragment.getContentElement()
if (debugExpression == null) return null
val newDebugExpression = parent.addBefore(debugExpression, contextElement)
if (newDebugExpression == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
return newDebugExpression as JetExpression
}
@@ -0,0 +1,161 @@
/*
* 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.editor
import org.jetbrains.jet.plugin.editor.fixers.*
import com.intellij.lang.SmartEnterProcessorWithFixers
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiFile
import com.intellij.util.text.CharArrayUtil
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.jet.lang.psi.JetDeclaration
import org.jetbrains.jet.lang.psi.JetBlockExpression
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
import org.jetbrains.jet.lang.psi.JetIfExpression
import org.jetbrains.jet.lang.psi.JetForExpression
import org.jetbrains.jet.lang.psi.JetParameter
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
import com.intellij.psi.tree.TokenSet
import org.jetbrains.jet.JetNodeTypes
import org.jetbrains.jet.lang.psi.JetLoopExpression
public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() {
{
addFixers(
KotlinIfConditionFixer(),
KotlinMissingIfBranchFixer(),
KotlinWhileConditionFixer(),
KotlinForConditionFixer(),
KotlinMissingForOrWhileBodyFixer(),
KotlinWhenSubjectCaretFixer(),
KotlinMissingWhenBodyFixer(),
KotlinDoWhileFixer(),
KotlinFunctionParametersFixer(),
KotlinFunctionDeclarationBodyFixer()
)
addEnterProcessors(KotlinPlainEnterProcessor())
}
override fun getStatementAtCaret(editor: Editor?, psiFile: PsiFile?): PsiElement? {
var atCaret = super.getStatementAtCaret(editor, psiFile)
if (atCaret is PsiWhiteSpace) return null
while (atCaret != null) {
when {
atCaret?.isJetStatement() == true -> return atCaret
atCaret?.getParent() is JetFunctionLiteral -> return atCaret
atCaret is JetDeclaration -> {
val declaration = atCaret!!
when {
declaration is JetParameter && !declaration.isInLambdaExpression() -> {/* proceed to function declaration */}
declaration.getParent() is JetForExpression -> {/* skip variable declaration in 'for' expression */}
else -> return atCaret
}
}
}
atCaret = atCaret?.getParent()
}
return null
}
override fun moveCaretInsideBracesIfAny(editor: Editor, file: PsiFile) {
var caretOffset = editor.getCaretModel().getOffset()
val chars = editor.getDocument().getCharsSequence()
if (CharArrayUtil.regionMatches(chars, caretOffset, "{}")) {
caretOffset += 2
}
else {
if (CharArrayUtil.regionMatches(chars, caretOffset, "{\n}")) {
caretOffset += 3
}
}
caretOffset = CharArrayUtil.shiftBackward(chars, caretOffset - 1, " \t") + 1
if (CharArrayUtil.regionMatches(chars, caretOffset - "{}".length(), "{}") ||
CharArrayUtil.regionMatches(chars, caretOffset - "{\n}".length(), "{\n}")) {
commit(editor)
val settings = CodeStyleSettingsManager.getSettings(file.getProject())
val old = settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = false
val elt = PsiTreeUtil.getParentOfType(file.findElementAt(caretOffset - 1), javaClass<JetBlockExpression>())
if (elt != null) {
reformat(elt)
}
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = old
editor.getCaretModel().moveToOffset(caretOffset - 1)
}
}
public fun registerUnresolvedError(offset: Int) {
if (myFirstErrorOffset > offset) {
myFirstErrorOffset = offset
}
}
private fun PsiElement.isJetStatement() =
getParent() is JetBlockExpression || (getParent()?.getNode()?.getElementType() in BRANCH_CONTAINERS)
class KotlinPlainEnterProcessor : SmartEnterProcessorWithFixers.FixEnterProcessor() {
private fun getControlStatementBlock(caret: Int, element: PsiElement): JetExpression? {
when (element) {
is JetDeclarationWithBody -> return element.getBodyExpression()
is JetIfExpression -> {
if (element.getThen().isWithCaret(caret)) return element.getThen()
if (element.getElse().isWithCaret(caret)) return element.getElse()
}
is JetLoopExpression -> return element.getBody()
}
return null
}
override fun doEnter(atCaret: PsiElement, file: PsiFile?, editor: Editor, modified: Boolean): Boolean {
val block = getControlStatementBlock(editor.getCaretModel().getOffset(), atCaret) as? JetBlockExpression
if (block != null) {
val firstElement = block.getFirstChild()?.getNextSibling()
val offset = if (firstElement != null) {
firstElement.getTextRange()!!.getStartOffset() - 1
} else {
block.getTextRange()!!.getEndOffset()
}
editor.getCaretModel().moveToOffset(offset)
}
plainEnter(editor)
return true
}
}
}
private val BRANCH_CONTAINERS = TokenSet.create(JetNodeTypes.THEN, JetNodeTypes.ELSE, JetNodeTypes.BODY)
private fun JetParameter.isInLambdaExpression() = this.getParent()?.getParent() is JetFunctionLiteral
@@ -0,0 +1,65 @@
/*
* 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.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetDoWhileExpression
import org.jetbrains.jet.lang.psi.JetBlockExpression
public class KotlinDoWhileFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, psiElement: PsiElement) {
if (psiElement !is JetDoWhileExpression) return
val doc = editor.getDocument()
val stmt = psiElement as JetDoWhileExpression
val start = stmt.range.start
val body = stmt.getBody()
val whileKeyword = stmt.getWhileKeywordElement()
if (body == null) {
if (whileKeyword == null) {
doc.replaceString(start, start + "do".length(), "do {} while()")
}
else {
doc.insertString(start + "do".length(), "{}")
}
return
}
else if (whileKeyword != null && body !is JetBlockExpression && body.startLine(doc) > stmt.startLine(doc)) {
doc.insertString(start + "do".length(), "{")
doc.insertString(whileKeyword.range.start - 1, "}")
return
}
if (stmt.getCondition() == null) {
val lParen = stmt.getLeftParenthesis()
val rParen = stmt.getRightParenthesis()
when {
whileKeyword == null -> doc.insertString(stmt.range.end, "while()")
lParen == null && rParen == null -> {
doc.replaceString(whileKeyword.range.start, whileKeyword.range.end, "while()")
}
lParen != null -> processor.registerUnresolvedError(lParen.range.end)
}
}
}
}
@@ -0,0 +1,31 @@
/*
* 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.editor.fixers
import org.jetbrains.jet.lang.psi.JetWhileExpression
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetForExpression
public class KotlinForConditionFixer: MissingConditionFixer<JetForExpression>() {
override val keyword = "for"
override fun getElement(element: PsiElement?) = element as? JetForExpression
override fun getCondition(element: JetForExpression) =
element.getLoopRange() ?: element.getLoopParameter() ?: element.getMultiParameter()
override fun getLeftParenthesis(element: JetForExpression) = element.getLeftParenthesis()
override fun getRightParenthesis(element: JetForExpression) = element.getRightParenthesis()
override fun getBody(element: JetForExpression) = element.getBody()
}
@@ -0,0 +1,54 @@
/*
* 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.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetDeclaration
import org.jetbrains.jet.lang.psi.JetFunction
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.psi.JetPsiUtil
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.lang.psi.JetNamedFunction
public class KotlinFunctionDeclarationBodyFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, psiElement: PsiElement) {
if (psiElement !is JetNamedFunction) return
if (psiElement.getBodyExpression() != null|| psiElement.getEqualsToken() != null) return
val parentDeclaration = PsiTreeUtil.getParentOfType(psiElement, javaClass<JetDeclaration>())
if (parentDeclaration is JetClassOrObject) {
if (JetPsiUtil.isTrait(parentDeclaration) || psiElement.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
return
}
}
val doc = editor.getDocument()
var endOffset = psiElement.range.end
if (psiElement.getText()?.last() == ';') {
doc.deleteString(endOffset - 1, endOffset)
endOffset--
}
doc.insertString(endOffset, "{}")
}
}
@@ -0,0 +1,51 @@
/*
* 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.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import com.intellij.psi.PsiElement
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetNamedFunction
public class KotlinFunctionParametersFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, psiElement: PsiElement) {
if (psiElement !is JetNamedFunction) return;
val parameterList = psiElement.getValueParameterList()
if (parameterList == null) {
val identifier = psiElement.getNameIdentifier()
if (identifier == null) return
// Insert () after name or after type parameters list when it placed after name
val offset = Math.max(identifier.range.end, psiElement.getTypeParameterList()?.range?.end ?: psiElement.range.start)
editor.getDocument().insertString(offset, "()")
processor.registerUnresolvedError(offset + 1)
}
else {
val rParen = parameterList.getLastChild()
if (rParen == null) return
if (")" != rParen.getText()) {
val params = parameterList.getParameters()
val offset = if (params.isEmpty()) parameterList.range.start + 1 else params.last().range.end
editor.getDocument().insertString(offset, ")")
}
}
}
}
@@ -0,0 +1,34 @@
/*
* 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.editor.fixers
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.lang.SmartEnterProcessorWithFixers
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetIfExpression
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.openapi.util.TextRange
public class KotlinIfConditionFixer : MissingConditionFixer<JetIfExpression>() {
override val keyword = "if"
override fun getElement(element: PsiElement?) = element as? JetIfExpression
override fun getCondition(element: JetIfExpression) = element.getCondition()
override fun getLeftParenthesis(element: JetIfExpression) = element.getLeftParenthesis()
override fun getRightParenthesis(element: JetIfExpression) = element.getRightParenthesis()
override fun getBody(element: JetIfExpression) = element.getThen()
}
@@ -0,0 +1,50 @@
/*
* 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.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetWhileExpression
import org.jetbrains.jet.lang.psi.JetBlockExpression
import org.jetbrains.jet.lang.psi.JetLoopExpression
import org.jetbrains.jet.lang.psi.JetForExpression
public class KotlinMissingForOrWhileBodyFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
if (!(element is JetForExpression || element is JetWhileExpression)) return
val loopExpression = element as JetLoopExpression
val doc = editor.getDocument()
val body = loopExpression.getBody()
if (body is JetBlockExpression) return
if (!loopExpression.isValidLoopCondition()) return
if (body != null && body.startLine(doc) == loopExpression.startLine(doc)) return
val rParen = loopExpression.getRightParenthesis()
if (rParen == null) return
doc.insertString(rParen.range.end, "{}")
}
fun JetLoopExpression.isValidLoopCondition() = getLeftParenthesis() != null && getRightParenthesis() != null
}
@@ -0,0 +1,66 @@
/*
* 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.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetIfExpression
import org.jetbrains.jet.plugin.formatter.JetBlock
import org.jetbrains.jet.lang.psi.JetBlockExpression
public class KotlinMissingIfBranchFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
if (element !is JetIfExpression) return
val ifExpression = element as JetIfExpression
val document = editor.getDocument()
val elseBranch = ifExpression.getElse()
val elseKeyword = ifExpression.getElseKeyword()
if (elseKeyword != null) {
if (elseBranch == null || elseBranch !is JetBlockExpression && elseBranch.startLine(editor.getDocument()) > elseKeyword.startLine(editor.getDocument())) {
document.insertString(elseKeyword.range.end, "{}")
return
}
}
val thenBranch = ifExpression.getThen()
if (thenBranch is JetBlockExpression) return
val rParen = ifExpression.getRightParenthesis()
if (rParen == null) return
var transformingOneLiner = false
if (thenBranch != null && thenBranch.startLine(editor.getDocument()) == ifExpression.startLine(editor.getDocument())) {
if (ifExpression.getCondition() != null) return
transformingOneLiner = true
}
val probablyNextStatementParsedAsThen = elseKeyword == null && elseBranch == null && !transformingOneLiner
if (thenBranch == null || probablyNextStatementParsedAsThen) {
document.insertString(rParen.range.end, "{}")
}
else {
document.insertString(rParen.range.end, "{")
document.insertString(thenBranch.range.end + 1, "}")
}
}
}
@@ -0,0 +1,49 @@
/*
* 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.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetWhenExpression
public class KotlinMissingWhenBodyFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
if (element !is JetWhenExpression) return
val whenExpression = element as JetWhenExpression
val doc = editor.getDocument()
val openBrace = whenExpression.getOpenBrace()
val closeBrace = whenExpression.getCloseBrace()
if (openBrace == null && closeBrace == null && whenExpression.getEntries().isEmpty()) {
val openBraceAfter = whenExpression.insertOpenBraceAfter()
if (openBraceAfter != null) {
doc.insertString(openBraceAfter.range.end, "{}")
}
}
}
fun JetWhenExpression.insertOpenBraceAfter(): PsiElement? = when {
getRightParenthesis() != null -> getRightParenthesis()
getSubjectExpression() != null -> null
getLeftParenthesis() != null -> null
else -> getWhenKeywordElement()
}
}
@@ -0,0 +1,37 @@
/*
* 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.editor.fixers
import com.intellij.psi.PsiElement
import com.intellij.lang.SmartEnterProcessorWithFixers
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetWhenExpression
public class KotlinWhenSubjectCaretFixer : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
if (element !is JetWhenExpression) return
val lParen = element.getLeftParenthesis()
val rParen = element.getRightParenthesis()
val subject = element.getSubjectExpression()
if (subject == null && lParen != null && rParen != null) {
processor.registerUnresolvedError(lParen.range.end)
}
}
}
@@ -0,0 +1,30 @@
/*
* 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.editor.fixers
import org.jetbrains.jet.lang.psi.JetIfExpression
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetWhileExpression
public class KotlinWhileConditionFixer: MissingConditionFixer<JetWhileExpression>() {
override val keyword = "while"
override fun getElement(element: PsiElement?) = element as? JetWhileExpression
override fun getCondition(element: JetWhileExpression) = element.getCondition()
override fun getLeftParenthesis(element: JetWhileExpression) = element.getLeftParenthesis()
override fun getRightParenthesis(element: JetWhileExpression) = element.getRightParenthesis()
override fun getBody(element: JetWhileExpression) = element.getBody()
}
@@ -0,0 +1,64 @@
/*
* 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.editor.fixers
import com.intellij.lang.SmartEnterProcessorWithFixers
import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
abstract class MissingConditionFixer<T: PsiElement>() : SmartEnterProcessorWithFixers.Fixer<KotlinSmartEnterHandler>() {
override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) {
val workElement = getElement(element)
if (workElement == null) return
val doc = editor.getDocument()
val lParen = getLeftParenthesis(workElement)
val rParen = getRightParenthesis(workElement)
val condition = getCondition(workElement)
if (condition == null) {
if (lParen == null || rParen == null) {
var stopOffset = doc.getLineEndOffset(doc.getLineNumber(workElement.range.start))
val then = getBody(workElement)
if (then != null) {
stopOffset = Math.min(stopOffset, then.range.start)
}
stopOffset = Math.min(stopOffset, workElement.range.end)
doc.replaceString(workElement.range.start, stopOffset, "$keyword ()")
processor.registerUnresolvedError(workElement.range.start + "$keyword (".length())
}
else {
processor.registerUnresolvedError(lParen.range.end)
}
}
else {
if (rParen == null) {
doc.insertString(condition.range.end, ")")
}
}
}
abstract val keyword: String
abstract fun getElement(element: PsiElement?): T?
abstract fun getCondition(element: T): PsiElement?
abstract fun getLeftParenthesis(element: T): PsiElement?
abstract fun getRightParenthesis(element: T): PsiElement?
abstract fun getBody(element: T): PsiElement?
}
@@ -0,0 +1,28 @@
/*
* 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.editor.fixers
import com.intellij.psi.PsiElement
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.editor.Document
val PsiElement.range: TextRange get() = getTextRange()!!
val TextRange.start: Int get() = getStartOffset()
val TextRange.end: Int get() = getEndOffset()
fun PsiElement.startLine(doc: Document): Int = doc.getLineNumber(range.start)
fun PsiElement?.isWithCaret(caret: Int) = this?.getTextRange()?.contains(caret) == true
@@ -259,6 +259,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
afterInside(LBRACE, BLOCK).lineBreakInCode()
beforeInside(RBRACE, CLASS_BODY).lineBreakInCode()
beforeInside(RBRACE, BLOCK).lineBreakInCode()
beforeInside(RBRACE, WHEN).lineBreakInCode()
between(RPAR, BODY).spaces(1)
// if when entry has block, spacing after arrow should be set by lbrace rule
@@ -35,7 +35,7 @@ public open class ReplaceContainsIntention : AttributeCallReplacementIntention("
val ret = call.resolved.getResultingDescriptor().getReturnType()
?: return intentionFailed(editor, "undefined.returntype")
if (!JetTypeChecker.INSTANCE.isSubtypeOf(ret, KotlinBuiltIns.getInstance().getBooleanType())) {
if (!JetTypeChecker.DEFAULT.isSubtypeOf(ret, KotlinBuiltIns.getInstance().getBooleanType())) {
return intentionFailed(editor, "contains.returns.boolean")
}
@@ -327,7 +327,7 @@ public class JetFunctionParameterInfoHandler implements ParameterInfoHandlerWith
if (argument.getArgumentExpression() != null) {
JetType paramType = getActualParameterType(param);
JetType exprType = bindingContext.get(BindingContext.EXPRESSION_TYPE, argument.getArgumentExpression());
return exprType == null || JetTypeChecker.INSTANCE.isSubtypeOf(exprType, paramType);
return exprType == null || JetTypeChecker.DEFAULT.isSubtypeOf(exprType, paramType);
}
return false;
@@ -127,7 +127,7 @@ public class AddFunctionParametersFix extends ChangeFunctionSignatureFix {
JetType argumentType = expression != null ? bindingContext.get(BindingContext.EXPRESSION_TYPE, expression) : null;
JetType parameterType = parameters.get(i).getType();
if (argumentType != null && !JetTypeChecker.INSTANCE.isSubtypeOf(argumentType, parameterType))
if (argumentType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameterType))
changeSignatureData.getParameters().get(i).setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(argumentType));
}
else {
@@ -76,10 +76,10 @@ public class AddFunctionToSupertypeFix extends JetHintAction<JetNamedFunction> {
if (o1.equals(o2)) {
return 0;
}
if (JetTypeChecker.INSTANCE.isSubtypeOf(o1, o2)) {
if (JetTypeChecker.DEFAULT.isSubtypeOf(o1, o2)) {
return -1;
}
if (JetTypeChecker.INSTANCE.isSubtypeOf(o2, o1)) {
if (JetTypeChecker.DEFAULT.isSubtypeOf(o2, o1)) {
return 1;
}
return o1.toString().compareTo(o2.toString());
@@ -76,7 +76,7 @@ public class AddNameToArgumentFix extends JetIntentionAction<JetValueArgument> {
for (ValueParameterDescriptor parameter: callableDescriptor.getValueParameters()) {
String name = parameter.getName().asString();
if (usedParameters.contains(name)) continue;
if (type == null || JetTypeChecker.INSTANCE.isSubtypeOf(type, parameter.getType())) {
if (type == null || JetTypeChecker.DEFAULT.isSubtypeOf(type, parameter.getType())) {
names.add(name);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
* 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.
@@ -55,12 +55,12 @@ public class AddWhenElseBranchFix extends JetIntentionAction<JetWhenExpression>
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return super.isAvailable(project, editor, file) && element.getCloseBraceNode() != null;
return super.isAvailable(project, editor, file) && element.getCloseBrace() != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, JetFile file) throws IncorrectOperationException {
PsiElement whenCloseBrace = element.getCloseBraceNode();
PsiElement whenCloseBrace = element.getCloseBrace();
assert (whenCloseBrace != null) : "isAvailable should check if close brace exist";
JetWhenEntry entry = JetPsiFactory.createWhenEntry(project, ELSE_ENTRY_TEXT);
@@ -61,7 +61,7 @@ public class CastExpressionFix extends JetIntentionAction<JetExpression> {
if (!super.isAvailable(project, editor, file)) return false;
BindingContext context = ResolvePackage.getBindingContext((JetFile) file);
JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, element);
return expressionType != null && JetTypeChecker.INSTANCE.isSubtypeOf(type, expressionType);
return expressionType != null && JetTypeChecker.DEFAULT.isSubtypeOf(type, expressionType);
}
@Override
@@ -68,7 +68,7 @@ public class ChangeFunctionLiteralReturnTypeFix extends JetIntentionAction<JetFu
if (correspondingProperty != null && QuickFixUtil.canEvaluateTo(correspondingProperty.getInitializer(), element)) {
JetTypeReference correspondingPropertyTypeRef = correspondingProperty.getTypeRef();
JetType propertyType = context.get(BindingContext.TYPE, correspondingPropertyTypeRef);
if (propertyType != null && !JetTypeChecker.INSTANCE.isSubtypeOf(eventualFunctionLiteralType, propertyType)) {
if (propertyType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, propertyType)) {
appropriateQuickFix = new ChangeVariableTypeFix(correspondingProperty, eventualFunctionLiteralType);
}
return;
@@ -78,7 +78,7 @@ public class ChangeFunctionLiteralReturnTypeFix extends JetIntentionAction<JetFu
if (correspondingParameter != null) {
JetTypeReference correspondingParameterTypeRef = correspondingParameter.getTypeReference();
JetType parameterType = context.get(BindingContext.TYPE, correspondingParameterTypeRef);
if (parameterType != null && !JetTypeChecker.INSTANCE.isSubtypeOf(eventualFunctionLiteralType, parameterType)) {
if (parameterType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parameterType)) {
appropriateQuickFix = new ChangeParameterTypeFix(correspondingParameter, eventualFunctionLiteralType);
}
return;
@@ -88,7 +88,7 @@ public class ChangeFunctionLiteralReturnTypeFix extends JetIntentionAction<JetFu
if (parentFunction != null && QuickFixUtil.canFunctionOrGetterReturnExpression(parentFunction, element)) {
JetTypeReference parentFunctionReturnTypeRef = parentFunction.getReturnTypeRef();
JetType parentFunctionReturnType = context.get(BindingContext.TYPE, parentFunctionReturnTypeRef);
if (parentFunctionReturnType != null && !JetTypeChecker.INSTANCE.isSubtypeOf(eventualFunctionLiteralType, parentFunctionReturnType)) {
if (parentFunctionReturnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parentFunctionReturnType)) {
appropriateQuickFix = new ChangeFunctionReturnTypeFix(parentFunction, eventualFunctionLiteralType);
}
}
@@ -217,7 +217,7 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
for (FunctionDescriptor overriddenFunction: descriptor.getOverriddenDescriptors()) {
JetType overriddenFunctionType = overriddenFunction.getReturnType();
if (overriddenFunctionType == null) continue;
if (!JetTypeChecker.INSTANCE.isSubtypeOf(functionType, overriddenFunctionType)) {
if (!JetTypeChecker.DEFAULT.isSubtypeOf(functionType, overriddenFunctionType)) {
overriddenMismatchingFunctions.add(overriddenFunction);
}
}
@@ -132,7 +132,7 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
argumentExpression != null ? bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression) : null;
JetType parameterType = parameters.get(i).getType();
if (argumentType == null || !JetTypeChecker.INSTANCE.isSubtypeOf(argumentType, parameterType)) {
if (argumentType == null || !JetTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameterType)) {
return true;
}
}
@@ -188,7 +188,7 @@ public class ChangeMemberFunctionSignatureFix extends JetHintAction<JetNamedFunc
@NotNull ValueParameterDescriptor superParameter
) {
// TODO: support for generic functions
if (JetTypeChecker.INSTANCE.equalTypes(parameter.getType(), superParameter.getType())) {
if (JetTypeChecker.DEFAULT.equalTypes(parameter.getType(), superParameter.getType())) {
return superParameter.copy(parameter.getContainingDeclaration(), parameter.getName());
}
else {
@@ -150,14 +150,14 @@ public class ChangeVariableTypeFix extends JetIntentionAction<JetVariableDeclara
for (PropertyDescriptor overriddenProperty: propertyDescriptor.getOverriddenDescriptors()) {
JetType overriddenPropertyType = overriddenProperty.getReturnType();
if (overriddenPropertyType != null) {
if (!JetTypeChecker.INSTANCE.isSubtypeOf(propertyType, overriddenPropertyType)) {
if (!JetTypeChecker.DEFAULT.isSubtypeOf(propertyType, overriddenPropertyType)) {
overriddenMismatchingProperties.add(overriddenProperty);
}
else if (overriddenProperty.isVar() && !JetTypeChecker.INSTANCE.equalTypes(overriddenPropertyType, propertyType)) {
else if (overriddenProperty.isVar() && !JetTypeChecker.DEFAULT.equalTypes(overriddenPropertyType, propertyType)) {
canChangeOverriddenPropertyType = false;
}
if (overriddenProperty.isVar() && lowerBoundOfOverriddenPropertiesTypes != null &&
!JetTypeChecker.INSTANCE.equalTypes(lowerBoundOfOverriddenPropertiesTypes, overriddenPropertyType)) {
!JetTypeChecker.DEFAULT.equalTypes(lowerBoundOfOverriddenPropertiesTypes, overriddenPropertyType)) {
lowerBoundOfOverriddenPropertiesTypes = null;
}
}
@@ -476,7 +476,7 @@ private fun JetNamedDeclaration.guessType(context: BindingContext): Array<JetTyp
if (expectedTypes.isEmpty() || expectedTypes.any { expectedType -> ErrorUtils.containsErrorType(expectedType) }) {
return array<JetType>()
}
val theType = TypeUtils.intersect(JetTypeChecker.INSTANCE, expectedTypes)
val theType = TypeUtils.intersect(JetTypeChecker.DEFAULT, expectedTypes)
if (theType != null) {
return array<JetType>(theType)
}
@@ -494,8 +494,8 @@ private class JetTypeSubstitution(public val forType: JetType, public val byType
private fun JetType.substitute(substitution: JetTypeSubstitution, variance: Variance): JetType {
if (when (variance) {
Variance.INVARIANT -> this == substitution.forType
Variance.IN_VARIANCE -> JetTypeChecker.INSTANCE.isSubtypeOf(this, substitution.forType)
Variance.OUT_VARIANCE -> JetTypeChecker.INSTANCE.isSubtypeOf(substitution.forType, this)
Variance.IN_VARIANCE -> JetTypeChecker.DEFAULT.isSubtypeOf(this, substitution.forType)
Variance.OUT_VARIANCE -> JetTypeChecker.DEFAULT.isSubtypeOf(substitution.forType, this)
}) {
return substitution.byType
}
@@ -43,12 +43,21 @@ public class ImportInsertHelper {
*
* @param importFqn full name of the import
* @param file File where directive should be added.
* @param optimize Optimize existing imports before adding new one.
*/
public static void addImportDirectiveIfNeeded(@NotNull FqName importFqn, @NotNull JetFile file) {
addImportDirectiveIfNeeded(new ImportPath(importFqn, false), file);
public static void addImportDirectiveIfNeeded(@NotNull FqName importFqn, @NotNull JetFile file, boolean optimize) {
addImportDirectiveIfNeeded(new ImportPath(importFqn, false), file, optimize);
}
public static void addImportDirectiveOrChangeToFqName(@NotNull FqName importFqn, @NotNull JetFile file, int refOffset, @NotNull PsiElement targetElement) {
public static void addImportDirectiveIfNeeded(@NotNull FqName importFqn, @NotNull JetFile file) {
addImportDirectiveIfNeeded(importFqn, file, true);
}
public static void addImportDirectiveOrChangeToFqName(
@NotNull FqName importFqn,
@NotNull JetFile file,
int refOffset,
@NotNull PsiElement targetElement) {
PsiReference reference = file.findReferenceAt(refOffset);
if (reference instanceof JetReference) {
PsiElement target = reference.resolve();
@@ -80,12 +89,12 @@ public class ImportInsertHelper {
return;
}
}
addImportDirectiveIfNeeded(new ImportPath(importFqn, false), file);
addImportDirectiveIfNeeded(importFqn, file);
}
public static void addImportDirectiveIfNeeded(@NotNull ImportPath importPath, @NotNull JetFile file) {
if (CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) {
new OptimizeImportsProcessor(file.getProject(), file).runWithoutProgress();
public static void addImportDirectiveIfNeeded(@NotNull ImportPath importPath, @NotNull JetFile file, boolean optimize) {
if (optimize) {
optimizeImportsIfNeeded(file);
}
if (!needImport(importPath, file)) {
@@ -95,6 +104,16 @@ public class ImportInsertHelper {
writeImportToFile(importPath, file);
}
public static void optimizeImportsIfNeeded(JetFile file) {
if (CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) {
optimizeImports(file);
}
}
public static void optimizeImports(JetFile file) {
new OptimizeImportsProcessor(file.getProject(), file).runWithoutProgress();
}
public static void writeImportToFile(@NotNull ImportPath importPath, @NotNull JetFile file) {
if (file instanceof JetCodeFragment) {
JetImportDirective newDirective = JetPsiFactory.createImportDirective(file.getProject(), importPath);
@@ -105,6 +124,7 @@ public class ImportInsertHelper {
JetImportList importList = file.getImportList();
if (importList != null) {
JetImportDirective newDirective = JetPsiFactory.createImportDirective(file.getProject(), importPath);
importList.add(JetPsiFactory.createNewLine(file.getProject()));
importList.add(newDirective);
}
else {
@@ -20,12 +20,12 @@ import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters2;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
@@ -33,6 +33,7 @@ import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import java.util.LinkedList;
import java.util.List;
//TODO: should use change signature to deal with cases of multiple overridden descriptors
public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsFactory {
@NotNull
@Override
@@ -71,9 +72,9 @@ public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsF
ResolvedCall<?> resolvedCall =
context.get(BindingContext.RESOLVED_CALL, ((JetOperationExpression) expression).getOperationReference());
if (resolvedCall != null) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getResultingDescriptor());
if (declaration instanceof JetFunction) {
actions.add(new ChangeFunctionReturnTypeFix((JetFunction) declaration, expectedType));
JetFunction declaration = getFunctionDeclaration(context, resolvedCall);
if (declaration != null) {
actions.add(new ChangeFunctionReturnTypeFix(declaration, expectedType));
}
}
}
@@ -82,9 +83,9 @@ public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsF
if (parentBinary.getRight() == expression) {
ResolvedCall<?> resolvedCall = context.get(BindingContext.RESOLVED_CALL, parentBinary.getOperationReference());
if (resolvedCall != null) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getResultingDescriptor());
if (declaration instanceof JetFunction) {
JetParameter binaryOperatorParameter = ((JetFunction) declaration).getValueParameterList().getParameters().get(0);
JetFunction declaration = getFunctionDeclaration(context, resolvedCall);
if (declaration != null) {
JetParameter binaryOperatorParameter = declaration.getValueParameterList().getParameters().get(0);
actions.add(new ChangeParameterTypeFix(binaryOperatorParameter, expressionType));
}
}
@@ -96,9 +97,9 @@ public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsF
ResolvedCall<?> resolvedCall =
context.get(BindingContext.RESOLVED_CALL, ((JetCallExpression) expression).getCalleeExpression());
if (resolvedCall != null) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getResultingDescriptor());
if (declaration instanceof JetFunction) {
actions.add(new ChangeFunctionReturnTypeFix((JetFunction) declaration, expectedType));
JetFunction declaration = getFunctionDeclaration(context, resolvedCall);
if (declaration != null) {
actions.add(new ChangeFunctionReturnTypeFix(declaration, expectedType));
}
}
}
@@ -128,4 +129,13 @@ public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsF
}
return actions;
}
@Nullable
private static JetFunction getFunctionDeclaration(@NotNull BindingContext context, @NotNull ResolvedCall<?> resolvedCall) {
PsiElement result = QuickFixUtil.safeGetDeclaration(context, resolvedCall);
if (result instanceof JetFunction) {
return (JetFunction) result;
}
return null;
}
}
@@ -90,10 +90,10 @@ public class QuickFixUtil {
if (overriddenReturnType == null) {
return null;
}
if (matchingReturnType == null || JetTypeChecker.INSTANCE.isSubtypeOf(overriddenReturnType, matchingReturnType)) {
if (matchingReturnType == null || JetTypeChecker.DEFAULT.isSubtypeOf(overriddenReturnType, matchingReturnType)) {
matchingReturnType = overriddenReturnType;
}
else if (!JetTypeChecker.INSTANCE.isSubtypeOf(matchingReturnType, overriddenReturnType)) {
else if (!JetTypeChecker.DEFAULT.isSubtypeOf(matchingReturnType, overriddenReturnType)) {
return null;
}
}
@@ -109,7 +109,7 @@ public class QuickFixUtil {
BindingContext context = ResolvePackage.getBindingContext(callExpression.getContainingJetFile());
ResolvedCall<?> resolvedCall = context.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
if (resolvedCall == null) return null;
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
PsiElement declaration = safeGetDeclaration(context, resolvedCall);
if (declaration instanceof JetFunction) {
return ((JetFunction) declaration).getValueParameterList();
}
@@ -119,6 +119,16 @@ public class QuickFixUtil {
return null;
}
@Nullable
public static PsiElement safeGetDeclaration(@NotNull BindingContext context, @NotNull ResolvedCall<?> resolvedCall) {
List<PsiElement> declarations = BindingContextUtils.descriptorToDeclarations(context, resolvedCall.getResultingDescriptor());
//do not create fix if descriptor has more than one overridden declaration
if (declarations.size() == 1) {
return declarations.iterator().next();
}
return null;
}
@Nullable
public static JetParameter getParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList(@NotNull JetFunctionLiteralExpression functionLiteralExpression) {
if (!(functionLiteralExpression.getParent() instanceof JetCallExpression)) {
@@ -22,9 +22,11 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.psi.JetProperty;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.JetBundle;
@@ -40,7 +42,7 @@ public class SpecifyTypeExplicitlyFix extends PsiElementBaseIntentionAction {
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement element) {
//noinspection unchecked
JetNamedDeclaration declaration = PsiTreeUtil.getParentOfType(element, JetProperty.class, JetNamedFunction.class);
JetType type = getTypeForDeclaration(declaration);
@@ -51,12 +53,12 @@ public class SpecifyTypeExplicitlyFix extends PsiElementBaseIntentionAction {
addTypeAnnotation(project, editor, (JetNamedFunction) declaration, type);
}
else {
assert false : "Couldn't find property or function";
assert false : "Couldn't find property or function " + JetPsiUtil.getElementTextWithContext((JetElement) element);
}
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
public boolean isAvailable(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement element) {
//noinspection unchecked
JetNamedDeclaration declaration = PsiTreeUtil.getParentOfType(element, JetProperty.class, JetNamedFunction.class);
if (declaration instanceof JetProperty) {
@@ -66,7 +68,7 @@ public class SpecifyTypeExplicitlyFix extends PsiElementBaseIntentionAction {
setText(JetBundle.message("specify.type.explicitly.add.return.type.action.name"));
}
else {
assert false : "Couldn't find property or function";
assert false : "Couldn't find property or function " + JetPsiUtil.getElementTextWithContext((JetElement) element);
}
return !getTypeForDeclaration(declaration).isError();
@@ -98,7 +98,7 @@ public class JetNameSuggester {
private static void addNamesForType(ArrayList<String> result, JetType jetType, JetNameValidator validator) {
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
JetTypeChecker typeChecker = JetTypeChecker.DEFAULT;
jetType = TypeUtils.makeNotNullable(jetType); // wipe out '?'
if (ErrorUtils.containsErrorType(jetType)) return;
if (typeChecker.equalTypes(builtIns.getBooleanType(), jetType)) {
@@ -440,7 +440,7 @@ public class JetRefactoringUtil {
BindingContext bindingContext = AnalyzerFacadeWithCache.getContextForElement(expression);
JetType expressionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, expression);
if (expressionType == null || !(expressionType instanceof PackageType) &&
!JetTypeChecker.INSTANCE.equalTypes(KotlinBuiltIns.
!JetTypeChecker.DEFAULT.equalTypes(KotlinBuiltIns.
getInstance().getUnitType(), expressionType)) {
expressions.add(expression);
}
@@ -59,7 +59,8 @@ public class ExtractKotlinFunctionHandler(public val allContainersEnabled: Boole
editor: Editor,
file: JetFile,
elements: List<PsiElement>,
targetSibling: PsiElement
targetSibling: PsiElement,
preprocessor: ((ExtractionDescriptor) -> Unit)? = null
) {
val project = file.getProject()
@@ -83,7 +84,7 @@ public class ExtractKotlinFunctionHandler(public val allContainersEnabled: Boole
dialog.getCurrentDescriptor()
}
preprocessor?.invoke(descriptor)
project.executeWriteCommand(EXTRACT_FUNCTION) { descriptor.generateFunction() }
}
@@ -45,6 +45,12 @@ import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
import org.jetbrains.jet.lang.psi.JetDeclaration
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
import org.jetbrains.jet.lang.psi.JetUserType
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.jet.lang.psi.JetParameter
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
data class ExtractionOptions(val inferUnitTypeForUnusedValues: Boolean) {
class object {
@@ -95,20 +101,31 @@ class ExtractionData(
val originalStartOffset = originalElements.first?.let { e -> e.getTextRange()!!.getStartOffset() }
private val itFakeDeclaration by Delegates.lazy { JetPsiFactory.createParameter(project, "it", null) }
val refOffsetToDeclaration by Delegates.lazy {
fun isExtractableIt(descriptor: DeclarationDescriptor, context: BindingContext): Boolean {
if (!(descriptor is ValueParameterDescriptor && (context[BindingContext.AUTO_CREATED_IT, descriptor] ?: false))) return false
val function = BindingContextUtils.descriptorToDeclaration(context, descriptor.getContainingDeclaration()) as? JetFunctionLiteral
return function == null || !function.isInsideOf(originalElements)
}
if (originalStartOffset != null) {
val resultMap = HashMap<Int, ResolveResult>()
for ((ref, context) in JetFileReferencesResolver.resolve(originalFile, getExpressions())) {
if (ref !is JetSimpleNameExpression) continue
val resolvedCallKey = (ref.getParent() as? JetThisExpression) ?: ref
val resolvedCall = context[BindingContext.RESOLVED_CALL, resolvedCallKey]
val resolvedCall = context[BindingContext.RESOLVED_CALL, resolvedCallKey]?.let {
(it as? VariableAsFunctionResolvedCall)?.functionCall ?: it
}
val descriptor = context[BindingContext.REFERENCE_TARGET, ref]
if (descriptor == null) continue
val declaration = DescriptorToDeclarationUtil.getDeclaration(project, descriptor, context) as? PsiNamedElement
if (declaration == null) continue
?: if (isExtractableIt(descriptor, context)) itFakeDeclaration else continue
val offset = ref.getTextRange()!!.getStartOffset() - originalStartOffset
resultMap[offset] = ResolveResult(ref, declaration, descriptor, resolvedCall)
@@ -31,19 +31,21 @@ import org.jetbrains.jet.lang.psi.psiUtil.replaced
import org.jetbrains.jet.lang.psi.JetQualifiedExpression
import org.jetbrains.jet.lang.psi.JetTypeParameter
import org.jetbrains.jet.lang.psi.JetTypeConstraint
import kotlin.properties.Delegates
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.ErrorMessage
data class Parameter(
val argumentText: String,
val name: String,
var mirrorVarName: String?,
val parameterType: JetType,
val receiverCandidate: Boolean
) {
trait Parameter {
val argumentText: String
val name: String
val mirrorVarName: String?
val parameterType: JetType
val parameterTypeCandidates: List<JetType>
val receiverCandidate: Boolean
val nameForRef: String get() = mirrorVarName ?: name
fun copy(name: String, parameterType: JetType): Parameter
}
data class TypeParameter(
@@ -165,7 +167,7 @@ class AnalysisResult (
fun renderMessage(): String {
val message = JetRefactoringBundle.message(when(this) {
NO_EXPRESSION -> "cannot.refactor.no.expresson"
NO_EXPRESSION -> "cannot.refactor.no.expression"
NO_CONTAINER -> "cannot.refactor.no.container"
SUPER_CALL -> "cannot.extract.super.call"
DENOTABLE_TYPES -> "parameter.types.are.not.denotable"
@@ -38,7 +38,6 @@ import org.jetbrains.jet.lang.psi.JetPsiFactory.FunctionBuilder
import org.jetbrains.jet.plugin.refactoring.JetNameValidatorImpl
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
import org.jetbrains.jet.plugin.imports.canBeReferencedViaImport
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import com.intellij.psi.PsiNamedElement
import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor
@@ -56,18 +55,16 @@ import org.jetbrains.jet.lang.diagnostics.Errors
import org.jetbrains.jet.lang.psi.psiUtil.replaced
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.ErrorMessage
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.WriteValueInstruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.CallInstruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.JetElementInstruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.OperationInstruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.ReadValueInstruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*
import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.getNextInstructions
import kotlin.properties.Delegates
import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.traverse
import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder
import org.jetbrains.jet.lang.resolve.bindingContextUtil.getTargetFunctionDescriptor
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.jet.lang.resolve.OverridingUtil
private val DEFAULT_FUNCTION_NAME = "myFun"
private val DEFAULT_RETURN_TYPE = KotlinBuiltIns.getInstance().getUnitType()
@@ -84,7 +81,10 @@ private fun List<Instruction>.getModifiedVarDescriptors(bindingContext: BindingC
private fun List<Instruction>.getExitPoints(): List<Instruction> =
filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } }
private fun List<Instruction>.getResultType(bindingContext: BindingContext, options: ExtractionOptions): JetType {
private fun List<Instruction>.getResultType(
pseudocode: Pseudocode,
bindingContext: BindingContext,
options: ExtractionOptions): JetType {
fun instructionToType(instruction: Instruction): JetType? {
val expression = when (instruction) {
is ReturnValueInstruction -> {
@@ -101,10 +101,7 @@ private fun List<Instruction>.getResultType(bindingContext: BindingContext, opti
}
if (expression == null) return null
if (options.inferUnitTypeForUnusedValues) {
val pseudocode = firstOrNull()?.owner
if (pseudocode != null && expression.isStatement(pseudocode)) return null
}
if (options.inferUnitTypeForUnusedValues && expression.isStatement(pseudocode)) return null
return bindingContext[BindingContext.EXPRESSION_TYPE, expression]
}
@@ -123,10 +120,17 @@ private fun JetType.isMeaningful(): Boolean {
}
private fun List<Instruction>.analyzeControlFlow(
pseudocode: Pseudocode,
bindingContext: BindingContext,
options: ExtractionOptions,
parameters: Set<Parameter>
): Pair<ControlFlow, ErrorMessage?> {
fun isCurrentFunctionReturn(expression: JetReturnExpression): Boolean {
val functionDescriptor = expression.getTargetFunctionDescriptor(bindingContext)
val currentDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, pseudocode.getCorrespondingElement()]
return currentDescriptor == functionDescriptor
}
val exitPoints = getExitPoints()
val valuedReturnExits = ArrayList<ReturnValueInstruction>()
@@ -135,19 +139,26 @@ private fun List<Instruction>.analyzeControlFlow(
exitPoints.forEach {
val e = (it as? UnconditionalJumpInstruction)?.element
val insn =
if (e != null && e !is JetBreakExpression && e !is JetContinueExpression) {
it.previousInstructions.firstOrNull()
when {
it !is ReturnValueInstruction && it !is ReturnNoValueInstruction && it.owner != pseudocode ->
null
e != null && e !is JetBreakExpression && e !is JetContinueExpression ->
it.previousInstructions.firstOrNull()
else ->
it
}
else it
when (insn) {
is ReturnValueInstruction -> valuedReturnExits.add(insn)
is ReturnValueInstruction ->
if (isCurrentFunctionReturn(insn.element as JetReturnExpression)) {
valuedReturnExits.add(insn)
}
is AbstractJumpInstruction -> {
val element = insn.element
if (element is JetReturnExpression
|| element is JetBreakExpression
|| element is JetContinueExpression) {
if ((element is JetReturnExpression && isCurrentFunctionReturn(element))
|| element is JetBreakExpression
|| element is JetContinueExpression) {
jumpExits.add(insn)
}
else if (element !is JetThrowExpression) {
@@ -161,8 +172,8 @@ private fun List<Instruction>.analyzeControlFlow(
}
}
val typeOfDefaultFlow = defaultExits.getResultType(bindingContext, options)
val returnValueType = valuedReturnExits.getResultType(bindingContext, options)
val typeOfDefaultFlow = defaultExits.getResultType(pseudocode, bindingContext, options)
val returnValueType = valuedReturnExits.getResultType(pseudocode, bindingContext, options)
val defaultControlFlow = DefaultControlFlow(if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow)
val outParameters = parameters.filterTo(HashSet<Parameter>()) { it.mirrorVarName != null }
@@ -204,7 +215,7 @@ private fun List<Instruction>.analyzeControlFlow(
}
if (!valuedReturnExits.checkEquivalence(false)) return multipleExitsError
return Pair(ExpressionEvaluationWithCallSiteReturn(valuedReturnExits.getResultType(bindingContext, options)), null)
return Pair(ExpressionEvaluationWithCallSiteReturn(valuedReturnExits.getResultType(pseudocode, bindingContext, options)), null)
}
if (jumpExits.isNotEmpty()) {
@@ -307,8 +318,51 @@ private fun JetType.processTypeIfExtractable(
}
}
private class MutableParameter(
override val argumentText: String,
override val name: String,
override val mirrorVarName: String?,
override val receiverCandidate: Boolean
): Parameter {
// All modifications happen in the same thread
private var writable: Boolean = true
private val defaultTypes = HashSet<JetType>()
private val typePredicates = HashSet<TypePredicate>()
fun addDefaultType(jetType: JetType) {
assert(writable, "Can't add type to non-writable parameter $name")
defaultTypes.add(jetType)
}
fun addTypePredicate(predicate: TypePredicate) {
assert(writable, "Can't add type predicate to non-writable parameter $name")
typePredicates.add(predicate)
}
override val parameterTypeCandidates: List<JetType> by Delegates.lazy {
writable = false
listOf(parameterType) + TypeUtils.getAllSupertypes(parameterType).filter(and(typePredicates))
}
override val parameterType: JetType by Delegates.lazy {
writable = false
CommonSupertypes.commonSupertype(defaultTypes)
}
override fun copy(name: String, parameterType: JetType): Parameter = DelegatingParameter(this, name, parameterType)
}
private class DelegatingParameter(
val original: Parameter,
override val name: String,
override val parameterType: JetType
): Parameter by original {
override fun copy(name: String, parameterType: JetType): Parameter = DelegatingParameter(original, name, parameterType)
}
private fun ExtractionData.inferParametersInfo(
commonParent: PsiElement,
pseudocode: Pseudocode,
localInstructions: List<Instruction>,
bindingContext: BindingContext,
replacementMap: MutableMap<Int, Replacement>,
@@ -323,7 +377,9 @@ private fun ExtractionData.inferParametersInfo(
)
val modifiedVarDescriptors = localInstructions.getModifiedVarDescriptors(bindingContext)
val extractedDescriptorToParameter = HashMap<DeclarationDescriptor, Parameter>()
val extractedDescriptorToParameter = HashMap<DeclarationDescriptor, MutableParameter>()
val valueUsageMap = pseudocode.collectValueUsages()
for (refInfo in getBrokenReferencesInfo(createTemporaryCodeBlock())) {
val (originalRef, originalDeclaration, originalDescriptor, resolvedCall) = refInfo.resolveResult
@@ -379,64 +435,42 @@ private fun ExtractionData.inferParametersInfo(
val extractParameter = extractThis || extractLocalVar
if (extractParameter) {
val parameterType =
if (hasThisReceiver) {
when (descriptorToExtract) {
is ClassDescriptor -> descriptorToExtract.getDefaultType()
is CallableDescriptor -> descriptorToExtract.getReceiverParameter()?.getType()
else -> null
} ?: DEFAULT_PARAMETER_TYPE
}
else bindingContext[BindingContext.EXPRESSION_TYPE, originalRef] ?: DEFAULT_PARAMETER_TYPE
val parameterType = when {
receiver.exists() -> receiver.getType()
else -> bindingContext[BindingContext.AUTOCAST, originalRef]
?: bindingContext[BindingContext.EXPRESSION_TYPE, originalRef]
?: DEFAULT_PARAMETER_TYPE
}
if (!parameterType.processTypeIfExtractable(bindingContext, typeParameters, nonDenotableTypes)) continue
val existingParameter = extractedDescriptorToParameter[descriptorToExtract]
val parameter: Parameter =
if (existingParameter != null) {
if (!JetTypeChecker.INSTANCE.equalTypes(existingParameter.parameterType, parameterType)) {
val newParameter = existingParameter.copy(
parameterType = CommonSupertypes.commonSupertype(listOf(existingParameter.parameterType, parameterType))
)
val parameterTypePredicate =
pseudocode.getElementValue(originalRef)?.let { getExpectedTypePredicate(it, valueUsageMap, bindingContext) } ?: AllTypes
extractedDescriptorToParameter[descriptorToExtract] = newParameter
for ((offset, replacement) in replacementMap) {
if (replacement is ParameterReplacement && replacement.parameter == existingParameter) {
replacementMap[offset] = replacement.copy(newParameter)
}
}
newParameter
val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) {
val parameterName =
if (extractThis) {
JetNameSuggester.suggestNames(parameterType, varNameValidator, null).first()
}
else existingParameter
}
else {
val parameterName =
if (extractThis) {
JetNameSuggester.suggestNames(parameterType, varNameValidator, null).first()
}
else originalDeclaration.getName()!!
else originalDeclaration.getName()!!
val mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors)
varNameValidator.validateName(parameterName)!!
else null
val mirrorVarName =
if (descriptorToExtract in modifiedVarDescriptors) varNameValidator.validateName(parameterName)!! else null
val argumentText =
if (hasThisReceiver && extractThis)
"this@${parameterType.getConstructor().getDeclarationDescriptor()!!.getName().asString()}"
else
(thisExpr ?: ref).getText() ?: throw AssertionError("'this' reference shouldn't be empty: code fragment = ${getCodeFragmentText()}")
val argumentText =
if (hasThisReceiver && extractThis)
"this@${parameterType.getConstructor().getDeclarationDescriptor()!!.getName().asString()}"
else
(thisExpr ?: ref).getText() ?: throw AssertionError("'this' reference shouldn't be empty: code fragment = ${getCodeFragmentText()}")
val parameter = Parameter(argumentText, parameterName, mirrorVarName, parameterType, extractThis)
MutableParameter(argumentText, parameterName, mirrorVarName, extractThis)
}
extractedDescriptorToParameter[descriptorToExtract] = parameter
parameter
}
parameter.addDefaultType(parameterType)
parameter.addTypePredicate(parameterTypePredicate)
replacementMap[refInfo.offsetInBody] =
if (hasThisReceiver && extractThis) AddPrefixReplacement(parameter) else RenameReplacement(parameter)
if (hasThisReceiver && extractThis) AddPrefixReplacement(parameter) else RenameReplacement(parameter)
}
}
}
@@ -451,20 +485,20 @@ private fun ExtractionData.inferParametersInfo(
}
private fun ExtractionData.checkLocalDeclarationsWithNonLocalUsages(
allInstructions: List<Instruction>,
pseudocode: Pseudocode,
localInstructions: List<Instruction>,
bindingContext: BindingContext
): ErrorMessage? {
// todo: non-locally used declaration can be turned into the output value
val declarations = ArrayList<JetNamedDeclaration>()
for (instruction in allInstructions) {
if (instruction in localInstructions) continue
PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, bindingContext)?.let { descriptor ->
val declaration = DescriptorToDeclarationUtil.getDeclaration(project, descriptor, bindingContext)
if (declaration is JetNamedDeclaration && declaration.isInsideOf(originalElements)) {
declarations.add(declaration)
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
if (instruction !in localInstructions) {
PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, bindingContext)?.let { descriptor ->
val declaration = DescriptorToDeclarationUtil.getDeclaration(project, descriptor, bindingContext)
if (declaration is JetNamedDeclaration && declaration.isInsideOf(originalElements)) {
declarations.add(declaration)
}
}
}
}
@@ -500,6 +534,16 @@ private fun ExtractionData.checkDeclarationsMovingOutOfScope(controlFlow: Contro
return null
}
private fun ExtractionData.getLocalInstructions(pseudocode: Pseudocode): List<Instruction> {
val instructions = ArrayList<Instruction>()
pseudocode.traverse(TraversalOrder.FORWARD) {
if (it is JetElementInstruction && it.element.isInsideOf(originalElements)) {
instructions.add(it)
}
}
return instructions
}
fun ExtractionData.performAnalysis(): AnalysisResult {
if (originalElements.empty) {
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(ErrorMessage.NO_EXPRESSION))
@@ -516,16 +560,14 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
val bindingContext = resolveSession.resolveToElement(enclosingDeclaration.getBodyExpression())
val pseudocode = PseudocodeUtil.generatePseudocode(enclosingDeclaration, bindingContext)
val localInstructions = pseudocode.getInstructions().filter {
it is JetElementInstruction && it.element.isInsideOf(originalElements)
}
val localInstructions = getLocalInstructions(pseudocode)
val replacementMap = HashMap<Int, Replacement>()
val parameters = HashSet<Parameter>()
val typeParameters = HashSet<TypeParameter>()
val nonDenotableTypes = HashSet<JetType>()
val parameterError = inferParametersInfo(
commonParent, localInstructions, bindingContext, replacementMap, parameters, typeParameters, nonDenotableTypes
commonParent, pseudocode, localInstructions, bindingContext, replacementMap, parameters, typeParameters, nonDenotableTypes
)
if (parameterError != null) {
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(parameterError))
@@ -533,7 +575,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
val messages = ArrayList<ErrorMessage>()
val (controlFlow, controlFlowMessage) = localInstructions.analyzeControlFlow(bindingContext, options, parameters)
val (controlFlow, controlFlowMessage) = localInstructions.analyzeControlFlow(pseudocode, bindingContext, options, parameters)
controlFlowMessage?.let { messages.add(it) }
controlFlow.returnType.processTypeIfExtractable(bindingContext, typeParameters, nonDenotableTypes)
@@ -547,7 +589,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
)
}
checkLocalDeclarationsWithNonLocalUsages(pseudocode.getInstructions(), localInstructions, bindingContext)?.let { messages.add(it) }
checkLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext)?.let { messages.add(it) }
checkDeclarationsMovingOutOfScope(controlFlow)?.let { messages.add(it) }
val functionNameValidator =
@@ -607,7 +649,7 @@ fun ExtractionDescriptor.validate(): ExtractionDescriptorWithConflicts {
if (diagnostics.any { it.getFactory() == Errors.UNRESOLVED_REFERENCE }
|| (currentDescriptor != null
&& !ErrorUtils.isError(currentDescriptor)
&& !compareDescriptors(currentDescriptor, resolveResult.descriptor))) {
&& !comparePossiblyOverridingDescriptors(currentDescriptor, resolveResult.descriptor))) {
conflicts.putValue(
currentRefExpr,
JetRefactoringBundle.message(
@@ -632,6 +674,15 @@ fun ExtractionDescriptor.validate(): ExtractionDescriptorWithConflicts {
return ExtractionDescriptorWithConflicts(this, conflicts)
}
private fun comparePossiblyOverridingDescriptors(currentDescriptor: DeclarationDescriptor?, originalDescriptor: DeclarationDescriptor?): Boolean {
if (compareDescriptors(currentDescriptor, originalDescriptor)) return true
if (originalDescriptor is CallableDescriptor) {
if (!OverridingUtil.traverseOverridenDescriptors(originalDescriptor) { !compareDescriptors(currentDescriptor, it) }) return true
}
return false
}
fun ExtractionDescriptor.getFunctionText(
withBody: Boolean = true,
descriptorRenderer: DescriptorRenderer = DescriptorRenderer.FQ_NAMES_IN_TYPES
@@ -795,6 +846,27 @@ fun ExtractionDescriptor.generateFunction(
}
}
fun insertCall(anchor: PsiElement, wrappedCall: JetExpression) {
val firstExpression = extractionData.getExpressions().firstOrNull()
val enclosingCall = firstExpression?.getParent() as? JetCallExpression
if (enclosingCall == null || firstExpression !in enclosingCall.getFunctionLiteralArguments()) {
anchor.replace(wrappedCall)
return
}
val argumentListExt = JetPsiFactory.createCallArguments(project, "(${wrappedCall.getText()})")
val argumentList = enclosingCall.getValueArgumentList()
if (argumentList == null) {
(anchor.getPrevSibling() as? PsiWhiteSpace)?.let { it.delete() }
anchor.replace(argumentListExt)
return
}
val newArgText = (argumentList.getArguments() + argumentListExt.getArguments()).map { it.getText() }.joinToString(", ", "(", ")")
argumentList.replace(JetPsiFactory.createCallArguments(project, newArgText))
anchor.delete()
}
fun makeCall(function: JetNamedFunction): JetNamedFunction {
val anchor = extractionData.originalElements.first
if (anchor == null) return function
@@ -808,7 +880,7 @@ fun ExtractionDescriptor.generateFunction(
val callText = parameters
.map { it.argumentText }
.makeString(separator = ", ", prefix = "$name(", postfix = ")")
.joinToString(separator = ", ", prefix = "$name(", postfix = ")")
val wrappedCall = when (controlFlow) {
is ExpressionEvaluationWithCallSiteReturn ->
JetPsiFactory.createReturn(project, callText)
@@ -833,7 +905,7 @@ fun ExtractionDescriptor.generateFunction(
else ->
JetPsiFactory.createExpression(project, callText)
}
anchor.replace(wrappedCall)
insertCall(anchor, wrappedCall)
return function
}
@@ -63,7 +63,7 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
setModal(true);
setTitle(JetRefactoringBundle.message("extract.function"));
init();
update(false);
update();
}
private void createUIComponents() {
@@ -94,10 +94,8 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
return true;
}
private void update(boolean recreateDescriptor) {
if (recreateDescriptor) {
this.currentDescriptor = createDescriptor();
}
private void update() {
this.currentDescriptor = createDescriptor();
setOKActionEnabled(checkNames());
signaturePreviewField.setText(
@@ -116,7 +114,7 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent event) {
update(true);
update();
}
}
);
@@ -130,7 +128,7 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
new ItemListener() {
@Override
public void itemStateChanged(@NotNull ItemEvent e) {
update(true);
update();
}
}
);
@@ -138,7 +136,7 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
parameterTablePanel = new KotlinParameterTablePanel() {
@Override
protected void updateSignature() {
KotlinExtractFunctionDialog.this.update(true);
KotlinExtractFunctionDialog.this.update();
}
@Override
@@ -19,18 +19,25 @@ package org.jetbrains.jet.plugin.refactoring.extractFunction.ui;
import com.intellij.ui.BooleanTableCellRenderer;
import com.intellij.ui.TableUtil;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBComboBoxLabel;
import com.intellij.ui.components.editors.JBComboBoxTableCellEditorComponent;
import com.intellij.ui.table.JBTable;
import com.intellij.util.Function;
import com.intellij.util.ui.AbstractTableCellEditor;
import com.intellij.util.ui.EditableModel;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester;
import org.jetbrains.jet.plugin.refactoring.extractFunction.Parameter;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import java.awt.*;
@@ -42,11 +49,13 @@ public class KotlinParameterTablePanel extends JPanel {
public static class ParameterInfo {
private final Parameter originalParameter;
private String name;
private JetType type;
private boolean enabled = true;
public ParameterInfo(Parameter originalParameter) {
this.originalParameter = originalParameter;
this.name = originalParameter.getName();
this.type = originalParameter.getParameterType();
}
public Parameter getOriginalParameter() {
@@ -69,18 +78,16 @@ public class KotlinParameterTablePanel extends JPanel {
this.name = name;
}
public String getTypeAsString() {
return DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(getOriginalParameter().getParameterType());
public JetType getType() {
return type;
}
public void setType(JetType type) {
this.type = type;
}
public Parameter toParameter() {
return new Parameter(
originalParameter.getArgumentText(),
name,
originalParameter.getMirrorVarName(),
originalParameter.getParameterType(),
originalParameter.getReceiverCandidate()
);
return originalParameter.copy(name, type);
}
}
@@ -111,6 +118,7 @@ public class KotlinParameterTablePanel extends JPanel {
myTable.setTableHeader(null);
myTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myTable.setCellSelectionEnabled(true);
TableColumn checkBoxColumn = myTable.getColumnModel().getColumn(MyTableModel.CHECKMARK_COLUMN);
TableUtil.setupCheckboxColumn(checkBoxColumn);
@@ -128,6 +136,55 @@ public class KotlinParameterTablePanel extends JPanel {
}
);
myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_TYPE_COLUMN).setCellRenderer(new DefaultTableCellRenderer() {
private final JBComboBoxLabel myLabel = new JBComboBoxLabel();
@Override
@NotNull
public Component getTableCellRendererComponent(
@NotNull JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column
) {
myLabel.setText(String.valueOf(value));
myLabel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
myLabel.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
if (isSelected) {
myLabel.setSelectionIcon();
} else {
myLabel.setRegularIcon();
}
return myLabel;
}
});
myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_TYPE_COLUMN).setCellEditor(new AbstractTableCellEditor() {
final JBComboBoxTableCellEditorComponent myEditorComponent = new JBComboBoxTableCellEditorComponent();
@Override
@Nullable
public Object getCellEditorValue() {
return myEditorComponent.getEditorValue();
}
@Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column
) {
ParameterInfo info = parameterInfos.get(row);
myEditorComponent.setCell(table, row, column);
myEditorComponent.setOptions(info.getOriginalParameter().getParameterTypeCandidates().toArray());
myEditorComponent.setDefaultValue(info.getType());
myEditorComponent.setToString(new Function<Object, String>() {
@Override
public String fun(Object o) {
return DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType((JetType) o);
}
});
return myEditorComponent;
}
});
myTable.setPreferredScrollableViewportSize(new Dimension(250, myTable.getRowHeight() * 5));
myTable.setShowGrid(false);
myTable.setIntercellSpacing(new Dimension(0, 0));
@@ -159,20 +216,6 @@ public class KotlinParameterTablePanel extends JPanel {
}
});
// F2: edit parameter name
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "edit_parameter_name");
actionMap.put("edit_parameter_name", new AbstractAction() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
if (!myTable.isEditing()) {
int row = myTable.getSelectedRow();
if (row >= 0 && row < myTableModel.getRowCount()) {
TableUtil.editCellAt(myTable, row, MyTableModel.PARAMETER_NAME_COLUMN);
}
}
}
});
// make ENTER work when the table has focus
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "invoke_impl");
actionMap.put("invoke_impl", new AbstractAction() {
@@ -266,32 +309,29 @@ public class KotlinParameterTablePanel extends JPanel {
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case CHECKMARK_COLUMN: {
case CHECKMARK_COLUMN:
return parameterInfos.get(rowIndex).isEnabled();
}
case PARAMETER_NAME_COLUMN: {
case PARAMETER_NAME_COLUMN:
return parameterInfos.get(rowIndex).getName();
}
case PARAMETER_TYPE_COLUMN: {
return parameterInfos.get(rowIndex).getTypeAsString();
}
case PARAMETER_TYPE_COLUMN:
return parameterInfos.get(rowIndex).getType();
default:
return null;
}
assert false;
return null;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
ParameterInfo info = parameterInfos.get(rowIndex);
switch (columnIndex) {
case CHECKMARK_COLUMN: {
parameterInfos.get(rowIndex).setEnabled((Boolean) aValue);
info.setEnabled((Boolean) aValue);
fireTableRowsUpdated(rowIndex, rowIndex);
myTable.getSelectionModel().setSelectionInterval(rowIndex, rowIndex);
updateSignature();
break;
}
case PARAMETER_NAME_COLUMN: {
ParameterInfo info = parameterInfos.get(rowIndex);
String name = (String) aValue;
if (JetNameSuggester.isIdentifier(name)) {
info.setName(name);
@@ -299,16 +339,24 @@ public class KotlinParameterTablePanel extends JPanel {
updateSignature();
break;
}
case PARAMETER_TYPE_COLUMN: {
info.setType((JetType) aValue);
updateSignature();
break;
}
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
ParameterInfo info = parameterInfos.get(rowIndex);
switch (columnIndex) {
case CHECKMARK_COLUMN:
return isEnabled();
case PARAMETER_NAME_COLUMN:
return isEnabled() && parameterInfos.get(rowIndex).isEnabled();
return isEnabled() && info.isEnabled();
case PARAMETER_TYPE_COLUMN:
return isEnabled() && info.isEnabled() && info.getOriginalParameter().getParameterTypeCandidates().size() > 1;
default:
return false;
}
@@ -31,11 +31,8 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import kotlin.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.analyzer.AnalyzerPackage;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
@@ -139,7 +136,7 @@ public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase {
JetType typeNoExpectedType = AnalyzerPackage.computeTypeInfoInContext(
expression, scope, bindingTrace, dataFlowInfo, TypeUtils.NO_EXPECTED_TYPE, resolveSession.getModuleDescriptor()
).getType();
if (expressionType != null && typeNoExpectedType != null && !JetTypeChecker.INSTANCE.equalTypes(expressionType,
if (expressionType != null && typeNoExpectedType != null && !JetTypeChecker.DEFAULT.equalTypes(expressionType,
typeNoExpectedType)) {
noTypeInference = true;
}
@@ -149,7 +146,7 @@ public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase {
return;
}
if (expressionType != null &&
JetTypeChecker.INSTANCE.equalTypes(KotlinBuiltIns.getInstance().getUnitType(), expressionType)) {
JetTypeChecker.DEFAULT.equalTypes(KotlinBuiltIns.getInstance().getUnitType(), expressionType)) {
showErrorHint(project, editor, JetRefactoringBundle.message("cannot.refactor.expression.has.unit.type"));
return;
}
+2 -2
View File
@@ -48,7 +48,7 @@ fun blockAndAndMismatch1() : Int {
return <error>true && false</error>
}
fun blockAndAndMismatch2() : Int {
<warning>(return <error>true</error>) && (return <error>false</error>)</warning>
(return <error>true</error>) <warning>&& (return <error>false</error>)</warning>
}
fun blockAndAndMismatch3() : Int {
@@ -58,7 +58,7 @@ fun blockAndAndMismatch4() : Int {
return <error>true || false</error>
}
fun blockAndAndMismatch5() : Int {
<warning>(return <error>true</error>) || (return <error>false</error>)</warning>
(return <error>true</error>) <warning>|| (return <error>false</error>)</warning>
}
fun blockReturnValueTypeMatch1() : Int {
return if (1 > 2) <error>1.0</error> else <error>2.0</error>
+3 -3
View File
@@ -126,17 +126,17 @@ fun t8() : Int {
}
fun blockAndAndMismatch() : Boolean {
<warning>(return true) || (return false)</warning>
(return true) <warning>|| (return false)</warning>
<warning>return true</warning>
}
fun tf() : Int {
try {<warning>return 1</warning>} finally{return 1}
try {<warning>return</warning> 1} finally{return 1}
<warning>return 1</warning>
}
fun failtest(<warning>a</warning> : Int) : Int {
if (fail() || <warning>true</warning>) <warning>{
if (fail() <warning>|| true</warning>) <warning>{
}</warning>
<warning>return 1</warning>
@@ -0,0 +1,5 @@
package bar;
public interface Bar {
}
@@ -0,0 +1,5 @@
package bar;
public interface Other {
}
@@ -0,0 +1,9 @@
package foo;
import bar.Bar;
import bar.Other;
import java.util.ArrayList;
public class Foo {
abstract public Bar foo(ArrayList<Integer> list, Other other);
}
@@ -0,0 +1,7 @@
package foo
class Impl: Foo() {
<caret>
}
// KT-4732 Override/Implement action does not add all imports when "Optimize imports on the fly" is enabled
@@ -0,0 +1,14 @@
package foo
import java.util.ArrayList
import bar.Other
import bar.Bar
class Impl: Foo() {
override fun foo(list: ArrayList<Int>?, other: Other?): Bar? {
throw UnsupportedOperationException()
}
}
// KT-4732 Override/Implement action does not add all imports when "Optimize imports on the fly" is enabled
@@ -1,11 +1,9 @@
package foo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class JavaClass {
@NotNull
@Override
public void bar(@Nullable String price) {
@@ -2,6 +2,7 @@ LineBreakpoint created at abstractFunCall.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! abstractFunCall.AbstractFunCallPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
abstractFunCall.kt:4
Compile bytecode for (1 as java.lang.Number).intValue()
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,15 @@ LineBreakpoint created at arrays.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! arrays.ArraysPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
arrays.kt:4
Compile bytecode for array(1, 2).map { it.toString() }
Compile bytecode for array(1, 2, 101, 102).filter { it > 100 }
Compile bytecode for array(1, 2).none()
Compile bytecode for array(1, 2).count()
Compile bytecode for array(1, 2).size
Compile bytecode for array(1, 2).first()
Compile bytecode for array(1, 2).last()
Compile bytecode for intArray(1, 2).max()
Compile bytecode for array(1, 2).max()
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,8 @@ LineBreakpoint created at classFromAnotherPackage.kt:7
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! classFromAnotherPackage.ClassFromAnotherPackagePackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
classFromAnotherPackage.kt:6
Compile bytecode for MyJavaClass()
Compile bytecode for stepInto.MyJavaClass()
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,8 @@ LineBreakpoint created at classObjectVal.kt:10
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! classObjectVal.ClassObjectValPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
classObjectVal.kt:9
Compile bytecode for coProp
Compile bytecode for MyClass.coProp
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,52 @@
LineBreakpoint created at clearCache.kt:12
LineBreakpoint created at clearCache.kt:20
LineBreakpoint created at clearCache.kt:31
LineBreakpoint created at clearCache.kt:42
LineBreakpoint created at clearCache.kt:52
LineBreakpoint created at clearCache.kt:60
LineBreakpoint created at clearCache.kt:78
LineBreakpoint created at clearCache.kt:86
LineBreakpoint created at clearCache.kt:95
LineBreakpoint created at clearCache.kt:105
LineBreakpoint created at clearCache.kt:113
LineBreakpoint created at clearCache.kt:123
LineBreakpoint created at clearCache.kt:131
LineBreakpoint created at clearCache.kt:149
LineBreakpoint created at clearCache.kt:154
LineBreakpoint created at clearCache.kt:161
LineBreakpoint created at clearCache.kt:169
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! clearCache.ClearCachePackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
clearCache.kt:11
Compile bytecode for a
clearCache.kt:19
Compile bytecode for a
clearCache.kt:30
Compile bytecode for i
clearCache.kt:30
clearCache.kt:41
Compile bytecode for i
clearCache.kt:41
clearCache.kt:51
Compile bytecode for o.test()
clearCache.kt:59
clearCache.kt:77
Compile bytecode for c.size()
clearCache.kt:85
clearCache.kt:94
clearCache.kt:104
clearCache.kt:112
Compile bytecode for c.size()
clearCache.kt:122
Compile bytecode for o.test()
clearCache.kt:130
clearCache.kt:148
Compile bytecode for obj.test()
clearCache.kt:153
clearCache.kt:160
Compile bytecode for o.test()
clearCache.kt:168
Compile bytecode for o.test()
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,17 @@ LineBreakpoint created at collections.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! collections.CollectionsPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
collections.kt:5
Compile bytecode for arrayListOf(1, 2).map { it.toString() }
Compile bytecode for arrayListOf(1, 2, 101, 102).filter { it > 100 }
Compile bytecode for arrayListOf(1, 2).max()
Compile bytecode for arrayListOf(1, 2).count()
Compile bytecode for arrayListOf(1, 2).size
Compile bytecode for arrayListOf(1, 2).drop(1)
Compile bytecode for ar.map { if (it > 50) "big" else "small" }
.filter { it == "small" }
.size
// RESULT: 2: I
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,14 @@ LineBreakpoint created at dependentOnFile.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! dependentOnFile.DependentOnFilePackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
dependentOnFile.kt:4
Compile bytecode for TestClass().testFun()
Compile bytecode for testFun()
Compile bytecode for TestObject.p
Compile bytecode for TestClass.p
Compile bytecode for 1.testExtFun()
Compile bytecode for testVal
Compile bytecode for 1.testExtVal
Compile bytecode for testDelVal
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at doubles.kt:7
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! doubles.DoublesPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
doubles.kt:6
Compile bytecode for d1 + d2
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,7 @@ LineBreakpoint created at enums.kt:7
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! enums.EnumsPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
enums.kt:6
Compile bytecode for A == MyEnum.A
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,20 @@
LineBreakpoint created at exceptions.kt:9
LineBreakpoint created at exceptions.kt:14
LineBreakpoint created at exceptions.kt:26
LineBreakpoint created at exceptions.kt:31
LineBreakpoint created at exceptions.kt:42
LineBreakpoint created at exceptions.kt:51
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! exceptions.ExceptionsPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
exceptions.kt:8
Compile bytecode for fail()
exceptions.kt:13
exceptions.kt:25
Compile bytecode for o as Derived
exceptions.kt:30
exceptions.kt:41
Compile bytecode for c.get(0)
exceptions.kt:50
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,10 @@ LineBreakpoint created at extractLocalVariables.kt:7
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! extractLocalVariables.ExtractLocalVariablesPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
extractLocalVariables.kt:6
Compile bytecode for a
Compile bytecode for klass.f1(1)
Compile bytecode for args.size
Compile bytecode for klass.b
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,9 @@ LineBreakpoint created at extractThis.kt:13
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! extractThis.ExtractThisPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
extractThis.kt:12
Compile bytecode for prop
Compile bytecode for this.prop
Compile bytecode for prop + a
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,10 @@ LineBreakpoint created at extractVariablesFromCall.kt:8
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! extractVariablesFromCall.ExtractVariablesFromCallPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
extractVariablesFromCall.kt:7
Compile bytecode for f1(a, s)
Compile bytecode for a.f2(s)
Compile bytecode for a f2 s
Compile bytecode for klass.f1(a, s)
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,10 @@ LineBreakpoint created at imports.kt:10
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! imports.ImportsPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
imports.kt:9
Compile bytecode for Collections.emptyList<String>()
Compile bytecode for ArrayList<Int>()
Compile bytecode for HashSet<Int>()
Compile bytecode for JHashMap<Int, Int>()
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,7 @@ LineBreakpoint created at insertInBlock.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! insertInBlock.InsertInBlockPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
insertInBlock.kt:5
Compile bytecode for 1 + 1
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,7 @@ LineBreakpoint created at multilineExpressionAtBreakpoint.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! multilineExpressionAtBreakpoint.MultilineExpressionAtBreakpointPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
multilineExpressionAtBreakpoint.kt:4
Compile bytecode for 1 + 1
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -1,7 +1,13 @@
LineBreakpoint created at privateMember.kt:5
LineBreakpoint created at privateMember.kt:9
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! privateMember.PrivateMemberPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
privateMember.kt:4
privateMember.kt:8
Compile bytecode for MyClass().privateFun()
Compile bytecode for MyClass().privateVal
Compile bytecode for MyClass.PrivateClass().a
Compile bytecode for base.privateFun()
Compile bytecode for derived.privateFun()
Compile bytecode for derivedAsBase.privateFun()
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,9 @@ LineBreakpoint created at protectedMember.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! protectedMember.ProtectedMemberPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
protectedMember.kt:4
Compile bytecode for MyClass().protectedFun()
Compile bytecode for MyClass().protectedVal
Compile bytecode for MyClass.ProtectedClass().a
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,13 @@ LineBreakpoint created at simple.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! simple.SimplePackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
simple.kt:4
Compile bytecode for 1
Compile bytecode for 1 + 1
Compile bytecode for val a = 1
a + args.size
// RESULT: 1: I
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,13 @@ LineBreakpoint created at stdlib.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! stdlib.StdlibPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stdlib.kt:4
Compile bytecode for array(100, 101)
Compile bytecode for array("a", "b", "c")
Compile bytecode for intArray(1, 2)
Compile bytecode for javaClass<String>()
Compile bytecode for javaClass<Int>()
Compile bytecode for 100.toInt()
Compile bytecode for 100.toLong()
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,6 +2,8 @@ LineBreakpoint created at vars.kt:7
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! vars.VarsPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
vars.kt:6
Compile bytecode for a
Compile bytecode for a += 1
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,185 @@
package clearCache
import java.util.ArrayList
import java.util.HashSet
fun primitiveTypes() {
if (true) {
val a: Any = 0
// EXPRESSION: a
// RESULT: instance of java.lang.Integer(id=ID): Ljava/lang/Integer;
//Breakpoint!
val b = 1
}
if (true) {
val a = 1
// EXPRESSION: a
// RESULT: 1: I
//Breakpoint!
val b = 1
}
for (i in 1..2) {
// EXPRESSION: i
// RESULT: 1: I
// EXPRESSION: i
// RESULT: 2: I
//Breakpoint!
val b = 1
}
for (i in 5.0..6.0) {
// EXPRESSION: i
// RESULT: 5.0: D
// EXPRESSION: i
// RESULT: 6.0: D
//Breakpoint!
val b = 1
}
}
fun subType() {
if (true) {
val o = Base()
// EXPRESSION: o.test()
// RESULT: 100: I
//Breakpoint!
val b = 1
}
if (true) {
val o = Derived()
// EXPRESSION: o.test()
// RESULT: 200: I
//Breakpoint!
val b = 1
}
}
open class Base {
open fun test() = 100
}
class Derived: Base() {
override fun test() = 200
}
fun subTypePlatform() {
if (true) {
val c: MutableList<String> = ArrayList<String>()
// EXPRESSION: c.size()
// RESULT: 0: I
//Breakpoint!
val b = 1
}
if (true) {
val c: List<String> = ArrayList<String>()
// EXPRESSION: c.size()
// RESULT: 0: I
//Breakpoint!
val b = 1
}
if (true) {
val c = ArrayList<String>()
c.add("a")
// EXPRESSION: c.size()
// RESULT: 1: I
//Breakpoint!
val b = 1
}
if (true) {
val c = ArrayList<Int>()
c.add(1)
c.add(2)
// EXPRESSION: c.size()
// RESULT: 2: I
//Breakpoint!
val b = 1
}
if (true) {
val c = HashSet<Int>()
// EXPRESSION: c.size()
// RESULT: 0: I
//Breakpoint!
val b = 1
}
}
fun innerClass() {
if (true) {
val o = TestInnerClasses.Base()
// EXPRESSION: o.test()
// RESULT: 100: I
//Breakpoint!
val b = 1
}
if (true) {
val o = TestInnerClasses.Derived()
// EXPRESSION: o.test()
// RESULT: 200: I
//Breakpoint!
val b = 1
}
}
class TestInnerClasses {
open class Base {
open fun test() = 100
}
class Derived: Base() {
override fun test() = 200
}
}
fun objects() {
// EXPRESSION: obj.test()
// RESULT: 1: I
//Breakpoint!
val a1 = 1
// EXPRESSION: obj.test()
// RESULT: 1: I
//Breakpoint!
val a2 = 1
if (true) {
val o: BaseObject = obj
// EXPRESSION: o.test()
// RESULT: 1: I
//Breakpoint!
val b = 1
}
if (true) {
val o = obj
// EXPRESSION: o.test()
// RESULT: 1: I
//Breakpoint!
val b = 1
}
}
val obj = object: BaseObject() { }
open class BaseObject {
fun test() = 1
}
fun main(args: Array<String>) {
primitiveTypes()
subType()
subTypePlatform()
innerClass()
objects()
}
@@ -0,0 +1,65 @@
package exceptions
import java.util.ArrayList
fun throwException() {
// EXPRESSION: fail()
// RESULT: instance of java.lang.UnsupportedOperationException(id=ID): Ljava/lang/UnsupportedOperationException;
//Breakpoint!
val a = 1
// EXPRESSION: fail()
// RESULT: instance of java.lang.UnsupportedOperationException(id=ID): Ljava/lang/UnsupportedOperationException;
//Breakpoint!
val b = 1
}
fun fail() {
throw UnsupportedOperationException()
}
fun classCast() {
val o = Base()
// EXPRESSION: o as Derived
// RESULT: java.lang.ClassCastException: exceptions.Base cannot be cast to exceptions.Derived: Ljava/lang/ClassCastException;
//Breakpoint!
val a = 1
// EXPRESSION: o as Derived
// RESULT: java.lang.ClassCastException: exceptions.Base cannot be cast to exceptions.Derived: Ljava/lang/ClassCastException;
//Breakpoint!
val b = 1
}
fun genericClassCast() {
if (true) {
val c = ArrayList<Int>()
c.add(1)
// EXPRESSION: c.get(0)
// RESULT: 1: I
//Breakpoint!
val b = 1
}
if (true) {
val c = ArrayList<String>()
c.add("a")
// EXPRESSION: c.get(0)
// RESULT: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number: Ljava/lang/ClassCastException;
//Breakpoint!
val b = 1
}
}
open class Base {
private fun test(): Int = 1
}
class Derived: Base()
fun main(args: Array<String>) {
throwException()
classCast()
genericClassCast()
}
@@ -1,24 +0,0 @@
package privateMember
fun main(args: Array<String>) {
//Breakpoint!
args.size
}
class MyClass {
private fun privateFun() = 1
private val privateVal = 1
private class PrivateClass {
val a = 1
}
}
// EXPRESSION: MyClass().privateFun()
// RESULT: 1: I
// EXPRESSION: MyClass().privateVal
// RESULT: 1: I
// EXPRESSION: MyClass.PrivateClass().a
// RESULT: 1: I
@@ -0,0 +1,11 @@
package doubles
fun main(args: Array<String>) {
val d1 = 1.0
val d2 = 3.0
//Breakpoint!
args.size
}
// EXPRESSION: d1 + d2
// RESULT: 4.0: D

Some files were not shown because too many files have changed in this diff Show More