Suggested Rename and Change Signature implementation for Kotlin
This commit is contained in:
@@ -88,5 +88,6 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<fileTypeUsageSchemaDescriptor schema="Gradle Script" implementationClass="org.jetbrains.kotlin.idea.core.script.KotlinGradleScriptFileTypeSchemaDetector"/>
|
<fileTypeUsageSchemaDescriptor schema="Gradle Script" implementationClass="org.jetbrains.kotlin.idea.core.script.KotlinGradleScriptFileTypeSchemaDetector"/>
|
||||||
|
|
||||||
<completion.ml.model implementation="org.jetbrains.kotlin.idea.completion.ml.KotlinMLRankingProvider"/>
|
<completion.ml.model implementation="org.jetbrains.kotlin.idea.completion.ml.KotlinMLRankingProvider"/>
|
||||||
|
<suggestedRefactoringSupport language="kotlin" implementationClass="org.jetbrains.kotlin.idea.refactoring.suggested.KotlinSuggestedRefactoringSupport"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
+77
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+161
@@ -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<Any, String>()
|
||||||
|
val improvedNewParameterTypesById = mutableMapOf<Any, String>()
|
||||||
|
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<String?, String?> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+157
@@ -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<KotlinTypeInfo>
|
||||||
|
)
|
||||||
|
|
||||||
|
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<NewParameterValue>,
|
||||||
|
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<PsiElement>) = true
|
||||||
|
}
|
||||||
|
val changeSignature = KotlinChangeSignature(project, descriptor, configuration, declaration, null)
|
||||||
|
val methodDescriptor = changeSignature.adjustDescriptor(listOf(descriptor))!!
|
||||||
|
|
||||||
|
val parameters = mutableListOf<KotlinParameterInfo>()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+66
@@ -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<TextRange?> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
+153
@@ -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 ?: ""
|
||||||
+46
@@ -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<NewParameterData> {
|
||||||
|
val newParameters = mutableListOf<NewParameterData>()
|
||||||
|
|
||||||
|
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("<receiver>", 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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+874
@@ -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<Parameter>,
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+371
@@ -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<caret>) {
|
||||||
|
foo(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo(2)
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
myFixture.type(", p2: Any")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
myFixture.type(", p")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertTrailingComma() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
fun foo(p1: Int<caret>) {
|
||||||
|
foo(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo(2)
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
myFixture.type(",")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.NotAvailable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeNonVirtualPropertyType() {
|
||||||
|
doTest(
|
||||||
|
"val v: <caret>String = \"\"",
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("String", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeParameterTypeNonVirtual() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p: <caret>String) {}",
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("String", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeReturnTypeNonVirtual() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(): <caret>String = \"\"",
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("String", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeLocalVariableType() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
fun foo() {
|
||||||
|
val local: <caret>Int
|
||||||
|
local = 10
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("Int", "Long")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.NotAvailable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddLocalVariableType() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
fun foo() {
|
||||||
|
var local<caret> = 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: <caret>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(<caret>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: <caret>Int, p2: Int) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("Int", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "overrides"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeParameterTypeForAbstract() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
abstract class C {
|
||||||
|
abstract fun foo(p1: <caret>Int, p2: Int)
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("Int", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "implementations"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeParameterTypeInInterface() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
fun foo(p1: <caret>Int, p2: Int)
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("Int", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "implementations"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeParameterTypeInInterfaceWithBody() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
fun foo(p1: <caret>Int, p2: Int) {}
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("Int", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "overrides"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeTypeOfPropertyWithImplementationInInterface() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
val p: <caret>Int
|
||||||
|
get() = 0
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
replaceTextAtCaret("Int", "Any")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("p", "overrides"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSpecifyExplicitType() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
open class C {
|
||||||
|
open fun foo()<caret> = 1
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
{
|
||||||
|
myFixture.type(": Int")
|
||||||
|
},
|
||||||
|
expectedAvailability = Availability.Available(changeSignatureAvailableTooltip("foo", "overrides")),
|
||||||
|
expectedAvailabilityAfterResolve = Availability.NotAvailable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testRemoveExplicitType() {
|
||||||
|
doTest(
|
||||||
|
"""
|
||||||
|
open class C {
|
||||||
|
open fun foo(): Int<caret> = 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(): <caret>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 <caret>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: <caret>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(): <caret>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()
|
||||||
|
}
|
||||||
|
}
|
||||||
+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<KtNamedFunction>() {
|
||||||
|
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))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+395
@@ -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(<caret>) {}")
|
||||||
|
|
||||||
|
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(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p: DoubleArra") }
|
||||||
|
perform("nextSignature: 'foo(p: DoubleArra)'", "nextSignature: 'foo(p: DoubleArray)'") { myFixture.completeBasic() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeOutsideSignature() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
|
||||||
|
perform("reset") {
|
||||||
|
insertString(editor.document.textLength, "\nval")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testEditOtherSignature() {
|
||||||
|
setup("fun foo(<caret>) {}\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(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
|
||||||
|
perform("reset") {
|
||||||
|
setup("")
|
||||||
|
myFixture.type(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddImport() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
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(<caret>) {}
|
||||||
|
""".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<String>)'") {
|
||||||
|
myFixture.type("<String>")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList<String>, p2: Any)'") {
|
||||||
|
myFixture.type(", p2: Any")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddImportWithBlankLinesRemoval() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
import foo.bar
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun foo(<caret>) {}
|
||||||
|
""".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<String>)'") {
|
||||||
|
myFixture.type("<String>")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList<String>, p2: Any)'") {
|
||||||
|
myFixture.type(", p2: Any")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testReorderParameters() {
|
||||||
|
setup("fun foo(p1: String, p2: Any, p3<caret>: 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(<caret>) {}")
|
||||||
|
|
||||||
|
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()<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type(": String") }
|
||||||
|
|
||||||
|
perform("nextSignature: 'foo(): String'") { commitAll() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewLocal() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun foo() {
|
||||||
|
<caret>
|
||||||
|
print(a)
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("val a")
|
||||||
|
commitAll()
|
||||||
|
myFixture.type("bcd")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewFunction() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("fun foo_bar123(_p1: Int)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewProperty() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("val prop: I")
|
||||||
|
commitAll()
|
||||||
|
|
||||||
|
myFixture.type("nt")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewLocalWithNewUsage() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun foo() {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".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<KtProperty>()!!
|
||||||
|
myFixture.editor.caretModel.moveToOffset(variable.nameIdentifier!!.endOffset)
|
||||||
|
myFixture.type("bcd")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewLocalBeforeExpression() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun foo(p: Int) {
|
||||||
|
<caret>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 {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("constructor(p1: Int)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform {
|
||||||
|
myFixture.type("(, p2: String)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testRenameComponentVar() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun f() {
|
||||||
|
val (<caret>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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1553
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user