Minor: clean up some quickfixes & refactorings

This commit is contained in:
Dmitry Gridin
2019-07-05 15:12:46 +07:00
parent 9ba7907b81
commit 795dcfa8ff
6 changed files with 269 additions and 329 deletions
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.quickfix package org.jetbrains.kotlin.idea.quickfix
@@ -25,7 +14,8 @@ import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
class RemoveUselessCastFix(element: KtBinaryExpressionWithTypeRHS) : KotlinQuickFixAction<KtBinaryExpressionWithTypeRHS>(element), CleanupFix { class RemoveUselessCastFix(element: KtBinaryExpressionWithTypeRHS) : KotlinQuickFixAction<KtBinaryExpressionWithTypeRHS>(element),
CleanupFix {
override fun getFamilyName() = "Remove useless cast" override fun getFamilyName() = "Remove useless cast"
override fun getText(): String = familyName override fun getText(): String = familyName
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.quickfix.replaceWith package org.jetbrains.kotlin.idea.quickfix.replaceWith
@@ -51,16 +40,13 @@ class DeprecatedSymbolUsageInWholeProjectFix(
replacementStrategy.replaceUsagesInWholeProject(psiElement, progressTitle = "Applying '$text'", commandName = text) replacementStrategy.replaceUsagesInWholeProject(psiElement, progressTitle = "Applying '$text'", commandName = text)
} }
private fun targetPsiElement(): KtDeclaration? { private fun targetPsiElement(): KtDeclaration? = when (val referenceTarget = element?.mainReference?.resolve()) {
val referenceTarget = element?.mainReference?.resolve() is KtNamedFunction -> referenceTarget
return when (referenceTarget) { is KtProperty -> referenceTarget
is KtNamedFunction -> referenceTarget is KtTypeAlias -> referenceTarget
is KtProperty -> referenceTarget is KtConstructor<*> -> referenceTarget.getContainingClassOrObject() //TODO: constructor can be deprecated itself
is KtTypeAlias -> referenceTarget is KtClass -> referenceTarget.takeIf { it.isAnnotation() }
is KtConstructor<*> -> referenceTarget.getContainingClassOrObject() //TODO: constructor can be deprecated itself else -> null
is KtClass -> referenceTarget.takeIf { it.isAnnotation() }
else -> null
}
} }
companion object : KotlinSingleIntentionActionFactory() { companion object : KotlinSingleIntentionActionFactory() {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring package org.jetbrains.kotlin.idea.refactoring
@@ -34,20 +23,21 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import java.util.* import java.util.*
abstract class AbstractPullPushMembersHandler( abstract class AbstractPullPushMembersHandler(
private val refactoringName: String, private val refactoringName: String,
private val helpId: String, private val helpId: String,
private val wrongPositionMessage: String private val wrongPositionMessage: String
) : RefactoringActionHandler, ElementsHandler { ) : RefactoringActionHandler, ElementsHandler {
private fun reportWrongPosition(project: Project, editor: Editor?) { private fun reportWrongPosition(project: Project, editor: Editor?) {
val message = RefactoringBundle.getCannotRefactorMessage(wrongPositionMessage) val message = RefactoringBundle.getCannotRefactorMessage(wrongPositionMessage)
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId) CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId)
} }
private fun KtParameter.getContainingClass() = if (hasValOrVar()) (ownerFunction as? KtPrimaryConstructor)?.containingClassOrObject else null private fun KtParameter.getContainingClass() =
if (hasValOrVar()) (ownerFunction as? KtPrimaryConstructor)?.containingClassOrObject else null
protected fun reportWrongContext(project: Project, editor: Editor?) { protected fun reportWrongContext(project: Project, editor: Editor?) {
val message = RefactoringBundle.getCannotRefactorMessage( val message = RefactoringBundle.getCannotRefactorMessage(
RefactoringBundle.message("is.not.supported.in.the.current.context", refactoringName) RefactoringBundle.message("is.not.supported.in.the.current.context", refactoringName)
) )
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId) CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId)
} }
@@ -64,8 +54,8 @@ abstract class AbstractPullPushMembersHandler(
val target = (file.findElementAt(offset) ?: return).parentsWithSelf.firstOrNull { val target = (file.findElementAt(offset) ?: return).parentsWithSelf.firstOrNull {
it is KtClassOrObject it is KtClassOrObject
|| ((it is KtNamedFunction || it is KtProperty) && it.parent is KtClassBody) || ((it is KtNamedFunction || it is KtProperty) && it.parent is KtClassBody)
|| it is KtParameter && it.hasValOrVar() && it.ownerFunction is KtPrimaryConstructor || it is KtParameter && it.hasValOrVar() && it.ownerFunction is KtPrimaryConstructor
} }
if (target == null) { if (target == null) {
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter package org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter
@@ -66,23 +55,22 @@ import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
data class IntroduceParameterDescriptor( data class IntroduceParameterDescriptor(
val originalRange: KotlinPsiRange, val originalRange: KotlinPsiRange,
val callable: KtNamedDeclaration, val callable: KtNamedDeclaration,
val callableDescriptor: FunctionDescriptor, val callableDescriptor: FunctionDescriptor,
val newParameterName: String, val newParameterName: String,
val newParameterTypeText: String, val newParameterTypeText: String,
val argumentValue: KtExpression, val argumentValue: KtExpression,
val withDefaultValue: Boolean, val withDefaultValue: Boolean,
val parametersUsages: MultiMap<KtElement, KtElement>, val parametersUsages: MultiMap<KtElement, KtElement>,
val occurrencesToReplace: List<KotlinPsiRange>, val occurrencesToReplace: List<KotlinPsiRange>,
val parametersToRemove: List<KtElement> = getParametersToRemove(withDefaultValue, parametersUsages, occurrencesToReplace), val parametersToRemove: List<KtElement> = getParametersToRemove(withDefaultValue, parametersUsages, occurrencesToReplace),
val occurrenceReplacer: IntroduceParameterDescriptor.(KotlinPsiRange) -> Unit = {} val occurrenceReplacer: IntroduceParameterDescriptor.(KotlinPsiRange) -> Unit = {}
) { ) {
val newArgumentValue: KtExpression by lazy { val newArgumentValue: KtExpression by lazy {
if (argumentValue.mustBeParenthesizedInInitializerPosition()) { if (argumentValue.mustBeParenthesizedInInitializerPosition()) {
KtPsiFactory(callable).createExpressionByPattern("($0)", argumentValue) KtPsiFactory(callable).createExpressionByPattern("($0)", argumentValue)
} } else {
else {
argumentValue argumentValue
} }
} }
@@ -95,7 +83,7 @@ data class IntroduceParameterDescriptor(
valVar = if (callable is KtClass) { valVar = if (callable is KtClass) {
val modifierIsUnnecessary: (PsiElement) -> Boolean = { val modifierIsUnnecessary: (PsiElement) -> Boolean = {
when { when {
it.parent != callable.getBody() -> it.parent != callable.body ->
false false
it is KtAnonymousInitializer -> it is KtAnonymousInitializer ->
true true
@@ -106,17 +94,16 @@ data class IntroduceParameterDescriptor(
} }
} }
if (occurrencesToReplace.all { if (occurrencesToReplace.all {
PsiTreeUtil.findCommonParent(it.elements)?.parentsWithSelf?.any(modifierIsUnnecessary) ?: false PsiTreeUtil.findCommonParent(it.elements)?.parentsWithSelf?.any(modifierIsUnnecessary) == true
}) KotlinValVar.None else KotlinValVar.Val }) KotlinValVar.None else KotlinValVar.Val
} } else KotlinValVar.None
else KotlinValVar.None
} }
} }
fun getParametersToRemove( fun getParametersToRemove(
withDefaultValue: Boolean, withDefaultValue: Boolean,
parametersUsages: MultiMap<KtElement, KtElement>, parametersUsages: MultiMap<KtElement, KtElement>,
occurrencesToReplace: List<KotlinPsiRange> occurrencesToReplace: List<KotlinPsiRange>
): List<KtElement> { ): List<KtElement> {
if (withDefaultValue) return Collections.emptyList() if (withDefaultValue) return Collections.emptyList()
@@ -146,15 +133,17 @@ fun IntroduceParameterDescriptor.performRefactoring(onExit: (() -> Unit)? = null
} else 0 } else 0
} }
.sortedDescending() .sortedDescending()
.forEach { methodDescriptor.removeParameter(it) } .forEach { methodDescriptor.removeParameter(it) }
} }
val defaultValue = if (newArgumentValue is KtProperty) (newArgumentValue as KtProperty).initializer else newArgumentValue val defaultValue = if (newArgumentValue is KtProperty) (newArgumentValue as KtProperty).initializer else newArgumentValue
val parameterInfo = KotlinParameterInfo(callableDescriptor = callableDescriptor, val parameterInfo = KotlinParameterInfo(
name = newParameterName, callableDescriptor = callableDescriptor,
defaultValueForCall = if (withDefaultValue) null else defaultValue, name = newParameterName,
defaultValueForParameter = if (withDefaultValue) defaultValue else null, defaultValueForCall = if (withDefaultValue) null else defaultValue,
valOrVar = valVar) defaultValueForParameter = if (withDefaultValue) defaultValue else null,
valOrVar = valVar
)
parameterInfo.currentTypeInfo = KotlinTypeInfo(false, null, newParameterTypeText) parameterInfo.currentTypeInfo = KotlinTypeInfo(false, null, newParameterTypeText)
methodDescriptor.addParameter(parameterInfo) methodDescriptor.addParameter(parameterInfo)
} }
@@ -180,9 +169,9 @@ fun IntroduceParameterDescriptor.performRefactoring(onExit: (() -> Unit)? = null
} }
fun selectNewParameterContext( fun selectNewParameterContext(
editor: Editor, editor: Editor,
file: KtFile, file: KtFile,
continuation: (elements: List<PsiElement>, targetParent: PsiElement) -> Unit continuation: (elements: List<PsiElement>, targetParent: PsiElement) -> Unit
) { ) {
selectElementsWithTargetParent( selectElementsWithTargetParent(
operationName = INTRODUCE_PARAMETER, operationName = INTRODUCE_PARAMETER,
@@ -192,24 +181,24 @@ fun selectNewParameterContext(
elementKinds = listOf(CodeInsightUtils.ElementKind.EXPRESSION), elementKinds = listOf(CodeInsightUtils.ElementKind.EXPRESSION),
elementValidator = ::validateExpressionElements, elementValidator = ::validateExpressionElements,
getContainers = { _, parent -> getContainers = { _, parent ->
val parents = parent.parents val parents = parent.parents
val stopAt = (parent.parents.zip(parent.parents.drop(1))) val stopAt = (parent.parents.zip(parent.parents.drop(1)))
.firstOrNull { isObjectOrNonInnerClass(it.first) } .firstOrNull { isObjectOrNonInnerClass(it.first) }
?.second ?.second
(if (stopAt != null) parent.parents.takeWhile { it != stopAt } else parents) (if (stopAt != null) parent.parents.takeWhile { it != stopAt } else parents)
.filter { .filter {
((it is KtClass && !it.isInterface() && it !is KtEnumEntry) || it is KtNamedFunction || it is KtSecondaryConstructor) && ((it is KtClass && !it.isInterface() && it !is KtEnumEntry) || it is KtNamedFunction || it is KtSecondaryConstructor) &&
((it as KtNamedDeclaration).getValueParameterList() != null || it.nameIdentifier != null) ((it as KtNamedDeclaration).getValueParameterList() != null || it.nameIdentifier != null)
} }
.toList() .toList()
}, },
continuation = continuation continuation = continuation
) )
} }
interface KotlinIntroduceParameterHelper { interface KotlinIntroduceParameterHelper {
object Default: KotlinIntroduceParameterHelper object Default : KotlinIntroduceParameterHelper
fun configure(descriptor: IntroduceParameterDescriptor): IntroduceParameterDescriptor = descriptor fun configure(descriptor: IntroduceParameterDescriptor): IntroduceParameterDescriptor = descriptor
} }
@@ -228,8 +217,7 @@ open class KotlinIntroduceParameterHandler(
val expressionType = if (physicalExpression is KtProperty && physicalExpression.isLocal) { val expressionType = if (physicalExpression is KtProperty && physicalExpression.isLocal) {
context[BindingContext.VARIABLE, physicalExpression]?.type context[BindingContext.VARIABLE, physicalExpression]?.type
} } else {
else {
expression.extractableSubstringInfo?.type ?: context.getType(physicalExpression) expression.extractableSubstringInfo?.type ?: context.getType(physicalExpression)
} }
@@ -240,8 +228,8 @@ open class KotlinIntroduceParameterHandler(
if (expressionType.isUnit() || expressionType.isNothing()) { if (expressionType.isUnit() || expressionType.isNothing()) {
val message = KotlinRefactoringBundle.message( val message = KotlinRefactoringBundle.message(
"cannot.introduce.parameter.of.0.type", "cannot.introduce.parameter.of.0.type",
IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(expressionType) IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(expressionType)
) )
showErrorHint(project, editor, message, INTRODUCE_PARAMETER) showErrorHint(project, editor, message, INTRODUCE_PARAMETER)
return return
@@ -249,15 +237,18 @@ open class KotlinIntroduceParameterHandler(
val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, targetParent] val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, targetParent]
val functionDescriptor = descriptor.toFunctionDescriptor(targetParent) val functionDescriptor = descriptor.toFunctionDescriptor(targetParent)
val replacementType = expressionType.approximateWithResolvableType(targetParent.getResolutionScope(context, targetParent.getResolutionFacade()), false) val replacementType = expressionType.approximateWithResolvableType(
targetParent.getResolutionScope(context, targetParent.getResolutionFacade()),
false
)
val body = when (targetParent) { val body = when (targetParent) {
is KtFunction -> targetParent.bodyExpression is KtFunction -> targetParent.bodyExpression
is KtClass -> targetParent.getBody() is KtClass -> targetParent.body
else -> null else -> null
} }
val bodyValidator: ((String) -> Boolean)? = val bodyValidator: ((String) -> Boolean)? =
body?.let { NewDeclarationNameValidator(it, sequenceOf(it), NewDeclarationNameValidator.Target.VARIABLES) } body?.let { NewDeclarationNameValidator(it, sequenceOf(it), NewDeclarationNameValidator.Target.VARIABLES) }
val nameValidator = CollectingNameValidator(targetParent.getValueParameters().mapNotNull { it.name }, bodyValidator ?: { true }) val nameValidator = CollectingNameValidator(targetParent.getValueParameters().mapNotNull { it.name }, bodyValidator ?: { true })
val suggestedNames = SmartList<String>().apply { val suggestedNames = SmartList<String>().apply {
@@ -277,18 +268,16 @@ open class KotlinIntroduceParameterHandler(
val occurrencesToReplace = if (expression is KtProperty) { val occurrencesToReplace = if (expression is KtProperty) {
ReferencesSearch.search(expression).mapNotNullTo(SmartList(expression.toRange())) { it.element.toRange() } ReferencesSearch.search(expression).mapNotNullTo(SmartList(expression.toRange())) { it.element.toRange() }
} } else {
else {
expression.toRange() expression.toRange()
.match(targetParent, KotlinPsiUnifier.DEFAULT) .match(targetParent, KotlinPsiUnifier.DEFAULT)
.asSequence() .asSequence()
.filterNot { .filterNot {
val textRange = it.range.getPhysicalTextRange() val textRange = it.range.getPhysicalTextRange()
forbiddenRanges.any { it.intersects(textRange) } forbiddenRanges.any { range -> range.intersects(textRange) }
} }
.mapNotNull { .mapNotNull {
val matchedElement = it.range.elements.singleOrNull() val matchedExpr = when (val matchedElement = it.range.elements.singleOrNull()) {
val matchedExpr = when (matchedElement) {
is KtExpression -> matchedElement is KtExpression -> matchedElement
is KtStringTemplateEntryWithExpression -> matchedElement.expression is KtStringTemplateEntryWithExpression -> matchedElement.expression
else -> null else -> null
@@ -299,80 +288,82 @@ open class KotlinIntroduceParameterHandler(
} }
project.executeCommand( project.executeCommand(
INTRODUCE_PARAMETER, INTRODUCE_PARAMETER,
null, null,
fun() { fun() {
val isTestMode = ApplicationManager.getApplication().isUnitTestMode val isTestMode = ApplicationManager.getApplication().isUnitTestMode
val haveLambdaArgumentsToReplace = occurrencesToReplace.any { val haveLambdaArgumentsToReplace = occurrencesToReplace.any { range ->
it.elements.any { it is KtLambdaExpression && it.parent is KtLambdaArgument } range.elements.any { it is KtLambdaExpression && it.parent is KtLambdaArgument }
}
val inplaceIsAvailable = editor.settings.isVariableInplaceRenameEnabled
&& !isTestMode
&& !haveLambdaArgumentsToReplace
&& expression.extractableSubstringInfo == null
&& !expression.mustBeParenthesizedInInitializerPosition()
val originalExpression = KtPsiUtil.safeDeparenthesize(expression)
val psiFactory = KtPsiFactory(project)
val introduceParameterDescriptor =
helper.configure(
IntroduceParameterDescriptor(
originalRange = originalExpression.toRange(),
callable = targetParent,
callableDescriptor = functionDescriptor,
newParameterName = suggestedNames.first().quoteIfNeeded(),
newParameterTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(replacementType),
argumentValue = originalExpression,
withDefaultValue = false,
parametersUsages = parametersUsages,
occurrencesToReplace = occurrencesToReplace,
occurrenceReplacer = replacer@ {
val expressionToReplace = it.elements.single() as KtExpression
val replacingExpression = psiFactory.createExpression(newParameterName)
val substringInfo = expressionToReplace.extractableSubstringInfo
val result = when {
expressionToReplace is KtProperty -> return@replacer expressionToReplace.delete()
expressionToReplace.isLambdaOutsideParentheses() -> {
expressionToReplace
.getStrictParentOfType<KtLambdaArgument>()!!
.moveInsideParenthesesAndReplaceWith(replacingExpression, context)
}
substringInfo != null -> substringInfo.replaceWith(replacingExpression)
else -> expressionToReplace.replaced(replacingExpression)
}
result.removeTemplateEntryBracesIfPossible()
}
)
)
if (isTestMode) {
introduceParameterDescriptor.performRefactoring()
return
}
if (inplaceIsAvailable) {
with(PsiDocumentManager.getInstance(project)) {
commitDocument(editor.document)
doPostponedOperationsAndUnblockDocument(editor.document)
}
val introducer = KotlinInplaceParameterIntroducer(introduceParameterDescriptor,
replacementType,
suggestedNames.toTypedArray(),
project,
editor)
if (introducer.startInplaceIntroduceTemplate()) return
}
val dialog = KotlinIntroduceParameterDialog(
project,
editor,
introduceParameterDescriptor,
suggestedNames.toTypedArray(),
listOf(replacementType) + replacementType.supertypes(),
helper
)
dialog.showWithTransaction()
} }
val inplaceIsAvailable = editor.settings.isVariableInplaceRenameEnabled
&& !isTestMode
&& !haveLambdaArgumentsToReplace
&& expression.extractableSubstringInfo == null
&& !expression.mustBeParenthesizedInInitializerPosition()
val originalExpression = KtPsiUtil.safeDeparenthesize(expression)
val psiFactory = KtPsiFactory(project)
val introduceParameterDescriptor =
helper.configure(
IntroduceParameterDescriptor(
originalRange = originalExpression.toRange(),
callable = targetParent,
callableDescriptor = functionDescriptor,
newParameterName = suggestedNames.first().quoteIfNeeded(),
newParameterTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(replacementType),
argumentValue = originalExpression,
withDefaultValue = false,
parametersUsages = parametersUsages,
occurrencesToReplace = occurrencesToReplace,
occurrenceReplacer = replacer@{
val expressionToReplace = it.elements.single() as KtExpression
val replacingExpression = psiFactory.createExpression(newParameterName)
val substringInfo = expressionToReplace.extractableSubstringInfo
val result = when {
expressionToReplace is KtProperty -> return@replacer expressionToReplace.delete()
expressionToReplace.isLambdaOutsideParentheses() -> {
expressionToReplace
.getStrictParentOfType<KtLambdaArgument>()!!
.moveInsideParenthesesAndReplaceWith(replacingExpression, context)
}
substringInfo != null -> substringInfo.replaceWith(replacingExpression)
else -> expressionToReplace.replaced(replacingExpression)
}
result.removeTemplateEntryBracesIfPossible()
}
)
)
if (isTestMode) {
introduceParameterDescriptor.performRefactoring()
return
}
if (inplaceIsAvailable) {
with(PsiDocumentManager.getInstance(project)) {
commitDocument(editor.document)
doPostponedOperationsAndUnblockDocument(editor.document)
}
val introducer = KotlinInplaceParameterIntroducer(
introduceParameterDescriptor,
replacementType,
suggestedNames.toTypedArray(),
project,
editor
)
if (introducer.startInplaceIntroduceTemplate()) return
}
val dialog = KotlinIntroduceParameterDialog(
project,
editor,
introduceParameterDescriptor,
suggestedNames.toTypedArray(),
listOf(replacementType) + replacementType.supertypes(),
helper
)
dialog.showWithTransaction()
}
) )
} }
@@ -398,8 +389,7 @@ open class KotlinIntroduceParameterHandler(
val expression = ((elements.singleOrNull() as? KtBlockExpression)?.statements ?: elements).singleOrNull() val expression = ((elements.singleOrNull() as? KtBlockExpression)?.statements ?: elements).singleOrNull()
if (expression is KtExpression) { if (expression is KtExpression) {
invoke(project, editor, expression, targetParent as KtNamedDeclaration) invoke(project, editor, expression, targetParent as KtNamedDeclaration)
} } else {
else {
showErrorHintByKey(project, editor, "cannot.refactor.no.expression", INTRODUCE_PARAMETER) showErrorHintByKey(project, editor, "cannot.refactor.no.expression", INTRODUCE_PARAMETER)
} }
} }
@@ -412,74 +402,75 @@ open class KotlinIntroduceParameterHandler(
private fun DeclarationDescriptor?.toFunctionDescriptor(targetParent: KtNamedDeclaration): FunctionDescriptor { private fun DeclarationDescriptor?.toFunctionDescriptor(targetParent: KtNamedDeclaration): FunctionDescriptor {
val functionDescriptor: FunctionDescriptor? = val functionDescriptor: FunctionDescriptor? =
when (this) { when (this) {
is FunctionDescriptor -> this is FunctionDescriptor -> this
is ClassDescriptor -> this.unsubstitutedPrimaryConstructor is ClassDescriptor -> this.unsubstitutedPrimaryConstructor
else -> null else -> null
} }
return functionDescriptor ?: throw AssertionError("Unexpected element type: ${targetParent.getElementTextWithContext()}") return functionDescriptor ?: throw AssertionError("Unexpected element type: ${targetParent.getElementTextWithContext()}")
} }
private fun findInternalUsagesOfParametersAndReceiver( private fun findInternalUsagesOfParametersAndReceiver(
targetParent: KtNamedDeclaration, targetParent: KtNamedDeclaration,
targetDescriptor: FunctionDescriptor targetDescriptor: FunctionDescriptor
): MultiMap<KtElement, KtElement>? { ): MultiMap<KtElement, KtElement>? {
val usages = MultiMap<KtElement, KtElement>() val usages = MultiMap<KtElement, KtElement>()
val searchComplete = targetParent.project.runSynchronouslyWithProgress("Searching usages of '${targetParent.name}' parameter", true) { val searchComplete = targetParent.project.runSynchronouslyWithProgress("Searching usages of '${targetParent.name}' parameter", true) {
runReadAction { runReadAction {
targetParent.getValueParameters() targetParent.getValueParameters()
.filter { !it.hasValOrVar() } .filter { !it.hasValOrVar() }
.forEach { .forEach {
val paramUsages = ReferencesSearch.search(it).map { it.element as KtElement } val paramUsages = ReferencesSearch.search(it).map { reference -> reference.element as KtElement }
if (paramUsages.isNotEmpty()) { if (paramUsages.isNotEmpty()) {
usages.put(it, paramUsages) usages.put(it, paramUsages)
}
} }
}
} }
} != null } != null
if (!searchComplete) return null if (!searchComplete) return null
val receiverTypeRef = (targetParent as? KtFunction)?.receiverTypeReference val receiverTypeRef = (targetParent as? KtFunction)?.receiverTypeReference
if (receiverTypeRef != null) { if (receiverTypeRef != null) {
targetParent.acceptChildren( targetParent.acceptChildren(
object : KtTreeVisitorVoid() { object : KtTreeVisitorVoid() {
override fun visitThisExpression(expression: KtThisExpression) { override fun visitThisExpression(expression: KtThisExpression) {
super.visitThisExpression(expression) super.visitThisExpression(expression)
if (expression.instanceReference.mainReference.resolve() == targetDescriptor) { if (expression.instanceReference.mainReference.resolve() == targetDescriptor) {
usages.putValue(receiverTypeRef, expression) usages.putValue(receiverTypeRef, expression)
}
}
override fun visitKtElement(element: KtElement) {
super.visitKtElement(element)
val resolvedCall = element.resolveToCall() ?: return
if ((resolvedCall.extensionReceiver as? ImplicitReceiver)?.declarationDescriptor == targetDescriptor ||
(resolvedCall.dispatchReceiver as? ImplicitReceiver)?.declarationDescriptor == targetDescriptor) {
usages.putValue(receiverTypeRef, resolvedCall.call.callElement)
}
} }
} }
override fun visitKtElement(element: KtElement) {
super.visitKtElement(element)
val resolvedCall = element.resolveToCall() ?: return
if ((resolvedCall.extensionReceiver as? ImplicitReceiver)?.declarationDescriptor == targetDescriptor ||
(resolvedCall.dispatchReceiver as? ImplicitReceiver)?.declarationDescriptor == targetDescriptor
) {
usages.putValue(receiverTypeRef, resolvedCall.call.callElement)
}
}
}
) )
} }
return usages return usages
} }
interface KotlinIntroduceLambdaParameterHelper: KotlinIntroduceParameterHelper { interface KotlinIntroduceLambdaParameterHelper : KotlinIntroduceParameterHelper {
object Default: KotlinIntroduceLambdaParameterHelper object Default : KotlinIntroduceLambdaParameterHelper
fun configureExtractLambda(descriptor: ExtractableCodeDescriptor): ExtractableCodeDescriptor = descriptor fun configureExtractLambda(descriptor: ExtractableCodeDescriptor): ExtractableCodeDescriptor = descriptor
} }
open class KotlinIntroduceLambdaParameterHandler( open class KotlinIntroduceLambdaParameterHandler(
helper: KotlinIntroduceLambdaParameterHelper = KotlinIntroduceLambdaParameterHelper.Default helper: KotlinIntroduceLambdaParameterHelper = KotlinIntroduceLambdaParameterHelper.Default
): KotlinIntroduceParameterHandler(helper) { ) : KotlinIntroduceParameterHandler(helper) {
private val extractLambdaHelper = object: ExtractionEngineHelper(INTRODUCE_LAMBDA_PARAMETER) { private val extractLambdaHelper = object : ExtractionEngineHelper(INTRODUCE_LAMBDA_PARAMETER) {
private fun createDialog( private fun createDialog(
project: Project, project: Project,
editor: Editor, editor: Editor,
lambdaExtractionDescriptor: ExtractableCodeDescriptor lambdaExtractionDescriptor: ExtractableCodeDescriptor
): KotlinIntroduceParameterDialog? { ): KotlinIntroduceParameterDialog? {
val callable = lambdaExtractionDescriptor.extractionData.targetSibling as KtNamedDeclaration val callable = lambdaExtractionDescriptor.extractionData.targetSibling as KtNamedDeclaration
val descriptor = callable.unsafeResolveToDescriptor() val descriptor = callable.unsafeResolveToDescriptor()
@@ -487,26 +478,26 @@ open class KotlinIntroduceLambdaParameterHandler(
val originalRange = lambdaExtractionDescriptor.extractionData.originalRange val originalRange = lambdaExtractionDescriptor.extractionData.originalRange
val parametersUsages = findInternalUsagesOfParametersAndReceiver(callable, callableDescriptor) ?: return null val parametersUsages = findInternalUsagesOfParametersAndReceiver(callable, callableDescriptor) ?: return null
val introduceParameterDescriptor = IntroduceParameterDescriptor( val introduceParameterDescriptor = IntroduceParameterDescriptor(
originalRange = originalRange, originalRange = originalRange,
callable = callable, callable = callable,
callableDescriptor = callableDescriptor, callableDescriptor = callableDescriptor,
newParameterName = "", // to be chosen in the dialog newParameterName = "", // to be chosen in the dialog
newParameterTypeText = "", // to be chosen in the dialog newParameterTypeText = "", // to be chosen in the dialog
argumentValue = KtPsiFactory(project).createExpression("{}"), // substituted later argumentValue = KtPsiFactory(project).createExpression("{}"), // substituted later
withDefaultValue = false, withDefaultValue = false,
parametersUsages = parametersUsages, parametersUsages = parametersUsages,
occurrencesToReplace = listOf(originalRange), occurrencesToReplace = listOf(originalRange),
parametersToRemove = listOf() parametersToRemove = listOf()
) )
return KotlinIntroduceParameterDialog(project, editor, introduceParameterDescriptor, lambdaExtractionDescriptor, helper) return KotlinIntroduceParameterDialog(project, editor, introduceParameterDescriptor, lambdaExtractionDescriptor, helper)
} }
override fun configureAndRun( override fun configureAndRun(
project: Project, project: Project,
editor: Editor, editor: Editor,
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts, descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
onFinish: (ExtractionResult) -> Unit onFinish: (ExtractionResult) -> Unit
) { ) {
val lambdaExtractionDescriptor = helper.configureExtractLambda(descriptorWithConflicts.descriptor) val lambdaExtractionDescriptor = helper.configureExtractLambda(descriptorWithConflicts.descriptor)
if (!ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION.isAvailable(lambdaExtractionDescriptor)) { if (!ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION.isAvailable(lambdaExtractionDescriptor)) {
@@ -517,8 +508,7 @@ open class KotlinIntroduceLambdaParameterHandler(
val dialog = createDialog(project, editor, lambdaExtractionDescriptor) ?: return val dialog = createDialog(project, editor, lambdaExtractionDescriptor) ?: return
if (ApplicationManager.getApplication()!!.isUnitTestMode) { if (ApplicationManager.getApplication()!!.isUnitTestMode) {
dialog.performRefactoring() dialog.performRefactoring()
} } else {
else {
dialog.showWithTransaction() dialog.showWithTransaction()
} }
} }
@@ -526,21 +516,23 @@ open class KotlinIntroduceLambdaParameterHandler(
override fun invoke(project: Project, editor: Editor, expression: KtExpression, targetParent: KtNamedDeclaration) { override fun invoke(project: Project, editor: Editor, expression: KtExpression, targetParent: KtNamedDeclaration) {
val duplicateContainer = val duplicateContainer =
when (targetParent) { when (targetParent) {
is KtFunction -> targetParent.bodyExpression is KtFunction -> targetParent.bodyExpression
is KtClass -> targetParent.getBody() is KtClass -> targetParent.body
else -> null else -> null
} ?: throw AssertionError("Body element is not found: ${targetParent.getElementTextWithContext()}") } ?: throw AssertionError("Body element is not found: ${targetParent.getElementTextWithContext()}")
val extractionData = ExtractionData(targetParent.containingKtFile, val extractionData = ExtractionData(
expression.toRange(), targetParent.containingKtFile,
targetParent, expression.toRange(),
duplicateContainer, targetParent,
ExtractionOptions.DEFAULT) duplicateContainer,
ExtractionOptions.DEFAULT
)
ExtractionEngine(extractLambdaHelper).run(editor, extractionData) ExtractionEngine(extractLambdaHelper).run(editor, extractionData)
} }
} }
val INTRODUCE_PARAMETER_REFACTORING_ID: String = "kotlin.refactoring.introduceParameter" const val INTRODUCE_PARAMETER_REFACTORING_ID: String = "kotlin.refactoring.introduceParameter"
val INTRODUCE_PARAMETER: String = "Introduce Parameter" const val INTRODUCE_PARAMETER: String = "Introduce Parameter"
val INTRODUCE_LAMBDA_PARAMETER: String = "Introduce Lambda Parameter" const val INTRODUCE_LAMBDA_PARAMETER: String = "Introduce Lambda Parameter"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.pullUp package org.jetbrains.kotlin.idea.refactoring.pullUp
@@ -21,7 +10,6 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiNamedElement import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.HelpID import com.intellij.refactoring.HelpID
@@ -44,12 +32,12 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.types.typeUtil.supertypes
class KotlinPullUpHandler : AbstractPullPushMembersHandler( class KotlinPullUpHandler : AbstractPullPushMembersHandler(
refactoringName = PULL_MEMBERS_UP, refactoringName = PULL_MEMBERS_UP,
helpId = HelpID.MEMBERS_PULL_UP, helpId = HelpID.MEMBERS_PULL_UP,
wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from") wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")
) { ) {
companion object { companion object {
val PULL_UP_TEST_HELPER_KEY = "PULL_UP_TEST_HELPER_KEY" const val PULL_UP_TEST_HELPER_KEY = "PULL_UP_TEST_HELPER_KEY"
} }
interface TestHelper { interface TestHelper {
@@ -59,17 +47,21 @@ class KotlinPullUpHandler : AbstractPullPushMembersHandler(
private fun reportNoSuperClasses(project: Project, editor: Editor?, classOrObject: KtClassOrObject) { private fun reportNoSuperClasses(project: Project, editor: Editor?, classOrObject: KtClassOrObject) {
val message = RefactoringBundle.getCannotRefactorMessage( val message = RefactoringBundle.getCannotRefactorMessage(
RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project", RefactoringBundle.message(
classOrObject.qualifiedClassNameForRendering()) "class.does.not.have.base.classes.interfaces.in.current.project",
classOrObject.qualifiedClassNameForRendering()
)
) )
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP) CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
} }
override fun invoke(project: Project, override fun invoke(
editor: Editor?, project: Project,
classOrObject: KtClassOrObject?, editor: Editor?,
member: KtNamedDeclaration?, classOrObject: KtClassOrObject?,
dataContext: DataContext?) { member: KtNamedDeclaration?,
dataContext: DataContext?
) {
if (classOrObject == null) { if (classOrObject == null) {
reportWrongContext(project, editor) reportWrongContext(project, editor)
return return
@@ -77,21 +69,23 @@ class KotlinPullUpHandler : AbstractPullPushMembersHandler(
val classDescriptor = classOrObject.unsafeResolveToDescriptor() as ClassDescriptor val classDescriptor = classOrObject.unsafeResolveToDescriptor() as ClassDescriptor
val superClasses = classDescriptor.defaultType val superClasses = classDescriptor.defaultType
.supertypes() .supertypes()
.mapNotNull { .mapNotNull {
val descriptor = it.constructor.declarationDescriptor val descriptor = it.constructor.declarationDescriptor
val declaration = descriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) } val declaration = descriptor?.let { classifierDescriptor ->
if ((declaration is KtClass || declaration is PsiClass) DescriptorToSourceUtilsIde.getAnyDeclaration(project, classifierDescriptor)
&& declaration.canRefactor()) declaration as PsiNamedElement else null
} }
.sortedBy { it.qualifiedClassNameForRendering() } if ((declaration is KtClass || declaration is PsiClass)
&& declaration.canRefactor()
) declaration as PsiNamedElement else null
}
.sortedBy { it.qualifiedClassNameForRendering() }
if (superClasses.isEmpty()) { if (superClasses.isEmpty()) {
val containingClass = classOrObject.getStrictParentOfType<KtClassOrObject>() val containingClass = classOrObject.getStrictParentOfType<KtClassOrObject>()
if (containingClass != null) { if (containingClass != null) {
invoke(project, editor, containingClass, classOrObject, dataContext) invoke(project, editor, containingClass, classOrObject, dataContext)
} } else {
else {
reportNoSuperClasses(project, editor, classOrObject) reportNoSuperClasses(project, editor, classOrObject)
} }
return return
@@ -107,8 +101,7 @@ class KotlinPullUpHandler : AbstractPullPushMembersHandler(
checkConflicts(project, classOrObject, targetClass, selectedMembers) { checkConflicts(project, classOrObject, targetClass, selectedMembers) {
KotlinPullUpDialog.createProcessor(classOrObject, targetClass, selectedMembers).run() KotlinPullUpDialog.createProcessor(classOrObject, targetClass, selectedMembers).run()
} }
} } else {
else {
val manager = classOrObject.manager val manager = classOrObject.manager
members.filter { manager.areElementsEquivalent(it.member, member) }.forEach { it.isChecked = true } members.filter { manager.areElementsEquivalent(it.member, member) }.forEach { it.isChecked = true }
@@ -122,4 +115,4 @@ class KotlinPullUpHandler : AbstractPullPushMembersHandler(
} }
} }
val PULL_MEMBERS_UP = "Pull Members Up" const val PULL_MEMBERS_UP = "Pull Members Up"
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.pushDown package org.jetbrains.kotlin.idea.refactoring.pushDown
@@ -20,7 +9,6 @@ import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import com.intellij.refactoring.HelpID import com.intellij.refactoring.HelpID
@@ -39,15 +27,15 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtParameter
val PUSH_MEMBERS_DOWN = "Push Members Down" const val PUSH_MEMBERS_DOWN = "Push Members Down"
class KotlinPushDownHandler : AbstractPullPushMembersHandler( class KotlinPushDownHandler : AbstractPullPushMembersHandler(
refactoringName = PUSH_MEMBERS_DOWN, refactoringName = PUSH_MEMBERS_DOWN,
helpId = HelpID.MEMBERS_PUSH_DOWN, helpId = HelpID.MEMBERS_PUSH_DOWN,
wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.push.members.from") wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.push.members.from")
) { ) {
companion object { companion object {
val PUSH_DOWN_TEST_HELPER_KEY = "PUSH_DOWN_TEST_HELPER_KEY" const val PUSH_DOWN_TEST_HELPER_KEY = "PUSH_DOWN_TEST_HELPER_KEY"
} }
interface TestHelper { interface TestHelper {
@@ -56,16 +44,18 @@ class KotlinPushDownHandler : AbstractPullPushMembersHandler(
private fun reportFinalClassOrObject(project: Project, editor: Editor?, classOrObject: KtClassOrObject) { private fun reportFinalClassOrObject(project: Project, editor: Editor?, classOrObject: KtClassOrObject) {
val message = RefactoringBundle.getCannotRefactorMessage( val message = RefactoringBundle.getCannotRefactorMessage(
"${RefactoringUIUtil.getDescription(classOrObject, false)} is final".capitalize() "${RefactoringUIUtil.getDescription(classOrObject, false)} is final".capitalize()
) )
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP) CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
} }
override fun invoke(project: Project, override fun invoke(
editor: Editor?, project: Project,
classOrObject: KtClassOrObject?, editor: Editor?,
member: KtNamedDeclaration?, classOrObject: KtClassOrObject?,
dataContext: DataContext?) { member: KtNamedDeclaration?,
dataContext: DataContext?
) {
if (classOrObject == null) { if (classOrObject == null) {
reportWrongContext(project, editor) reportWrongContext(project, editor)
return return
@@ -81,8 +71,7 @@ class KotlinPushDownHandler : AbstractPullPushMembersHandler(
val helper = dataContext?.getData(PUSH_DOWN_TEST_HELPER_KEY) as TestHelper val helper = dataContext?.getData(PUSH_DOWN_TEST_HELPER_KEY) as TestHelper
val selectedMembers = helper.adjustMembers(members) val selectedMembers = helper.adjustMembers(members)
KotlinPushDownProcessor(project, classOrObject, selectedMembers).run() KotlinPushDownProcessor(project, classOrObject, selectedMembers).run()
} } else {
else {
val manager = PsiManager.getInstance(project) val manager = PsiManager.getInstance(project)
members.filter { manager.areElementsEquivalent(it.member, member) }.forEach { it.isChecked = true } members.filter { manager.areElementsEquivalent(it.member, member) }.forEach { it.isChecked = true }
KotlinPushDownDialog(project, members, classOrObject).show() KotlinPushDownDialog(project, members, classOrObject).show()