diff --git a/idea/resources/META-INF/plugin.xml.201 b/idea/resources/META-INF/plugin.xml.201
index a57de1afc0e..b2811fc37a6 100644
--- a/idea/resources/META-INF/plugin.xml.201
+++ b/idea/resources/META-INF/plugin.xml.201
@@ -88,5 +88,6 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSignaturePresentationBuilder.kt.201 b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSignaturePresentationBuilder.kt.201
new file mode 100644
index 00000000000..46d2003fa70
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSignaturePresentationBuilder.kt.201
@@ -0,0 +1,77 @@
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport
+import com.intellij.refactoring.suggested.SignatureChangePresentationModel.Effect
+import com.intellij.refactoring.suggested.SignatureChangePresentationModel.TextFragment.Leaf
+import com.intellij.refactoring.suggested.SignaturePresentationBuilder
+
+internal class KotlinSignaturePresentationBuilder(
+ signature: SuggestedRefactoringSupport.Signature,
+ otherSignature: SuggestedRefactoringSupport.Signature,
+ isOldSignature: Boolean
+) : SignaturePresentationBuilder(signature, otherSignature, isOldSignature)
+{
+ override fun buildPresentation() {
+ val declarationType = (signature.additionalData as KotlinSignatureAdditionalData).declarationType
+ val keyword = declarationType.prefixKeyword
+ if (keyword != null) {
+ fragments += Leaf("$keyword ")
+ }
+
+ signature.receiverType?.let {
+ when (val effect = effect(it, otherSignature.receiverType)) {
+ Effect.Modified -> {
+ fragments += Leaf(it, effect)
+ fragments += Leaf(".")
+ }
+
+ else -> {
+ fragments += Leaf("$it.", effect)
+ }
+ }
+ }
+
+ val name = if (declarationType == DeclarationType.SECONDARY_CONSTRUCTOR) "constructor" else signature.name
+ fragments += Leaf(name, effect(signature.name, otherSignature.name))
+
+ if (declarationType.isFunction) {
+ buildParameterList { fragments, parameter, correspondingParameter ->
+ if (parameter.modifiers.isNotEmpty()) {
+ fragments += leaf(parameter.modifiers, correspondingParameter?.modifiers ?: parameter.modifiers)
+ fragments += Leaf(" ")
+ }
+
+ fragments += leaf(parameter.name, correspondingParameter?.name ?: parameter.name)
+
+ fragments += Leaf(": ")
+
+ fragments += leaf(parameter.type, correspondingParameter?.type ?: parameter.type)
+
+ val defaultValue = parameter.defaultValue
+ if (defaultValue != null) {
+ val defaultValueEffect = if (correspondingParameter != null)
+ effect(defaultValue, correspondingParameter.defaultValue)
+ else
+ Effect.None
+ fragments += Leaf(" = ", defaultValueEffect.takeIf { it != Effect.Modified } ?: Effect.None)
+ fragments += Leaf(defaultValue, defaultValueEffect)
+ }
+ }
+ } else {
+ require(signature.parameters.isEmpty())
+ }
+
+ signature.type?.let { type ->
+ when (val effect = effect(type, otherSignature.type)) {
+ Effect.Added, Effect.Removed, Effect.None -> {
+ fragments += Leaf(": ${signature.type}", effect)
+ }
+
+ Effect.Modified -> {
+ fragments += Leaf(": ")
+ fragments += Leaf(type, effect)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringAvailability.kt.201 b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringAvailability.kt.201
new file mode 100644
index 00000000000..33839c73535
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringAvailability.kt.201
@@ -0,0 +1,161 @@
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.psi.PsiNamedElement
+import com.intellij.refactoring.suggested.*
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Parameter
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature
+import org.jetbrains.kotlin.descriptors.CallableDescriptor
+import org.jetbrains.kotlin.idea.caches.project.forcedModuleInfo
+import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
+import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtCallableDeclaration
+import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
+import org.jetbrains.kotlin.psi.psiUtil.hasBody
+import org.jetbrains.kotlin.renderer.DescriptorRenderer
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.isError
+
+class KotlinSuggestedRefactoringAvailability(refactoringSupport: SuggestedRefactoringSupport) :
+ SuggestedRefactoringAvailability(refactoringSupport)
+{
+ override fun refineSignaturesWithResolve(state: SuggestedRefactoringState): SuggestedRefactoringState {
+ val newDeclaration = state.declaration as? KtCallableDeclaration ?: return state
+ val oldDeclaration = state.createRestoredDeclarationCopy(refactoringSupport) as KtCallableDeclaration
+ oldDeclaration.containingKtFile.forcedModuleInfo = newDeclaration.getModuleInfo()
+
+ val descriptorWithOldSignature = oldDeclaration.resolveToDescriptorIfAny() as CallableDescriptor
+ val descriptorWithNewSignature = newDeclaration.resolveToDescriptorIfAny() as CallableDescriptor
+
+ val oldSignature = state.oldSignature
+ val newSignature = state.newSignature
+
+ val (oldReturnType, newReturnType) = refineType(
+ oldSignature.type,
+ newSignature.type,
+ descriptorWithOldSignature.returnType,
+ descriptorWithNewSignature.returnType
+ )
+
+ val improvedOldParameterTypesById = mutableMapOf()
+ val improvedNewParameterTypesById = mutableMapOf()
+ for (oldParameter in oldSignature.parameters) {
+ val id = oldParameter.id
+ val newParameter = newSignature.parameterById(id) ?: continue
+ val oldIndex = oldSignature.parameterIndex(oldParameter)
+ val newIndex = newSignature.parameterIndex(newParameter)
+ val (oldType, newType) = refineType(
+ oldParameter.type,
+ newParameter.type,
+ descriptorWithOldSignature.valueParameters[oldIndex].type,
+ descriptorWithNewSignature.valueParameters[newIndex].type
+ )
+ improvedOldParameterTypesById[id] = oldType!!
+ improvedNewParameterTypesById[id] = newType!!
+ }
+ val oldParameters = oldSignature.parameters.map { it.copy(type = improvedOldParameterTypesById[it.id] ?: it.type) }
+ val newParameters = newSignature.parameters.map { it.copy(type = improvedNewParameterTypesById[it.id] ?: it.type) }
+
+ val oldAdditionalData = oldSignature.additionalData as KotlinSignatureAdditionalData?
+ val newAdditionalData = newSignature.additionalData as KotlinSignatureAdditionalData?
+ val (oldReceiverType, newReceiverType) = refineType(
+ oldAdditionalData?.receiverType,
+ newAdditionalData?.receiverType,
+ descriptorWithOldSignature.extensionReceiverParameter?.type,
+ descriptorWithNewSignature.extensionReceiverParameter?.type
+ )
+
+ return state.copy(
+ oldSignature = Signature.create(
+ oldSignature.name,
+ oldReturnType,
+ oldParameters,
+ oldAdditionalData?.copy(receiverType = oldReceiverType)
+ )!!,
+ newSignature = Signature.create(
+ newSignature.name,
+ newReturnType,
+ newParameters,
+ newAdditionalData?.copy(receiverType = newReceiverType)
+ )!!
+ )
+ }
+
+ private fun refineType(
+ oldTypeInCode: String?,
+ newTypeInCode: String?,
+ oldTypeResolved: KotlinType?,
+ newTypeResolved: KotlinType?
+ ): Pair {
+ if (oldTypeResolved?.isError == true || newTypeResolved?.isError == true) {
+ return oldTypeInCode to newTypeInCode
+ }
+
+ val oldTypeFQ = oldTypeResolved?.fqText()
+ val newTypeFQ = newTypeResolved?.fqText()
+
+ if (oldTypeInCode != newTypeInCode) {
+ if (oldTypeFQ == newTypeFQ) {
+ return newTypeInCode to newTypeInCode
+ }
+ } else {
+ if (oldTypeFQ != newTypeFQ) {
+ return oldTypeFQ to newTypeFQ
+ }
+ }
+
+ return oldTypeInCode to newTypeInCode
+ }
+
+ private fun KotlinType.fqText() = DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(this)
+
+ override fun detectAvailableRefactoring(state: SuggestedRefactoringState): SuggestedRefactoringData? {
+ val declaration = state.declaration
+ if (declaration !is KtCallableDeclaration || KotlinSuggestedRefactoringSupport.isOnlyRenameSupported(declaration)) {
+ return SuggestedRenameData(declaration as PsiNamedElement, state.oldSignature.name)
+ }
+
+ val oldSignature = state.oldSignature
+ val newSignature = state.newSignature
+ val updateUsagesData = SuggestedChangeSignatureData.create(state, USAGES)
+
+ if (hasParameterAddedRemovedOrReordered(oldSignature, newSignature)) return updateUsagesData
+
+ // for non-virtual function we can add or remove receiver for usages but not change its type
+ if ((oldSignature.receiverType == null) != (newSignature.receiverType == null)) return updateUsagesData
+
+ val (nameChanges, renameData) = nameChanges(oldSignature, newSignature, declaration, declaration.valueParameters)
+
+ if (hasTypeChanges(oldSignature, newSignature)) {
+ return if (nameChanges > 0)
+ updateUsagesData
+ else
+ declaration.overridesName()?.let { updateUsagesData.copy(nameOfStuffToUpdate = it) }
+ }
+
+ return when {
+ renameData != null -> renameData
+ nameChanges > 0 -> updateUsagesData
+ else -> null
+ }
+ }
+
+ private fun KtCallableDeclaration.overridesName(): String? {
+ return when {
+ hasModifier(KtTokens.ABSTRACT_KEYWORD) -> IMPLEMENTATIONS
+ hasModifier(KtTokens.OPEN_KEYWORD) -> OVERRIDES
+ containingClassOrObject?.isInterfaceClass() == true -> if (hasBody()) OVERRIDES else IMPLEMENTATIONS
+ hasModifier(KtTokens.EXPECT_KEYWORD) -> "actual declarations"
+ else -> null
+ }
+ }
+
+ override fun hasTypeChanges(oldSignature: Signature, newSignature: Signature): Boolean {
+ return super.hasTypeChanges(oldSignature, newSignature) || oldSignature.receiverType != newSignature.receiverType
+ }
+
+ override fun hasParameterTypeChanges(oldParam: Parameter, newParam: Parameter): Boolean {
+ return super.hasParameterTypeChanges(oldParam, newParam) || oldParam.modifiers != newParam.modifiers
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringExecution.kt.201 b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringExecution.kt.201
new file mode 100644
index 00000000000..8bd21c848fe
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringExecution.kt.201
@@ -0,0 +1,157 @@
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.psi.PsiElement
+import com.intellij.refactoring.suggested.SuggestedChangeSignatureData
+import com.intellij.refactoring.suggested.SuggestedRefactoringExecution
+import org.jetbrains.kotlin.descriptors.CallableDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
+import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
+import org.jetbrains.kotlin.psi.KtCallableDeclaration
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import org.jetbrains.kotlin.types.isError
+import org.jetbrains.kotlin.utils.addIfNotNull
+
+class KotlinSuggestedRefactoringExecution(
+ refactoringSupport: KotlinSuggestedRefactoringSupport
+) : SuggestedRefactoringExecution(refactoringSupport) {
+
+ private data class PrepareChangeSignatureResult(
+ val returnTypeInfo: KotlinTypeInfo,
+ val parameterTypeInfos: List
+ )
+
+ override fun prepareChangeSignature(data: SuggestedChangeSignatureData): Any {
+ val declaration = data.declaration as KtCallableDeclaration
+ val descriptorWithNewSignature = declaration.resolveToDescriptorIfAny() as CallableDescriptor
+ val newSignature = data.newSignature
+
+ val returnType = descriptorWithNewSignature.returnType
+ val returnTypeInfo = if (returnType != null && !returnType.isError) {
+ KotlinTypeInfo(type = returnType, isCovariant = true)
+ } else {
+ KotlinTypeInfo(text = newSignature.type, isCovariant = true)
+ }
+
+ require(descriptorWithNewSignature.valueParameters.size == newSignature.parameters.size)
+ val parameterTypeInfos = descriptorWithNewSignature.valueParameters.zip(newSignature.parameters)
+ .map { (parameterDescriptor, parameterData) ->
+ val type = parameterDescriptor.varargElementType ?: parameterDescriptor.type
+ if (!type.isError) {
+ KotlinTypeInfo(type = type, isCovariant = true)
+ } else {
+ KotlinTypeInfo(text = parameterData.type, isCovariant = true)
+ }
+ }
+
+ return PrepareChangeSignatureResult(returnTypeInfo, parameterTypeInfos)
+ }
+
+ override fun performChangeSignature(
+ data: SuggestedChangeSignatureData,
+ newParameterValues: List,
+ preparedData: Any?
+ ) {
+ val (returnTypeInfo, parameterTypeInfos) = preparedData as PrepareChangeSignatureResult
+
+ val declaration = data.declaration as KtDeclaration
+ val descriptor = declaration.resolveToDescriptorIfAny() as CallableDescriptor
+ val project = declaration.project
+
+ val configuration = object : KotlinChangeSignatureConfiguration {
+ override fun performSilently(affectedFunctions: Collection) = true
+ }
+ val changeSignature = KotlinChangeSignature(project, descriptor, configuration, declaration, null)
+ val methodDescriptor = changeSignature.adjustDescriptor(listOf(descriptor))!!
+
+ val parameters = mutableListOf()
+ var newParameterValueIndex = 0
+
+ val receiver: KotlinParameterInfo? = if (data.newSignature.receiverType != null) {
+ val newTypeInfo = KotlinTypeInfo(text = data.newSignature.receiverType, isCovariant = false)
+ if (data.oldSignature.receiverType != null) {
+ methodDescriptor.receiver!!.apply { currentTypeInfo = newTypeInfo }
+ } else {
+ KotlinParameterInfo(descriptor, -1, "", newTypeInfo)
+ .withDefaultValue(newParameterValues[newParameterValueIndex++])
+ }
+ } else {
+ null
+ }
+
+ parameters.addIfNotNull(receiver)
+
+ val psiFactory = KtPsiFactory(project)
+
+ for ((index, parameter) in data.newSignature.parameters.withIndex()) {
+ val initialIndex = data.oldSignature.parameterById(parameter.id)
+ ?.let { data.oldSignature.parameterIndex(it) }
+
+ val defaultValue = parameter.defaultValue?.let { psiFactory.createExpression(it) }
+
+ val modifierList = parameter.modifiers
+ .takeIf { it.isNotEmpty() }
+ ?.let { psiFactory.createModifierList(it) }
+
+ val parameterInfo = if (initialIndex == null) {
+ KotlinParameterInfo(
+ descriptor,
+ -1,
+ parameter.name,
+ parameterTypeInfos[index],
+ defaultValueForParameter = defaultValue,
+ modifierList = modifierList
+ ).withDefaultValue(newParameterValues[newParameterValueIndex++])
+ } else {
+ KotlinParameterInfo(
+ descriptor,
+ initialIndex,
+ parameter.name,
+ methodDescriptor.parameters[initialIndex].originalTypeInfo,
+ defaultValueForParameter = defaultValue,
+ modifierList = modifierList
+ ).apply {
+ currentTypeInfo = parameterTypeInfos[index]
+ }
+ }
+ parameters.add(parameterInfo)
+ }
+
+ val changeInfo = KotlinChangeInfo(
+ methodDescriptor,
+ context = declaration,
+ name = data.newSignature.name,
+ newReturnTypeInfo = returnTypeInfo,
+ parameterInfos = parameters,
+ receiver = receiver
+ )
+ val processor = KotlinChangeSignatureProcessor(project, changeInfo, "")
+ processor.run()
+ }
+
+ //TODO: we can't just set defaultValueForCall due to bug in KotlinParameterInfo
+ private fun KotlinParameterInfo.withDefaultValue(value: NewParameterValue): KotlinParameterInfo {
+ when (value) {
+ is NewParameterValue.AnyVariable -> {
+ isUseAnySingleVariable = true
+ return this
+ }
+
+ is NewParameterValue.Expression -> {
+ val defaultValueForCall = value.expression as KtExpression
+ return KotlinParameterInfo(
+ callableDescriptor, originalIndex, name, originalTypeInfo,
+ defaultValueForParameter, defaultValueForCall, valOrVar, modifierList
+ ).apply {
+ currentTypeInfo = this@withDefaultValue.currentTypeInfo
+ }
+ }
+
+ is NewParameterValue.None -> {
+ defaultValueForCall = null
+ return this
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringStateChanges.kt.201 b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringStateChanges.kt.201
new file mode 100644
index 00000000000..f68d8a8427a
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringStateChanges.kt.201
@@ -0,0 +1,66 @@
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import com.intellij.refactoring.suggested.SuggestedRefactoringState
+import com.intellij.refactoring.suggested.SuggestedRefactoringStateChanges
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Parameter
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature
+import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtCallableDeclaration
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtFunction
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.psiUtil.children
+
+class KotlinSuggestedRefactoringStateChanges(refactoringSupport: SuggestedRefactoringSupport) :
+ SuggestedRefactoringStateChanges(refactoringSupport)
+{
+ override fun createInitialState(declaration: PsiElement): SuggestedRefactoringState? {
+ declaration as KtDeclaration
+ if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null // currently not supported
+ if (declaration.hasModifier(KtTokens.ACTUAL_KEYWORD)) return null // currently not supported
+ return super.createInitialState(declaration)
+ }
+
+ override fun signature(declaration: PsiElement, prevState: SuggestedRefactoringState?): Signature? {
+ declaration as KtDeclaration
+ val name = declaration.name ?: return null
+ if (declaration !is KtCallableDeclaration || KotlinSuggestedRefactoringSupport.isOnlyRenameSupported(declaration)) {
+ return Signature.create(name, null, emptyList(), null)
+ }
+
+ val parameters = declaration.valueParameters.map { it.extractParameterData() ?: return null }
+ val type = declaration.typeReference?.text
+ val receiverType = declaration.receiverTypeReference?.text
+ val signature = Signature.create(
+ name,
+ type,
+ parameters,
+ KotlinSignatureAdditionalData(DeclarationType.fromDeclaration(declaration), receiverType)
+ ) ?: return null
+
+ return if (prevState == null) signature else matchParametersWithPrevState(signature, declaration, prevState)
+ }
+
+ override fun parameterMarkerRanges(declaration: PsiElement): List {
+ if (declaration !is KtFunction) return emptyList()
+ return declaration.valueParameters.map { it.colon?.textRange }
+ }
+
+ private fun KtParameter.extractParameterData(): Parameter? {
+ val modifiers = modifierList?.node?.children()
+ ?.filter { it.elementType is KtModifierKeywordToken && it.text in modifiersToInclude }
+ ?.joinToString(separator = " ") { it.text } ?: ""
+ return Parameter(
+ Any(),
+ name ?: return null,
+ typeReference?.text ?: return null,
+ KotlinParameterAdditionalData(defaultValue?.text/*TODO: strip comments etc*/, modifiers)
+ )
+ }
+
+ private val modifiersToInclude = setOf(KtTokens.VARARG_KEYWORD.value)
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringSupport.kt.201 b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringSupport.kt.201
new file mode 100644
index 00000000000..94420d9898e
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringSupport.kt.201
@@ -0,0 +1,153 @@
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Parameter
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
+
+class KotlinSuggestedRefactoringSupport : SuggestedRefactoringSupport {
+ override fun isDeclaration(psiElement: PsiElement): Boolean {
+ if (psiElement !is KtDeclaration) return false
+ if (psiElement is KtParameter && psiElement.ownerFunction != null) return false
+ return true
+ }
+
+ override fun signatureRange(declaration: PsiElement): TextRange? {
+ when (declaration) {
+ is KtPrimaryConstructor -> return declaration.textRange
+
+ is KtSecondaryConstructor -> return declaration.valueParameterList?.textRange
+
+ is KtCallableDeclaration -> {
+ if (isOnlyRenameSupported(declaration)) {
+ return declaration.nameIdentifier?.textRange
+ }
+
+ val start = declaration.receiverTypeReference?.textRange?.startOffset
+ ?: declaration.nameIdentifier?.textRange?.startOffset
+ ?: return null
+ val end = (declaration.typeReference ?: declaration.valueParameterList ?: declaration.nameIdentifier)
+ ?.textRange?.endOffset
+ ?: return null
+ return TextRange(start, end)
+ }
+
+ is KtNamedDeclaration -> return declaration.nameIdentifier?.textRange
+
+ else -> return null
+ }
+ }
+
+ override fun importsRange(psiFile: PsiFile): TextRange? {
+ return (psiFile as KtFile).importList?.textRange
+ }
+
+ override fun nameRange(declaration: PsiElement): TextRange? {
+ val identifier = when (declaration) {
+ is KtPrimaryConstructor -> declaration.containingClassOrObject?.nameIdentifier
+ is KtSecondaryConstructor -> declaration.getConstructorKeyword()
+ is KtNamedDeclaration -> declaration.nameIdentifier
+ else -> null
+ }
+ return identifier?.textRange
+ }
+
+ override fun hasSyntaxError(declaration: PsiElement): Boolean {
+ if (super.hasSyntaxError(declaration)) return true
+
+ // do not suggest renaming of local variable which has neither type nor initializer
+ // it's important because such variable declarations may appear on typing "val name = " before an expression
+ if (declaration is KtProperty && declaration.isLocal) {
+ if (declaration.typeReference == null && declaration.initializer == null) return true
+ }
+
+ return false
+ }
+
+ override fun isIdentifierStart(c: Char) = c.isJavaIdentifierStart()
+ override fun isIdentifierPart(c: Char) = c.isJavaIdentifierPart()
+
+ override val stateChanges = KotlinSuggestedRefactoringStateChanges(this)
+ override val availability = KotlinSuggestedRefactoringAvailability(this)
+ override val ui get() = KotlinSuggestedRefactoringUI
+ override val execution = KotlinSuggestedRefactoringExecution(this)
+
+ companion object {
+ fun isOnlyRenameSupported(declaration: KtCallableDeclaration): Boolean {
+ // for local variable - only rename
+ return declaration is KtVariableDeclaration && KtPsiUtil.isLocal(declaration)
+ }
+ }
+}
+
+enum class DeclarationType {
+ FUN {
+ override val prefixKeyword get() = "fun"
+ override val isFunction get() = true
+ },
+ VAL {
+ override val prefixKeyword get() = "val"
+ override val isFunction get() = false
+ },
+ VAR {
+ override val prefixKeyword get() = "var"
+ override val isFunction get() = false
+ },
+ PRIMARY_CONSTRUCTOR {
+ override val prefixKeyword: String?
+ get() = null
+ override val isFunction get() = true
+ },
+ SECONDARY_CONSTRUCTOR {
+ override val prefixKeyword: String?
+ get() = null
+ override val isFunction get() = true
+ },
+ OTHER {
+ override val prefixKeyword: String?
+ get() = null
+ override val isFunction get() = false
+ }
+
+ ;
+
+ abstract val prefixKeyword: String?
+ abstract val isFunction: Boolean
+
+ companion object {
+ fun fromDeclaration(declaration: KtDeclaration): DeclarationType = when (declaration) {
+ is KtPrimaryConstructor -> PRIMARY_CONSTRUCTOR
+
+ is KtSecondaryConstructor -> SECONDARY_CONSTRUCTOR
+
+ is KtNamedFunction -> FUN
+
+ is KtProperty -> if (declaration.isVar) VAR else VAL
+
+ else -> OTHER
+ }
+ }
+}
+
+internal data class KotlinSignatureAdditionalData(
+ val declarationType: DeclarationType,
+ val receiverType: String?
+) : SuggestedRefactoringSupport.SignatureAdditionalData
+
+internal data class KotlinParameterAdditionalData(
+ val defaultValue: String?,
+ val modifiers: String
+) : SuggestedRefactoringSupport.ParameterAdditionalData
+
+internal val Signature.receiverType: String?
+ get() = (additionalData as KotlinSignatureAdditionalData?)?.receiverType
+
+internal val Parameter.defaultValue: String?
+ get() = (additionalData as KotlinParameterAdditionalData?)?.defaultValue
+
+internal val Parameter.modifiers: String
+ get() = (additionalData as KotlinParameterAdditionalData?)?.modifiers ?: ""
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringUI.kt.201 b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringUI.kt.201
new file mode 100644
index 00000000000..5c736f3efca
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringUI.kt.201
@@ -0,0 +1,46 @@
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.psi.PsiCodeFragment
+import com.intellij.refactoring.suggested.SuggestedChangeSignatureData
+import com.intellij.refactoring.suggested.SuggestedRefactoringExecution.NewParameterValue
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature
+import com.intellij.refactoring.suggested.SuggestedRefactoringUI
+import com.intellij.refactoring.suggested.SignaturePresentationBuilder
+import org.jetbrains.kotlin.psi.KtExpressionCodeFragment
+import org.jetbrains.kotlin.psi.KtPsiFactory
+
+object KotlinSuggestedRefactoringUI : SuggestedRefactoringUI() {
+ override fun createSignaturePresentationBuilder(
+ signature: Signature,
+ otherSignature: Signature,
+ isOldSignature: Boolean
+ ): SignaturePresentationBuilder {
+ return KotlinSignaturePresentationBuilder(signature, otherSignature, isOldSignature)
+ }
+
+ override fun extractNewParameterData(data: SuggestedChangeSignatureData): List {
+ val newParameters = mutableListOf()
+
+ val declaration = data.declaration
+ val factory = KtPsiFactory(declaration.project)
+
+ fun createCodeFragment() = factory.createExpressionCodeFragment("", declaration)
+
+ if (data.newSignature.receiverType != null && data.oldSignature.receiverType == null) {
+ newParameters.add(NewParameterData("", createCodeFragment(), false/*TODO*/))
+ }
+
+ for (parameter in data.newSignature.parameters) {
+ if (data.oldSignature.parameterById(parameter.id) == null) {
+ newParameters.add(NewParameterData(parameter.name, createCodeFragment(), false/*TODO*/))
+ }
+ }
+
+ return newParameters
+ }
+
+ override fun extractValue(fragment: PsiCodeFragment): NewParameterValue.Expression? {
+ return (fragment as KtExpressionCodeFragment).getContentElement()
+ ?.let { NewParameterValue.Expression(it) }
+ }
+}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSignatureChangePresentationTest.kt.201 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSignatureChangePresentationTest.kt.201
new file mode 100644
index 00000000000..a7c571591da
--- /dev/null
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSignatureChangePresentationTest.kt.201
@@ -0,0 +1,874 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Parameter
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature
+import com.intellij.refactoring.suggested.BaseSignatureChangePresentationTest
+
+class KotlinSignatureChangePresentationTest : BaseSignatureChangePresentationTest() {
+ override val refactoringSupport = KotlinSuggestedRefactoringSupport()
+
+ private fun signature(
+ name: String,
+ type: String?,
+ parameters: List,
+ receiverType: String? = null
+ ): Signature? {
+ return Signature.create(
+ name,
+ type,
+ parameters,
+ KotlinSignatureAdditionalData(DeclarationType.FUN, receiverType)
+ )
+ }
+
+ fun testAddParameters() {
+ val oldSignature = signature(
+ "foo",
+ "String",
+ listOf(Parameter(0, "p1", "Int")),
+ "Any"
+ )!!
+ val newSignature = signature(
+ "foo",
+ "String",
+ listOf(
+ Parameter(Any(), "p0", "Any"),
+ Parameter(0, "p1", "Int"),
+ Parameter(Any(), "p2", "Long")
+ ),
+ "Any"
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'Any.'
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ ': String'
+ New:
+ 'fun '
+ 'Any.'
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group (added):
+ 'p0'
+ ': '
+ 'Any'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p1'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group (added):
+ 'p2'
+ ': '
+ 'Long'
+ LineBreak('', false)
+ ')'
+ ': String'
+ """.trimIndent()
+ )
+ }
+
+ fun testSwapParameters() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int"),
+ Parameter(1, "p2", "Long")
+ )
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(1, "p2", "Long"),
+ Parameter(0, "p1", "Int")
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group (moved):
+ 'p1'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group (moved):
+ 'p1'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testMoveParameter() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int"),
+ Parameter(1, "p2", "Long"),
+ Parameter(2, "p3", "Any")
+ )
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(1, "p2", "Long"),
+ Parameter(2, "p3", "Any"),
+ Parameter(0, "p1", "Int")
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group (moved):
+ 'p1'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Any'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Any'
+ ','
+ LineBreak(' ', true)
+ Group (moved):
+ 'p1'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testReorderParameters() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int"),
+ Parameter(1, "p2", "Long"),
+ Parameter(2, "p3", "Any"),
+ Parameter(3, "p4", "Any")
+ )
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(1, "p2", "Long"),
+ Parameter(3, "p4", "Any"),
+ Parameter(2, "p3", "Any"),
+ Parameter(0, "p1", "Int")
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group (id = 0, moved):
+ 'p1'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group (id = 2, moved):
+ 'p3'
+ ': '
+ 'Any'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p4'
+ ': '
+ 'Any'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p4'
+ ': '
+ 'Any'
+ ','
+ LineBreak(' ', true)
+ Group (id = 2, moved):
+ 'p3'
+ ': '
+ 'Any'
+ ','
+ LineBreak(' ', true)
+ Group (id = 0, moved):
+ 'p1'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testChangeFunctionName() {
+ val oldSignature = signature("foo", null, emptyList())!!
+ val newSignature = signature("bar", null, emptyList())!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo' (modified)
+ '('
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'bar' (modified)
+ '('
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testChangeReturnType() {
+ val oldSignature = signature("foo", "Any", emptyList())!!
+ val newSignature = signature("foo", "String", emptyList())!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ ': '
+ 'Any' (modified)
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ ': '
+ 'String' (modified)
+ """.trimIndent()
+ )
+ }
+
+ fun testAddReturnType() {
+ val oldSignature = signature("foo", null, emptyList())!!
+ val newSignature = signature("foo", "String", emptyList())!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ ': String' (added)
+ """.trimIndent()
+ )
+ }
+
+ fun testRemoveReturnType() {
+ val oldSignature = signature("foo", "Any", emptyList())!!
+ val newSignature = signature("foo", null, emptyList())!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ ': Any' (removed)
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testChangeParameterName() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int"),
+ Parameter(1, "p2", "Long")
+ )
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1New", "Int"),
+ Parameter(1, "p2", "Long")
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1' (modified)
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1New' (modified)
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ LineBreak('', false)
+ ')'
+""".trimIndent()
+ )
+ }
+
+ fun testChangeTwoParameterNames() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int"),
+ Parameter(1, "p2", "Long")
+ )
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1New", "Int"),
+ Parameter(1, "p2New", "Long")
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1' (modified)
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2' (modified)
+ ': '
+ 'Long'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1New' (modified)
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2New' (modified)
+ ': '
+ 'Long'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testMoveAndRenameParameter() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int"),
+ Parameter(1, "p2", "Long"),
+ Parameter(2, "p3", "Any")
+ )
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(1, "p2", "Long"),
+ Parameter(2, "p3", "Any"),
+ Parameter(0, "p1New", "Int")
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group (moved):
+ 'p1' (modified)
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Any'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Any'
+ ','
+ LineBreak(' ', true)
+ Group (moved):
+ 'p1New' (modified)
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testMoveParameterAndChangeFunctionName() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int"),
+ Parameter(1, "p2", "Long"),
+ Parameter(2, "p3", "Any")
+ )
+ )!!
+ val newSignature = signature(
+ "fooNew",
+ null,
+ listOf(
+ Parameter(1, "p2", "Long"),
+ Parameter(2, "p3", "Any"),
+ Parameter(0, "p1", "Int")
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo' (modified)
+ '('
+ LineBreak('', true)
+ Group (id = 0, moved):
+ 'p1'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Any'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'fooNew' (modified)
+ '('
+ LineBreak('', true)
+ Group:
+ 'p2'
+ ': '
+ 'Long'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Any'
+ ','
+ LineBreak(' ', true)
+ Group (id = 0, moved):
+ 'p1'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testAddReceiver() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ emptyList()
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ emptyList(),
+ "Any"
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'Any.' (added)
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testRemoveReceiver() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ emptyList(),
+ "Any"
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ emptyList()
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'Any.' (removed)
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testChangeReceiverType() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ emptyList(),
+ "Any"
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ emptyList(),
+ "String"
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'Any' (modified)
+ '.'
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'String' (modified)
+ '.'
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testChangeDefaultValues() {
+ val oldSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int", KotlinParameterAdditionalData("1", "")),
+ Parameter(1, "p2", "Int", KotlinParameterAdditionalData("2", "")),
+ Parameter(2, "p3", "Int", KotlinParameterAdditionalData("3", "")),
+ Parameter(3, "p4", "Int")
+ )
+ )!!
+ val newSignature = signature(
+ "foo",
+ null,
+ listOf(
+ Parameter(0, "p1", "Int", KotlinParameterAdditionalData("1", "")),
+ Parameter(1, "p2", "Int", KotlinParameterAdditionalData("22", "")),
+ Parameter(2, "p3", "Int"),
+ Parameter(3, "p4", "Int", KotlinParameterAdditionalData("4", ""))
+ )
+ )!!
+ doTest(
+ oldSignature,
+ newSignature,
+ """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1'
+ ': '
+ 'Int'
+ ' = '
+ '1'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Int'
+ ' = '
+ '2' (modified)
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Int'
+ ' = ' (removed)
+ '3' (removed)
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p4'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1'
+ ': '
+ 'Int'
+ ' = '
+ '1'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Int'
+ ' = '
+ '22' (modified)
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p3'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p4'
+ ': '
+ 'Int'
+ ' = ' (added)
+ '4' (added)
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringAvailabilityTest.kt.201 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringAvailabilityTest.kt.201
new file mode 100644
index 00000000000..2ee42f89742
--- /dev/null
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringAvailabilityTest.kt.201
@@ -0,0 +1,371 @@
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.openapi.fileTypes.LanguageFileType
+import com.intellij.refactoring.suggested.BaseSuggestedRefactoringAvailabilityTest
+import org.jetbrains.kotlin.idea.KotlinFileType
+import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import org.jetbrains.kotlin.resolve.ImportPath
+
+class KotlinSuggestedRefactoringAvailabilityTest : BaseSuggestedRefactoringAvailabilityTest() {
+ override val fileType: LanguageFileType
+ get() = KotlinFileType.INSTANCE
+
+ override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
+
+ fun testNotAvailableWithSyntaxError() {
+ doTest(
+ """
+ fun foo(p1: Int) {
+ foo(1)
+ }
+
+ fun bar() {
+ foo(2)
+ }
+ """.trimIndent(),
+ {
+ myFixture.type(", p2: Any")
+ },
+ {
+ myFixture.type(", p")
+ },
+ expectedAvailability = Availability.Disabled
+ )
+ }
+
+ fun testInsertTrailingComma() {
+ doTest(
+ """
+ fun foo(p1: Int) {
+ foo(1)
+ }
+
+ fun bar() {
+ foo(2)
+ }
+ """.trimIndent(),
+ {
+ myFixture.type(",")
+ },
+ expectedAvailability = Availability.NotAvailable
+ )
+ }
+
+ fun testChangeNonVirtualPropertyType() {
+ doTest(
+ "val v: String = \"\"",
+ {
+ replaceTextAtCaret("String", "Any")
+ },
+ expectedAvailability = Availability.Disabled
+ )
+ }
+
+ fun testChangeParameterTypeNonVirtual() {
+ doTest(
+ "fun foo(p: String) {}",
+ {
+ replaceTextAtCaret("String", "Any")
+ },
+ expectedAvailability = Availability.Disabled
+ )
+ }
+
+ fun testChangeReturnTypeNonVirtual() {
+ doTest(
+ "fun foo(): String = \"\"",
+ {
+ replaceTextAtCaret("String", "Any")
+ },
+ expectedAvailability = Availability.Disabled
+ )
+ }
+
+ fun testChangeLocalVariableType() {
+ doTest(
+ """
+ fun foo() {
+ val local: Int
+ local = 10
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Int", "Long")
+ },
+ expectedAvailability = Availability.NotAvailable
+ )
+ }
+
+ fun testAddLocalVariableType() {
+ doTest(
+ """
+ fun foo() {
+ var local = 10
+ }
+ """.trimIndent(),
+ {
+ myFixture.type(": Long")
+ },
+ expectedAvailability = Availability.NotAvailable
+ )
+ }
+
+ fun testTypeLocalVariableBeforeExpression() {
+ doTest(
+ """
+ """.trimIndent(),
+ {
+ myFixture.type("val ")
+ },
+ {
+ myFixture.type("cod")
+ },
+ {
+ myFixture.type("e = ")
+ },
+ expectedAvailability = Availability.NotAvailable
+ )
+ }
+
+ fun testChangeParameterTypeAndName() {
+ doTest(
+ """
+ interface I {
+ fun foo(p: Int)
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Int", "String")
+ editor.caretModel.moveToOffset(editor.caretModel.offset - "p: ".length)
+ replaceTextAtCaret("p", "pNew")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "usages"))
+ )
+ }
+
+ fun testRenameTwoParameters() {
+ doTest(
+ """
+ interface I {
+ fun foo(p1: Int, p2: Int)
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("p1", "p1New")
+ },
+ {
+ editor.caretModel.moveToOffset(editor.caretModel.offset + "p1New: Int, ".length)
+ replaceTextAtCaret("p2", "p2New")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "usages"))
+ )
+ }
+
+ fun testChangeParameterType() {
+ doTest(
+ """
+ class C {
+ open fun foo(p1: Int, p2: Int) {
+ }
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Int", "Any")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "overrides"))
+ )
+ }
+
+ fun testChangeParameterTypeForAbstract() {
+ doTest(
+ """
+ abstract class C {
+ abstract fun foo(p1: Int, p2: Int)
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Int", "Any")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "implementations"))
+ )
+ }
+
+ fun testChangeParameterTypeInInterface() {
+ doTest(
+ """
+ interface I {
+ fun foo(p1: Int, p2: Int)
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Int", "Any")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "implementations"))
+ )
+ }
+
+ fun testChangeParameterTypeInInterfaceWithBody() {
+ doTest(
+ """
+ interface I {
+ fun foo(p1: Int, p2: Int) {}
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Int", "Any")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "overrides"))
+ )
+ }
+
+ fun testChangeTypeOfPropertyWithImplementationInInterface() {
+ doTest(
+ """
+ interface I {
+ val p: Int
+ get() = 0
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Int", "Any")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("p", "overrides"))
+ )
+ }
+
+ fun testSpecifyExplicitType() {
+ doTest(
+ """
+ open class C {
+ open fun foo() = 1
+ }
+ """.trimIndent(),
+ {
+ myFixture.type(": Int")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "overrides")),
+ expectedAvailabilityAfterResolve = Availability.NotAvailable
+ )
+ }
+
+ fun testRemoveExplicitType() {
+ doTest(
+ """
+ open class C {
+ open fun foo(): Int = 1
+ }
+ """.trimIndent(),
+ {
+ deleteTextBeforeCaret(": Int")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "overrides")),
+ expectedAvailabilityAfterResolve = Availability.NotAvailable
+ )
+ }
+
+ fun testImportNestedClass() {
+ doTest(
+ """
+ package ppp
+
+ class C {
+ class Nested
+ }
+
+ interface I {
+ fun foo(): C.Nested
+ }
+ """.trimIndent(),
+ {
+ addImport("ppp.C.Nested")
+ },
+ {
+ replaceTextAtCaret("C.Nested", "Nested")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "implementations")),
+ expectedAvailabilityAfterResolve = Availability.NotAvailable
+ )
+ }
+
+ fun testImportNestedClassForReceiverType() {
+ doTest(
+ """
+ package ppp
+
+ class C {
+ class Nested
+ }
+
+ interface I {
+ fun C.Nested.foo()
+ }
+ """.trimIndent(),
+ {
+ addImport("ppp.C.Nested")
+ },
+ {
+ replaceTextAtCaret("C.Nested", "Nested")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "implementations")),
+ expectedAvailabilityAfterResolve = Availability.NotAvailable
+ )
+ }
+
+ fun testImportNestedClassForParameterType() {
+ doTest(
+ """
+ package ppp
+
+ class C {
+ class Nested
+ }
+
+ interface I {
+ fun foo(p: C.Nested)
+ }
+ """.trimIndent(),
+ {
+ addImport("ppp.C.Nested")
+ },
+ {
+ replaceTextAtCaret("C.Nested", "Nested")
+ },
+ expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "implementations")),
+ expectedAvailabilityAfterResolve = Availability.NotAvailable
+ )
+ }
+
+ fun testImportAnotherType() {
+ doTest(
+ """
+ import java.util.Date
+
+ interface I {
+ fun foo(): Date
+ }
+ """.trimIndent(),
+ {
+ replaceTextAtCaret("Date", "java.sql.Date")
+ },
+ {
+ removeImport("java.util.Date")
+ addImport("java.sql.Date")
+ },
+ {
+ replaceTextAtCaret("java.sql.Date", "Date")
+ },
+ expectedAvailability = Availability.NotAvailable,
+ expectedAvailabilityAfterResolve = Availability.Available((changeSignatureAvailableTooltip("foo", "implementations")))
+ )
+ }
+
+ private fun addImport(fqName: String) {
+ (file as KtFile).importList!!.add(KtPsiFactory(project).createImportDirective(ImportPath.fromString(fqName)))
+ }
+
+ private fun removeImport(fqName: String) {
+ (file as KtFile).importList!!.imports.first { it.importedFqName?.asString() == fqName }.delete()
+ }
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringChangeCollectorTest.kt.201 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringChangeCollectorTest.kt.201
new file mode 100644
index 00000000000..fde7692cf27
--- /dev/null
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringChangeCollectorTest.kt.201
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.lang.Language
+import com.intellij.openapi.actionSystem.IdeActions
+import com.intellij.openapi.application.runWriteAction
+import com.intellij.openapi.command.executeCommand
+import com.intellij.openapi.fileTypes.FileType
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.psi.PsiFile
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Parameter
+import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature
+import org.jetbrains.kotlin.idea.KotlinFileType
+import org.jetbrains.kotlin.idea.KotlinLanguage
+import com.intellij.refactoring.suggested.BaseSuggestedRefactoringChangeCollectorTest
+import org.jetbrains.kotlin.psi.*
+
+class KotlinSuggestedRefactoringChangeCollectorTest : BaseSuggestedRefactoringChangeCollectorTest() {
+ override val fileType: FileType
+ get() = KotlinFileType.INSTANCE
+
+ override val language: Language
+ get() = KotlinLanguage.INSTANCE
+
+ override fun addDeclaration(file: PsiFile, text: String): KtNamedFunction {
+ val psiFactory = KtPsiFactory(project)
+ return (file as KtFile).add(psiFactory.createDeclaration(text)) as KtNamedFunction
+ }
+
+ override fun Signature.presentation(labelForParameterId: (Any) -> String?): String {
+ return buildString {
+ append("fun ")
+ val receiverType = (additionalData as KotlinSignatureAdditionalData?)?.receiverType
+ if (receiverType != null) {
+ append(receiverType)
+ append(".")
+ }
+ append(name)
+ append("(")
+ parameters.joinTo(this, separator = ", ") { it.presentation(labelForParameterId(it.id)) }
+ append(")")
+ if (type != null) {
+ append(": ")
+ append(type)
+ }
+ }
+ }
+
+ private fun Parameter.presentation(label: String?): String {
+ return buildString {
+ if (modifiers.isNotEmpty()) {
+ append(modifiers)
+ append(" ")
+ }
+ append(name)
+ append(": ")
+ append(type)
+ if (label != null) {
+ append(" (")
+ append(label)
+ append(")")
+ }
+ }
+ }
+
+ private fun createType(text: String): KtTypeReference {
+ return KtPsiFactory(project).createType(text)
+ }
+
+ private fun createParameter(text: String): KtParameter {
+ return KtPsiFactory(project).createParameter(text)
+ }
+
+ fun testAddParameter() {
+ doTest(
+ "fun foo(p1: Int) {}",
+ { it.valueParameterList!!.addParameter(createParameter("p2: Int")) },
+ expectedOldSignature = "fun foo(p1: Int)",
+ expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p2: Int (new))"
+ )
+ }
+
+ fun testRemoveParameter() {
+ doTest(
+ "fun foo(p1: Int, p2: Int) {}",
+ { it.valueParameterList!!.removeParameter(0) },
+ expectedOldSignature = "fun foo(p1: Int, p2: Int)",
+ expectedNewSignature = "fun foo(p2: Int (initialIndex = 1))"
+ )
+ }
+
+ fun testChangeParameterType() {
+ doTest(
+ "fun foo(p1: Int, p2: Int) {}",
+ { it.valueParameters[1].typeReference = createType("Any?") },
+ expectedOldSignature = "fun foo(p1: Int, p2: Int)",
+ expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p2: Any? (initialIndex = 1))"
+ )
+ }
+
+ fun testChangeParameterNames() {
+ doTest(
+ "fun foo(p1: Int, p2: Int) {}",
+ { it.valueParameters[0].setName("newP1") },
+ { it.valueParameters[1].setName("newP2") },
+ expectedOldSignature = "fun foo(p1: Int, p2: Int)",
+ expectedNewSignature = "fun foo(newP1: Int (initialIndex = 0), newP2: Int (initialIndex = 1))"
+ )
+ }
+
+ fun testReplaceParameter() {
+ doTest(
+ "fun foo(p1: Int, p2: Int) {}",
+ { it.valueParameters[0].replace(createParameter("newP1: Long")) },
+ expectedOldSignature = "fun foo(p1: Int, p2: Int)",
+ expectedNewSignature = "fun foo(newP1: Long (new), p2: Int (initialIndex = 1))"
+ )
+ }
+
+ fun testReorderParametersChangeTypesAndNames() {
+ doTest(
+ "fun foo(p1: Int, p2: Int, p3: Int) {}",
+ {
+ editor.caretModel.moveToOffset(it.valueParameters[2].textOffset)
+ myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
+ myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
+ },
+ {
+ executeCommand {
+ runWriteAction {
+ it.valueParameters[0].typeReference = createType("Any?")
+ it.valueParameters[1].typeReference = createType("Long")
+ it.valueParameters[2].typeReference = createType("Double")
+ }
+ }
+ },
+ {
+ executeCommand {
+ runWriteAction {
+ it.valueParameters[1].setName("newName")
+ }
+ }
+ },
+ wrapIntoCommandAndWriteAction = false,
+ expectedOldSignature = "fun foo(p1: Int, p2: Int, p3: Int)",
+ expectedNewSignature = "fun foo(p3: Any? (initialIndex = 2), newName: Long (initialIndex = 0), p2: Double (initialIndex = 1))"
+ )
+ }
+
+ fun testReorderParametersByCutPaste() {
+ doTest(
+ "fun foo(p1: Int, p2: String, p3: Char)",
+ {
+ val offset = it.valueParameters[1].textRange.endOffset
+ editor.caretModel.moveToOffset(offset)
+ editor.selectionModel.setSelection(offset, offset + ", p3: Char".length)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_CUT)
+ },
+ {
+ val offset = it.valueParameters[0].textRange.endOffset
+ editor.caretModel.moveToOffset(offset)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE)
+ },
+ wrapIntoCommandAndWriteAction = false,
+ expectedOldSignature = "fun foo(p1: Int, p2: String, p3: Char)",
+ expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p3: Char (initialIndex = 2), p2: String (initialIndex = 1))"
+ )
+ }
+
+ fun testReorderParametersByCutPasteAfterChangingName() {
+ doTest(
+ "fun foo(p1: Int, p2: String, p3: Char)",
+ {
+ executeCommand {
+ runWriteAction {
+ it.valueParameters[2].setName("p3New")
+ PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
+ }
+ }
+ },
+ {
+ val offset = it.valueParameters[1].textRange.endOffset
+ editor.caretModel.moveToOffset(offset)
+ editor.selectionModel.setSelection(offset, offset + ", p3New: Char".length)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_CUT)
+ },
+ {
+ val offset = it.valueParameters[0].textRange.endOffset
+ editor.caretModel.moveToOffset(offset)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE)
+ },
+ wrapIntoCommandAndWriteAction = false,
+ expectedOldSignature = "fun foo(p1: Int, p2: String, p3: Char)",
+ expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p3New: Char (initialIndex = 2), p2: String (initialIndex = 1))"
+ )
+ }
+}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringChangeListenerTest.kt.201 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringChangeListenerTest.kt.201
new file mode 100644
index 00000000000..1d335a060d7
--- /dev/null
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringChangeListenerTest.kt.201
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.openapi.actionSystem.IdeActions
+import com.intellij.openapi.application.runWriteAction
+import com.intellij.openapi.command.executeCommand
+import com.intellij.openapi.fileTypes.FileType
+import org.jetbrains.kotlin.idea.KotlinFileType
+import com.intellij.refactoring.suggested.BaseSuggestedRefactoringChangeListenerTest
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import org.jetbrains.kotlin.resolve.ImportPath
+
+class KotlinSuggestedRefactoringChangeListenerTest : BaseSuggestedRefactoringChangeListenerTest() {
+ override val fileType: FileType
+ get() = KotlinFileType.INSTANCE
+
+ fun test1() {
+ setup("fun foo() {}")
+
+ perform("editingStarted: 'foo()'") { myFixture.type("p") }
+
+ perform("nextSignature: 'foo(p)'") { commitAll() }
+ perform { myFixture.type(":") }
+ perform { myFixture.type(" S") }
+ perform { myFixture.type("tr") }
+ perform("nextSignature: 'foo(p: Str)'") { commitAll() }
+ perform { myFixture.type("ing") }
+ perform("nextSignature: 'foo(p: String)'") { commitAll() }
+
+ perform {
+ perform { myFixture.type(", ") }
+ commitAll()
+ }
+ }
+
+ fun testCompletion() {
+ setup("fun foo() {}")
+
+ perform("editingStarted: 'foo()'") { myFixture.type("p: DoubleArra") }
+ perform("nextSignature: 'foo(p: DoubleArra)'", "nextSignature: 'foo(p: DoubleArray)'") { myFixture.completeBasic() }
+ }
+
+ fun testChangeOutsideSignature() {
+ setup("fun foo() {}")
+
+ perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
+ perform("reset") {
+ insertString(editor.document.textLength, "\nval")
+ }
+ }
+
+ fun testEditOtherSignature() {
+ setup("fun foo() {}\nfun bar() = 0")
+
+ val otherFunction = (file as KtFile).declarations[1] as KtNamedFunction
+ val offset = otherFunction.valueParameterList!!.startOffset + 1
+ val marker = editor.document.createRangeMarker(offset, offset)
+
+ perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
+ perform("nextSignature: 'foo(p: A)'") { commitAll() }
+
+ perform("reset", "editingStarted: 'bar()'", "nextSignature: 'bar(p1: String)'") {
+ assert(marker.isValid)
+ insertString(marker.startOffset, "p1: String")
+ commitAll()
+ }
+ }
+
+ fun testChangeInAnotherFile() {
+ setup("fun foo() {}")
+
+ perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
+ perform("reset") {
+ setup("")
+ myFixture.type(" ")
+ }
+ }
+
+ fun testAddImport() {
+ setup("fun foo() {}")
+
+ perform("editingStarted: 'foo()'", "nextSignature: 'foo(p: Any)'") {
+ myFixture.type("p: Any")
+ commitAll()
+ }
+ perform("nextSignature: 'foo(p: Any)'", "nextSignature: 'foo(p: Any)'") {
+ addImport("java.util.ArrayList")
+ }
+ perform("nextSignature: 'foo(p: Any, p2: String)'") {
+ myFixture.type(", p2: String")
+ commitAll()
+ }
+ perform("nextSignature: 'foo(p: Any, p2: String)'", "nextSignature: 'foo(p: Any, p2: String)'") {
+ addImport("java.util.Date")
+ }
+ }
+
+ fun testAddImportWithBlankLineInsertion() {
+ setup(
+ """
+ import foo.bar
+ fun foo() {}
+ """.trimIndent()
+ )
+
+ perform("editingStarted: 'foo()'", "nextSignature: 'foo(p: ArrayList)'") {
+ myFixture.type("p: ArrayList")
+ commitAll()
+ }
+ perform("nextSignature: 'foo(p: ArrayList)'", "nextSignature: 'foo(p: ArrayList)'") {
+ addImport("java.util.ArrayList")
+ }
+ perform("nextSignature: 'foo(p: ArrayList)'") {
+ myFixture.type("")
+ commitAll()
+ }
+ perform("nextSignature: 'foo(p: ArrayList, p2: Any)'") {
+ myFixture.type(", p2: Any")
+ commitAll()
+ }
+ }
+
+ fun testAddImportWithBlankLinesRemoval() {
+ setup(
+ """
+ import foo.bar
+
+
+
+ fun foo() {}
+ """.trimIndent()
+ )
+
+ perform("editingStarted: 'foo()'", "nextSignature: 'foo(p: ArrayList)'") {
+ myFixture.type("p: ArrayList")
+ commitAll()
+ }
+ perform("nextSignature: 'foo(p: ArrayList)'", "nextSignature: 'foo(p: ArrayList)'") {
+ addImport("java.util.ArrayList")
+ }
+ perform("nextSignature: 'foo(p: ArrayList)'") {
+ myFixture.type("")
+ commitAll()
+ }
+ perform("nextSignature: 'foo(p: ArrayList, p2: Any)'") {
+ myFixture.type(", p2: Any")
+ commitAll()
+ }
+ }
+
+ fun testReorderParameters() {
+ setup("fun foo(p1: String, p2: Any, p3: Int) {}")
+
+ perform("editingStarted: 'foo(p1: String, p2: Any, p3: Int)'") {
+ myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
+ }
+ perform("nextSignature: 'foo(p1: String, p3: Int, p2: Any)'") {
+ myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
+ }
+ perform("nextSignature: 'foo(p3: Int, p1: String, p2: Any)'") {
+ commitAll()
+ }
+ perform("nextSignature: 'foo(p1: String, p3: Int, p2: Any)'") {
+ myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_RIGHT)
+ commitAll()
+ }
+ }
+
+ fun testAddParameterViaPsi() {
+ setup("fun foo(p1: Int) {}")
+
+ val function = (file as KtFile).declarations.single() as KtFunction
+ perform(
+ "editingStarted: 'foo(p1: Int)'",
+ "nextSignature: 'foo(p1: Int,)'",
+ "nextSignature: 'foo(p1: Int,p2: Int)'",
+ "nextSignature: 'foo(p1: Int, p2: Int)'"
+ ) {
+ executeCommand {
+ runWriteAction {
+ function.valueParameterList!!.addParameter(KtPsiFactory(project).createParameter("p2: Int"))
+ }
+ }
+ }
+ }
+
+ fun testCommentTyping() {
+ setup("fun foo() {}")
+
+ perform("editingStarted: 'foo()'", "nextSignature: 'foo(p1: Any)'") {
+ myFixture.type("p1: Any")
+ commitAll()
+ }
+
+ perform {
+ myFixture.type("/*")
+ commitAll()
+ }
+
+ perform {
+ myFixture.type(" this is comment for parameter")
+ commitAll()
+ }
+
+ perform("nextSignature: 'foo(p1: Any/* this is comment for parameter*/)'") {
+ myFixture.type("*/")
+ commitAll()
+ }
+
+ perform {
+ myFixture.type(", p2: Int /*")
+ commitAll()
+ }
+
+ perform {
+ myFixture.type("this is comment for another parameter")
+ commitAll()
+ }
+
+ perform("nextSignature: 'foo(p1: Any/* this is comment for parameter*/, p2: Int /*this is comment for another parameter*/)'") {
+ myFixture.type("*/")
+ commitAll()
+ }
+ }
+
+ fun testAddReturnType() {
+ setup(
+ """
+ interface I {
+ fun foo()
+ }
+ """.trimIndent()
+ )
+
+ perform("editingStarted: 'foo()'") { myFixture.type(": String") }
+
+ perform("nextSignature: 'foo(): String'") { commitAll() }
+ }
+
+ fun testNewLocal() {
+ setup(
+ """
+ fun foo() {
+
+ print(a)
+ }
+ """.trimIndent()
+ )
+
+ perform {
+ myFixture.type("val a")
+ commitAll()
+ myFixture.type("bcd")
+ commitAll()
+ }
+ }
+
+ fun testNewFunction() {
+ setup(
+ """
+ interface I {
+
+ }
+ """.trimIndent()
+ )
+
+ perform {
+ myFixture.type("fun foo_bar123(_p1: Int)")
+ commitAll()
+ }
+ }
+
+ fun testNewProperty() {
+ setup(
+ """
+ interface I {
+
+ }
+ """.trimIndent()
+ )
+
+ perform {
+ myFixture.type("val prop: I")
+ commitAll()
+
+ myFixture.type("nt")
+ commitAll()
+ }
+ }
+
+ fun testNewLocalWithNewUsage() {
+ setup(
+ """
+ fun foo() {
+
+ }
+ """.trimIndent()
+ )
+
+ perform {
+ myFixture.type("val a = 10")
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_ENTER)
+ myFixture.type("print(a)")
+ commitAll()
+ }
+
+ perform("editingStarted: 'a'", "nextSignature: 'abcd'") {
+ val variable = file.findDescendantOfType()!!
+ myFixture.editor.caretModel.moveToOffset(variable.nameIdentifier!!.endOffset)
+ myFixture.type("bcd")
+ commitAll()
+ }
+ }
+
+ fun testNewLocalBeforeExpression() {
+ setup(
+ """
+ fun foo(p: Int) {
+ p * p
+ }
+ """.trimIndent()
+ )
+
+ perform {
+ myFixture.type("val a")
+ commitAll()
+ }
+ perform {
+ myFixture.type("bcd = ")
+ commitAll()
+ }
+ }
+
+ fun testNewClassWithConstructor() {
+ setup("")
+
+ perform {
+ myFixture.type("class C")
+ commitAll()
+ }
+ perform {
+ myFixture.type("(p: Int)")
+ commitAll()
+ }
+ }
+
+ fun testNewSecondaryConstructor() {
+ setup(
+ """
+ class C {
+
+ }
+ """.trimIndent()
+ )
+
+ perform {
+ myFixture.type("constructor(p1: Int)")
+ commitAll()
+ }
+ perform {
+ myFixture.type("(, p2: String)")
+ commitAll()
+ }
+ }
+
+ fun testRenameComponentVar() {
+ setup(
+ """
+ fun f() {
+ val (a, b) = f()
+ }
+ """.trimIndent()
+ )
+
+ perform("editingStarted: 'a'", "nextSignature: 'newa'") {
+ myFixture.type("new")
+ commitAll()
+ }
+ }
+
+ private fun addImport(fqName: String) {
+ executeCommand {
+ runWriteAction {
+ (file as KtFile).importList!!.add(KtPsiFactory(project).createImportDirective(ImportPath.fromString(fqName)))
+ }
+ }
+ }
+}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringTest.kt.201 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringTest.kt.201
new file mode 100644
index 00000000000..c27a76c46b3
--- /dev/null
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringTest.kt.201
@@ -0,0 +1,1553 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.refactoring.suggested
+
+import com.intellij.openapi.actionSystem.IdeActions
+import com.intellij.openapi.application.runWriteAction
+import com.intellij.openapi.command.executeCommand
+import com.intellij.openapi.fileTypes.LanguageFileType
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.refactoring.suggested.SuggestedRefactoringExecution
+import com.intellij.refactoring.suggested.BaseSuggestedRefactoringTest
+import com.intellij.refactoring.suggested._suggestedChangeSignatureNewParameterValuesForTests
+import com.intellij.testFramework.LightProjectDescriptor
+import org.jetbrains.kotlin.idea.KotlinFileType
+import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtFunction
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import org.jetbrains.kotlin.resolve.ImportPath
+
+class KotlinSuggestedRefactoringTest : BaseSuggestedRefactoringTest() {
+ override val fileType: LanguageFileType
+ get() = KotlinFileType.INSTANCE
+
+ override fun setUp() {
+ super.setUp()
+ _suggestedChangeSignatureNewParameterValuesForTests = {
+ SuggestedRefactoringExecution.NewParameterValue.Expression(KtPsiFactory(project).createExpression("default$it"))
+ }
+ }
+
+ override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
+
+ fun testAddParameter() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ fun foo(p1: Int) {
+ foo(1)
+ }
+
+ fun bar() {
+ foo(2)
+ }
+ """.trimIndent(),
+ """
+ fun foo(p1: Int, p2: Any) {
+ foo(1, default0)
+ }
+
+ fun bar() {
+ foo(2, default0)
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.type(", p2: Any") }
+ )
+ }
+
+ fun testRemoveParameter() {
+ doTestChangeSignature(
+ """
+ fun foo(p1: Any?, p2: Int) {
+ foo(null, 1)
+ }
+
+ fun bar() {
+ foo(1, 2)
+ }
+ """.trimIndent(),
+ """
+ fun foo(p1: Any?) {
+ foo(null)
+ }
+
+ fun bar() {
+ foo(1)
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ deleteTextBeforeCaret(", p2: Int")
+ }
+ )
+ }
+
+ fun testReorderParameters1() {
+ doTestChangeSignature(
+ """
+ fun foo(p1: Any?, p2: Int, p3: Boolean) {
+ foo(null, 1, true)
+ }
+
+ fun bar() {
+ foo(1, 2, false)
+ }
+ """.trimIndent(),
+ """
+ fun foo(p3: Boolean, p1: Any?, p2: Int) {
+ foo(true, null, 1)
+ }
+
+ fun bar() {
+ foo(false, 1, 2)
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT) },
+ { myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT) },
+ wrapIntoCommandAndWriteAction = false
+ )
+ }
+
+ fun testReorderParameters2() {
+ doTestChangeSignature(
+ """
+ fun foo(p1: Any?, p2: Int, p3: Boolean) {
+ foo(null, 1, true)
+ }
+
+ fun bar() {
+ foo(1, 2, false)
+ }
+ """.trimIndent(),
+ """
+ fun foo(p2: Int, p1: Any?, p3: Boolean) {
+ foo(1, null, true)
+ }
+
+ fun bar() {
+ foo(2, 1, false)
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_CUT)
+ },
+ {
+ editor.caretModel.moveToOffset(editor.caretModel.offset - 10)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE)
+ },
+ {
+ myFixture.type(", ")
+ },
+ {
+ editor.caretModel.moveToOffset(editor.caretModel.offset + 10)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE)
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE)
+ },
+ wrapIntoCommandAndWriteAction = false
+ )
+ }
+
+ fun testChangeParameterType() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(p: String)
+ }
+
+ class C : I {
+ override fun foo(p: String) {
+ }
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(p: Any)
+ }
+
+ class C : I {
+ override fun foo(p: Any) {
+ }
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ replaceTextAtCaret("String", "Any")
+ }
+ )
+ }
+
+ fun testChangeParameterTypeExpectFunction() {
+ ignoreErrorsBefore = true
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ "expect fun foo(p: String)",
+ "expect fun foo(p: Any)",
+ "actual declarations",
+ {
+ replaceTextAtCaret("String", "Any")
+ }
+ )
+ }
+
+ fun testUnresolvedParameterType() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(p: String)
+ }
+
+ class C : I {
+ override fun foo(p: String) {
+ }
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(p: XXX)
+ }
+
+ class C : I {
+ override fun foo(p: XXX) {
+ }
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ replaceTextAtCaret("String", "XXX")
+ }
+ )
+ }
+
+ fun testChangeParameterTypeWithImportInsertion() {
+ myFixture.addFileToProject(
+ "X.kt",
+ """
+ package xxx
+ class X
+ """.trimIndent()
+ )
+
+ val otherFile = myFixture.addFileToProject(
+ "Other.kt",
+ """
+ class D : I {
+ override fun foo(p: String) {
+ }
+ }
+ """.trimIndent()
+ )
+
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(p: String)
+ }
+
+ class C : I {
+ override fun foo(p: String) {
+ }
+ }
+ """.trimIndent(),
+ """
+ import xxx.X
+
+ interface I {
+ fun foo(p: X)
+ }
+
+ class C : I {
+ override fun foo(p: X) {
+ }
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ replaceTextAtCaret("String", "X")
+ },
+ {
+ addImport("xxx.X")
+ }
+ )
+
+ assertEquals(
+ """
+ import xxx.X
+
+ class D : I {
+ override fun foo(p: X) {
+ }
+ }
+ """.trimIndent(),
+ otherFile.text
+ )
+ }
+
+ fun testChangeReturnType() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(): String
+ }
+
+ class C : I {
+ override fun foo(): String = ""
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(): Any
+ }
+
+ class C : I {
+ override fun foo(): Any = ""
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ replaceTextAtCaret("String", "Any")
+ }
+ )
+ }
+
+ fun testAddReturnType() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo()
+ }
+
+ class C : I {
+ override fun foo() {}
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(): String
+ }
+
+ class C : I {
+ override fun foo(): String {}
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ myFixture.type(": String")
+ }
+ )
+ }
+
+ fun testRemoveReturnType() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(): String
+ }
+
+ class C : I {
+ override fun foo(): String = ""
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo()
+ }
+
+ class C : I {
+ override fun foo() = ""
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ deleteTextAtCaret(": String")
+ }
+ )
+ }
+
+ fun testRenameAndAddReturnType() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo()
+ }
+
+ class C : I {
+ override fun foo() {}
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun fooNew(): String
+ }
+
+ class C : I {
+ override fun fooNew(): String {}
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ val offset = editor.caretModel.offset
+ editor.document.insertString(offset, "New")
+ },
+ {
+ val offset = editor.caretModel.offset
+ editor.document.insertString(offset + "New()".length, ": String")
+ }
+ )
+ }
+
+ fun testChangeParameterTypeWithImportReplaced() {
+ myFixture.addFileToProject(
+ "XY.kt",
+ """
+ package xxx
+ class X
+ class Y
+ """.trimIndent()
+ )
+
+ val otherFile = myFixture.addFileToProject(
+ "Other.kt",
+ """
+ import xxx.X
+
+ class D : I {
+ override fun foo(p: X) {
+ }
+ }
+ """.trimIndent()
+ )
+
+ doTestChangeSignature(
+ """
+ import xxx.X
+
+ interface I {
+ fun foo(p: X)
+ }
+ """.trimIndent(),
+ """
+ import xxx.Y
+
+ interface I {
+ fun foo(p: Y)
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ val offset = editor.caretModel.offset
+ editor.document.replaceString(offset, offset + "X".length, "Y")
+ },
+ {
+ addImport("xxx.Y")
+ removeImport("xxx.X")
+ }
+ )
+
+ assertEquals(
+ """
+ import xxx.Y
+
+ class D : I {
+ override fun foo(p: Y) {
+ }
+ }
+ """.trimIndent(),
+ otherFile.text
+ )
+ }
+
+ fun testPreserveCommentsAndFormatting() {
+ doTestChangeSignature(
+ """
+ class A : I {
+ override fun foo() {
+ }
+ }
+
+ interface I {
+ fun foo()
+ }
+ """.trimIndent(),
+ """
+ class A : I {
+ override fun foo(p1: Int, p2: Long, p3: Any?) {
+ }
+ }
+
+ interface I {
+ fun foo(p1: Int/*comment 1*/, p2: Long/*comment 2*/,
+ p3: Any?/*comment 3*/)
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ myFixture.type("p1: Int/*comment 1*/")
+ },
+ {
+ myFixture.type(", p2: Long/*comment 2*/")
+ },
+ {
+ myFixture.type(",")
+ myFixture.performEditorAction(IdeActions.ACTION_EDITOR_ENTER)
+ myFixture.type("p3: Any?/*comment 3*/")
+ },
+ wrapIntoCommandAndWriteAction = false
+ )
+ }
+
+ fun testParameterCompletion() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ "package ppp\nclass Abcdef\nfun foo(p: Int) { }\nfun bar() { foo(1) }",
+ "package ppp\nclass Abcdef\nfun foo(abcdef: Abcdef, p: Int) { }\nfun bar() { foo(default0, 1) }",
+ "usages",
+ {
+ executeCommand {
+ runWriteAction {
+ myFixture.type("abcde")
+ PsiDocumentManager.getInstance(project).commitAllDocuments()
+ }
+ }
+ },
+ {
+ myFixture.completeBasic()
+ },
+ {
+ myFixture.finishLookup('\n')
+ },
+ {
+ executeCommand {
+ runWriteAction {
+ myFixture.type(", ")
+ PsiDocumentManager.getInstance(project).commitAllDocuments()
+ }
+ }
+ },
+ wrapIntoCommandAndWriteAction = false
+ )
+ }
+
+ fun testRenameTwoParameters() {
+ doTestChangeSignature(
+ """
+ fun foo(p1: Int, p2: String) {
+ p1.hashCode()
+ p2.hashCode()
+ }
+ """.trimIndent(),
+ """
+ fun foo(p1New: Int, p2New: String) {
+ p1New.hashCode()
+ p2New.hashCode()
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ val function = (file as KtFile).declarations.single() as KtFunction
+ function.valueParameters[0].setName("p1New")
+ function.valueParameters[1].setName("p2New")
+ }
+ )
+ }
+
+ fun testChangePropertyType() {
+ doTestChangeSignature(
+ """
+ interface I {
+ var v: String
+ }
+
+ class C : I {
+ override var v: String = ""
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ var v: Any
+ }
+
+ class C : I {
+ override var v: Any = ""
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ replaceTextAtCaret("String", "Any")
+ },
+ expectedPresentation = """
+ Old:
+ 'var '
+ 'v'
+ ': '
+ 'String' (modified)
+ New:
+ 'var '
+ 'v'
+ ': '
+ 'Any' (modified)
+ """.trimIndent()
+ )
+ }
+
+ fun testRenameClass() {
+ doTestRename(
+ """
+ var v: C? = null
+
+ interface C
+ """.trimIndent(),
+ """
+ var v: CNew? = null
+
+ interface CNew
+ """.trimIndent(),
+ "C",
+ "CNew",
+ {
+ myFixture.type("New")
+ }
+ )
+ }
+
+ fun testRenameLocalVar() {
+ doTestRename(
+ """
+ fun foo() {
+ var v = 0
+ v++
+ }
+ """.trimIndent(),
+ """
+ fun foo() {
+ var vNew = 0
+ vNew++
+ }
+ """.trimIndent(),
+ "v",
+ "vNew",
+ {
+ myFixture.type("New")
+ }
+ )
+ }
+
+ fun testRenameComponentVar() {
+ doTestRename(
+ """
+ data class Pair(val t1: T1, val t2: T2)
+
+ fun f(): Pair = Pair("a", 1)
+
+ fun g() {
+ val (a, b) = f()
+ b.hashCode()
+ }
+ """.trimIndent(),
+ """
+ data class Pair(val t1: T1, val t2: T2)
+
+ fun f(): Pair = Pair("a", 1)
+
+ fun g() {
+ val (a, b1) = f()
+ b1.hashCode()
+ }
+ """.trimIndent(),
+ "b",
+ "b1",
+ {
+ myFixture.type("1")
+ }
+ )
+ }
+
+ fun testRenameLoopVar() {
+ doTestRename(
+ """
+ fun f(list: List) {
+ for (s in list) {
+ s.hashCode()
+ }
+ }
+ """.trimIndent(),
+ """
+ fun f(list: List) {
+ for (s1 in list) {
+ s1.hashCode()
+ }
+ }
+ """.trimIndent(),
+ "s",
+ "s1",
+ {
+ myFixture.type("1")
+ }
+ )
+ }
+
+ fun testRenameParameter() {
+ doTestRename(
+ """
+ fun foo(p: String) {
+ p.hashCode()
+ }
+ """.trimIndent(),
+ """
+ fun foo(pNew: String) {
+ pNew.hashCode()
+ }
+ """.trimIndent(),
+ "p",
+ "pNew",
+ {
+ myFixture.type("New")
+ }
+ )
+ }
+
+ fun testRenameParametersInOverrides() {
+ doTestRename(
+ """
+ interface I {
+ fun foo(p: String)
+ }
+
+ class C : I {
+ override fun foo(p: String) {
+ p.hashCode()
+ }
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(pNew: String)
+ }
+
+ class C : I {
+ override fun foo(pNew: String) {
+ pNew.hashCode()
+ }
+ }
+ """.trimIndent(),
+ "p",
+ "pNew",
+ {
+ myFixture.type("New")
+ }
+ )
+ }
+
+ fun testAddPrimaryConstructorParameter() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ class C(private val x: String, y: Int)
+
+ fun foo() {
+ val c = C("a", 1)
+ }
+ """.trimIndent(),
+ """
+ class C(private val x: String, y: Int, z: Any)
+
+ fun foo() {
+ val c = C("a", 1, default0)
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.type(", z: Any") },
+ expectedPresentation = """
+ Old:
+ 'C'
+ '('
+ LineBreak('', true)
+ Group:
+ 'x'
+ ': '
+ 'String'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'y'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ New:
+ 'C'
+ '('
+ LineBreak('', true)
+ Group:
+ 'x'
+ ': '
+ 'String'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'y'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group (added):
+ 'z'
+ ': '
+ 'Any'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testAddSecondaryConstructorParameter() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ class C {
+ constructor(x: String)
+ }
+
+ fun foo() {
+ val c = C("a")
+ }
+ """.trimIndent(),
+ """
+ class C {
+ constructor(x: String, y: Int)
+ }
+
+ fun foo() {
+ val c = C("a", default0)
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.type(", y: Int") },
+ expectedPresentation = """
+ Old:
+ 'constructor'
+ '('
+ LineBreak('', true)
+ Group:
+ 'x'
+ ': '
+ 'String'
+ LineBreak('', false)
+ ')'
+ New:
+ 'constructor'
+ '('
+ LineBreak('', true)
+ Group:
+ 'x'
+ ': '
+ 'String'
+ ','
+ LineBreak(' ', true)
+ Group (added):
+ 'y'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testAddReceiver() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo()
+ }
+
+ class C : I {
+ override fun foo() {
+ }
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun Int.foo()
+ }
+
+ class C : I {
+ override fun Int.foo() {
+ }
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.type("Int.") },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'Int.' (added)
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testAddReceiverAndParameter() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ fun foo() {
+ }
+
+ fun bar() {
+ foo()
+ }
+ """.trimIndent(),
+ """
+ fun Int.foo(o: Any) {
+ }
+
+ fun bar() {
+ default0.foo(default1)
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.type("Int.") },
+ { repeat("foo(".length) { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT) } },
+ { myFixture.type("o: Any") }
+ )
+ }
+
+ fun testRemoveReceiver() {
+ doTestChangeSignature(
+ """
+ fun Int.foo() {
+ }
+
+ fun bar() {
+ 1.foo()
+ }
+ """.trimIndent(),
+ """
+ fun foo() {
+ }
+
+ fun bar() {
+ foo()
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ repeat(4) { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE) }
+ }
+ )
+ }
+
+ fun testChangeReceiverType() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun Int.foo()
+ }
+
+ class C : I {
+ override fun Int.foo() {
+ }
+ }
+
+ fun I.f() {
+ 1.foo()
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun Any.foo()
+ }
+
+ class C : I {
+ override fun Any.foo() {
+ }
+ }
+
+ fun I.f() {
+ 1.foo()
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ repeat("Int".length) { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE) }
+ myFixture.type("Any")
+ },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'Int' (modified)
+ '.'
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'Any' (modified)
+ '.'
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testChangeReceiverTypeAndRemoveParameter() {
+ doTestChangeSignature(
+ """
+ fun Int.foo(p: Any) {}
+
+ fun bar() {
+ 1.foo("a")
+ }
+ """.trimIndent(),
+ """
+ fun Any.foo() {}
+
+ fun bar() {
+ 1.foo()
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ repeat("Int".length) { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE) }
+ myFixture.type("Any")
+ },
+ {
+ editor.caretModel.moveToOffset(editor.caretModel.offset + ".foo(".length)
+ repeat("p: Any".length) { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE) }
+ }
+ )
+ }
+
+ fun testAddVarargParameter() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(p: Int)
+ }
+
+ class C : I {
+ override fun foo(p: Int) {
+ }
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(p: Int, vararg s: String)
+ }
+
+ class C : I {
+ override fun foo(p: Int, vararg s: String) {
+ }
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.type(", vararg s: String") },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group (added):
+ 'vararg'
+ ' '
+ 's'
+ ': '
+ 'String'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ //TODO
+/*
+ fun testAddVarargModifier() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(p: Int)
+ }
+
+ class C : I {
+ override fun foo(p: Int) {
+ }
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(vararg p: Int)
+ }
+
+ class C : I {
+ override fun foo(vararg p: Int) {
+ }
+ }
+ """.trimIndent(),
+ { myFixture.type("vararg ") },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'vararg' (added)
+ ' '
+ 'p'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testRemoveVarargModifier() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(vararg p: Int)
+ }
+
+ class C : I {
+ override fun foo(vararg p: Int) {
+ }
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(p: Int)
+ }
+
+ class C : I {
+ override fun foo(p: Int) {
+ }
+ }
+ """.trimIndent(),
+ {
+ deleteStringAtCaret("vararg ")
+ },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'vararg' (removed)
+ ' '
+ 'p'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+*/
+
+ fun testSwapConstructorParameters() {
+ doTestChangeSignature(
+ """
+ class C(
+ p1: Int,
+ p2: String
+ )
+
+ fun foo() {
+ C(1, "")
+ }
+ """.trimIndent(),
+ """
+ class C(
+ p2: String,
+ p1: Int
+ )
+
+ fun foo() {
+ C("", 1)
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ myFixture.performEditorAction(IdeActions.ACTION_MOVE_STATEMENT_UP_ACTION)
+ }
+ )
+ }
+
+ fun testChangeParameterTypeOfVirtualExtensionMethod() {
+ doTestChangeSignature(
+ """
+ abstract class Base {
+ protected abstract fun Int.foo(p: String)
+
+ fun bar() {
+ 1.foo("a")
+ }
+ }
+
+ class Derived : Base() {
+ override fun Int.foo(p: String) {
+ }
+ }
+ """.trimIndent(),
+ """
+ abstract class Base {
+ protected abstract fun Int.foo(p: Any)
+
+ fun bar() {
+ 1.foo("a")
+ }
+ }
+
+ class Derived : Base() {
+ override fun Int.foo(p: Any) {
+ }
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ replaceTextAtCaret("String", "Any")
+ }
+ )
+ }
+
+ fun testAddParameterToVirtualExtensionMethod() {
+ ignoreErrorsAfter = true
+ doTestChangeSignature(
+ """
+ abstract class Base {
+ protected abstract fun Int.foo(p: String)
+
+ fun bar() {
+ 1.foo("a")
+ }
+ }
+
+ class Derived : Base() {
+ override fun Int.foo(p: String) {
+ }
+ }
+ """.trimIndent(),
+ """
+ abstract class Base {
+ protected abstract fun Int.foo(p: String, p1: Int)
+
+ fun bar() {
+ 1.foo("a", default0)
+ }
+ }
+
+ class Derived : Base() {
+ override fun Int.foo(p: String, p1: Int) {
+ }
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ myFixture.type(", p1: Int")
+ }
+ )
+ }
+
+ fun testAddParameterWithFullyQualifiedType() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo()
+ }
+
+ class C : I {
+ override fun foo() {
+ }
+ }
+ """.trimIndent(),
+ """
+ import java.io.InputStream
+
+ interface I {
+ fun foo(p: java.io.InputStream)
+ }
+
+ class C : I {
+ override fun foo(p: InputStream) {
+ }
+ }
+ """.trimIndent(),
+ "usages",
+ { myFixture.type("p: java.io.InputStream") }
+ )
+ }
+
+ fun testAddParameterWithDefaultValue() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(p1: Int)
+ }
+
+ class C : I {
+ override fun foo(p1: Int) {
+ }
+ }
+
+ fun f(i: I) {
+ i.foo(1)
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(p1: Int, p2: Int = 10)
+ }
+
+ class C : I {
+ override fun foo(p1: Int, p2: Int) {
+ }
+ }
+
+ fun f(i: I) {
+ i.foo(1)
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ myFixture.type(", p2: Int = 10")
+ },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p1'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group (added):
+ 'p2'
+ ': '
+ 'Int'
+ ' = '
+ '10'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testReorderParameterWithDefaultValue() {
+ doTestChangeSignature(
+ """
+ interface I {
+ fun foo(p1: Int, p2: Int = 10)
+ }
+
+ class C : I {
+ override fun foo(p1: Int, p2: Int) {
+ }
+ }
+
+ fun f(i: I) {
+ i.foo(2)
+ }
+ """.trimIndent(),
+ """
+ interface I {
+ fun foo(p2: Int = 10, p1: Int)
+ }
+
+ class C : I {
+ override fun foo(p2: Int, p1: Int) {
+ }
+ }
+
+ fun f(i: I) {
+ i.foo(p1 = 2)
+ }
+ """.trimIndent(),
+ "usages",
+ {
+ myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_RIGHT)
+ },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group (moved):
+ 'p1'
+ ': '
+ 'Int'
+ ','
+ LineBreak(' ', true)
+ Group:
+ 'p2'
+ ': '
+ 'Int'
+ ' = '
+ '10'
+ LineBreak('', false)
+ ')'
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', true)
+ Group:
+ 'p2'
+ ': '
+ 'Int'
+ ' = '
+ '10'
+ ','
+ LineBreak(' ', true)
+ Group (moved):
+ 'p1'
+ ': '
+ 'Int'
+ LineBreak('', false)
+ ')'
+ """.trimIndent()
+ )
+ }
+
+ fun testReplaceTypeWithItsAlias() {
+ doTestChangeSignature(
+ """
+ typealias StringToUnit = (String) -> Unit
+
+ interface I {
+ fun foo(): (String) -> Unit
+ }
+
+ class C : I {
+ override fun foo(): (String) -> Unit {
+ throw UnsupportedOperationException()
+ }
+ }
+ """.trimIndent(),
+ """
+ typealias StringToUnit = (String) -> Unit
+
+ interface I {
+ fun foo(): StringToUnit
+ }
+
+ class C : I {
+ override fun foo(): StringToUnit {
+ throw UnsupportedOperationException()
+ }
+ }
+ """.trimIndent(),
+ "implementations",
+ {
+ replaceTextAtCaret("(String) -> Unit", "StringToUnit")
+ },
+ expectedPresentation = """
+ Old:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ ': '
+ '(String) -> Unit' (modified)
+ New:
+ 'fun '
+ 'foo'
+ '('
+ LineBreak('', false)
+ ')'
+ ': '
+ 'StringToUnit' (modified)
+ """.trimIndent()
+ )
+ }
+
+ private fun addImport(fqName: String) {
+ (file as KtFile).importList!!.add(KtPsiFactory(project).createImportDirective(ImportPath.fromString(fqName)))
+ }
+
+ private fun removeImport(fqName: String) {
+ (file as KtFile).importList!!.imports.first { it.importedFqName?.asString() == fqName }.delete()
+ }
+}