Introduce Variable: Support destructuring declarations
#KT-6262
This commit is contained in:
@@ -199,6 +199,10 @@ public class KtPsiFactory(private val project: Project) {
|
|||||||
return getter
|
return getter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun createMultiDeclaration(text: String): KtMultiDeclaration {
|
||||||
|
return (createFunction("fun foo() {$text}").bodyExpression as KtBlockExpression).statements.first() as KtMultiDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
public fun <TDeclaration : KtDeclaration> createDeclaration(text: String): TDeclaration {
|
public fun <TDeclaration : KtDeclaration> createDeclaration(text: String): TDeclaration {
|
||||||
val file = createFile(text)
|
val file = createFile(text)
|
||||||
val declarations = file.getDeclarations()
|
val declarations = file.getDeclarations()
|
||||||
|
|||||||
+140
-44
@@ -16,30 +16,31 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable
|
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
|
import com.intellij.codeInsight.template.*
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.command.impl.FinishMarkAction
|
||||||
|
import com.intellij.openapi.command.impl.StartMarkAction
|
||||||
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.openapi.ui.Messages
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.psi.PsiWhiteSpace
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.intellij.refactoring.HelpID
|
import com.intellij.refactoring.HelpID
|
||||||
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser
|
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser
|
||||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
import org.jetbrains.kotlin.idea.analysis.computeTypeInfoInContext
|
import org.jetbrains.kotlin.idea.analysis.computeTypeInfoInContext
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
import org.jetbrains.kotlin.idea.core.*
|
||||||
import org.jetbrains.kotlin.idea.core.compareDescriptors
|
|
||||||
import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith
|
|
||||||
import org.jetbrains.kotlin.idea.core.refactoring.Pass
|
import org.jetbrains.kotlin.idea.core.refactoring.Pass
|
||||||
import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary
|
import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary
|
||||||
import org.jetbrains.kotlin.idea.core.refactoring.removeTemplateEntryBracesIfPossible
|
import org.jetbrains.kotlin.idea.core.refactoring.removeTemplateEntryBracesIfPossible
|
||||||
@@ -51,6 +52,7 @@ import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
|||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
||||||
@@ -68,6 +70,7 @@ import org.jetbrains.kotlin.resolve.source.getPsi
|
|||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||||
import org.jetbrains.kotlin.utils.ifEmpty
|
import org.jetbrains.kotlin.utils.ifEmpty
|
||||||
import org.jetbrains.kotlin.utils.sure
|
import org.jetbrains.kotlin.utils.sure
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -83,19 +86,20 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
|
|
||||||
private class IntroduceVariableContext(
|
private class IntroduceVariableContext(
|
||||||
private val expression: KtExpression,
|
private val expression: KtExpression,
|
||||||
private val nameSuggestion: String,
|
private val nameSuggestions: List<Collection<String>>,
|
||||||
private val allReplaces: List<KtExpression>,
|
private val allReplaces: List<KtExpression>,
|
||||||
private val commonContainer: PsiElement,
|
private val commonContainer: PsiElement,
|
||||||
private val commonParent: PsiElement,
|
private val commonParent: PsiElement,
|
||||||
private val replaceOccurrence: Boolean,
|
private val replaceOccurrence: Boolean,
|
||||||
private val noTypeInference: Boolean,
|
private val noTypeInference: Boolean,
|
||||||
private val expressionType: KotlinType?,
|
private val expressionType: KotlinType?,
|
||||||
|
private val componentFunctions: List<FunctionDescriptor>,
|
||||||
private val bindingContext: BindingContext,
|
private val bindingContext: BindingContext,
|
||||||
private val resolutionFacade: ResolutionFacade
|
private val resolutionFacade: ResolutionFacade
|
||||||
) {
|
) {
|
||||||
private val psiFactory = KtPsiFactory(expression)
|
private val psiFactory = KtPsiFactory(expression)
|
||||||
|
|
||||||
var propertyRef: KtProperty? = null
|
var propertyRef: KtDeclaration? = null
|
||||||
var reference: KtExpression? = null
|
var reference: KtExpression? = null
|
||||||
val references = ArrayList<KtExpression>()
|
val references = ArrayList<KtExpression>()
|
||||||
|
|
||||||
@@ -106,7 +110,7 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
private fun replaceExpression(expressionToReplace: KtExpression, addToReferences: Boolean): KtExpression {
|
private fun replaceExpression(expressionToReplace: KtExpression, addToReferences: Boolean): KtExpression {
|
||||||
val isActualExpression = expression == expressionToReplace
|
val isActualExpression = expression == expressionToReplace
|
||||||
|
|
||||||
val replacement = psiFactory.createExpression(nameSuggestion)
|
val replacement = psiFactory.createExpression(nameSuggestions.single().first())
|
||||||
val substringInfo = expressionToReplace.extractableSubstringInfo
|
val substringInfo = expressionToReplace.extractableSubstringInfo
|
||||||
var result = when {
|
var result = when {
|
||||||
expressionToReplace.isFunctionLiteralOutsideParentheses() -> {
|
expressionToReplace.isFunctionLiteralOutsideParentheses() -> {
|
||||||
@@ -133,22 +137,31 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
commonParent: PsiElement,
|
commonParent: PsiElement,
|
||||||
allReplaces: List<KtExpression>
|
allReplaces: List<KtExpression>
|
||||||
) {
|
) {
|
||||||
val variableText = StringBuilder("val ").apply {
|
val initializer = (expression as? KtParenthesizedExpression)?.expression ?: expression
|
||||||
append(nameSuggestion)
|
var property: KtDeclaration = if (componentFunctions.isNotEmpty()) {
|
||||||
if (noTypeInference) {
|
buildString {
|
||||||
val typeToRender = expressionType ?: resolutionFacade.moduleDescriptor.builtIns.anyType
|
componentFunctions.indices.joinTo(this, prefix = "val (", postfix = ")") { nameSuggestions[it].first() }
|
||||||
append(": ").append(IdeDescriptorRenderers.SOURCE_CODE.renderType(typeToRender))
|
append(" = ")
|
||||||
}
|
append(initializer.text)
|
||||||
append(" = ")
|
}.let { psiFactory.createMultiDeclaration(it) }
|
||||||
|
}
|
||||||
append(((expression as? KtParenthesizedExpression)?.expression ?: expression).text)
|
else {
|
||||||
}.toString()
|
buildString {
|
||||||
var property = psiFactory.createProperty(variableText)
|
append("val ")
|
||||||
|
append(nameSuggestions.single().first())
|
||||||
|
if (noTypeInference) {
|
||||||
|
val typeToRender = expressionType ?: resolutionFacade.moduleDescriptor.builtIns.anyType
|
||||||
|
append(": ").append(IdeDescriptorRenderers.SOURCE_CODE.renderType(typeToRender))
|
||||||
|
}
|
||||||
|
append(" = ")
|
||||||
|
append(initializer.text)
|
||||||
|
}.let { psiFactory.createProperty(it) }
|
||||||
|
}
|
||||||
|
|
||||||
var anchor = calculateAnchor(commonParent, commonContainer, allReplaces) ?: return
|
var anchor = calculateAnchor(commonParent, commonContainer, allReplaces) ?: return
|
||||||
val needBraces = commonContainer !is KtBlockExpression
|
val needBraces = commonContainer !is KtBlockExpression
|
||||||
if (!needBraces) {
|
if (!needBraces) {
|
||||||
property = commonContainer.addBefore(property, anchor) as KtProperty
|
property = commonContainer.addBefore(property, anchor) as KtDeclaration
|
||||||
commonContainer.addBefore(psiFactory.createNewLine(), anchor)
|
commonContainer.addBefore(psiFactory.createNewLine(), anchor)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -365,6 +378,70 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
CommonRefactoringUtil.showErrorHint(project, editor, message, INTRODUCE_VARIABLE, HelpID.INTRODUCE_VARIABLE)
|
CommonRefactoringUtil.showErrorHint(project, editor, message, INTRODUCE_VARIABLE, HelpID.INTRODUCE_VARIABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.chooseApplicableComponentFunctions(haveOccurrencesToReplace: Boolean): List<FunctionDescriptor>? {
|
||||||
|
if (haveOccurrencesToReplace) return emptyList()
|
||||||
|
|
||||||
|
val functions = getApplicableComponentFunctions(this)
|
||||||
|
if (functions.size <= 1) return emptyList()
|
||||||
|
|
||||||
|
return functions
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun executeMultiDeclarationTemplate(
|
||||||
|
project: Project,
|
||||||
|
editor: Editor,
|
||||||
|
declaration: KtMultiDeclaration,
|
||||||
|
suggestedNames: List<Collection<String>>) {
|
||||||
|
StartMarkAction.canStart(project)?.let { return }
|
||||||
|
|
||||||
|
val builder = TemplateBuilderImpl(declaration)
|
||||||
|
for ((index, entry) in declaration.entries.withIndex()) {
|
||||||
|
val templateExpression = object : Expression() {
|
||||||
|
private val lookupItems = suggestedNames[index].map { LookupElementBuilder.create(it) }.toTypedArray()
|
||||||
|
|
||||||
|
override fun calculateQuickResult(context: ExpressionContext?) = TextResult(suggestedNames[index].first())
|
||||||
|
|
||||||
|
override fun calculateResult(context: ExpressionContext?) = calculateQuickResult(context)
|
||||||
|
|
||||||
|
override fun calculateLookupItems(context: ExpressionContext?) = lookupItems
|
||||||
|
}
|
||||||
|
builder.replaceElement(entry, templateExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
val startMarkAction = StartMarkAction.start(editor, project, INTRODUCE_VARIABLE)
|
||||||
|
editor.caretModel.moveToOffset(declaration.startOffset)
|
||||||
|
|
||||||
|
project.executeWriteCommand(INTRODUCE_VARIABLE) {
|
||||||
|
TemplateManager.getInstance(project).startTemplate(
|
||||||
|
editor,
|
||||||
|
builder.buildInlineTemplate(),
|
||||||
|
object: TemplateEditingAdapter() {
|
||||||
|
private fun finishMarkAction() {
|
||||||
|
FinishMarkAction.finish(project, editor, startMarkAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun templateFinished(template: Template?, brokenOff: Boolean) {
|
||||||
|
finishMarkAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun templateCancelled(template: Template?) {
|
||||||
|
finishMarkAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun suggestNamesForComponent(descriptor: FunctionDescriptor, project: Project, validator: (String) -> Boolean): Set<String> {
|
||||||
|
return LinkedHashSet<String>().apply {
|
||||||
|
descriptor.returnType?.let { addAll(KotlinNameSuggester.suggestNamesByType(it, validator)) }
|
||||||
|
|
||||||
|
val componentName = (DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) as? PsiNamedElement)?.name
|
||||||
|
?: descriptor.name.asString()
|
||||||
|
add(KotlinNameSuggester.suggestNameByName(componentName, validator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun doRefactoring(
|
fun doRefactoring(
|
||||||
project: Project,
|
project: Project,
|
||||||
editor: Editor?,
|
editor: Editor?,
|
||||||
@@ -374,7 +451,7 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
resolutionFacade: ResolutionFacade,
|
resolutionFacade: ResolutionFacade,
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
occurrencesToReplace: List<KtExpression>?,
|
occurrencesToReplace: List<KtExpression>?,
|
||||||
onNonInteractiveFinish: ((KtProperty) -> Unit)?
|
onNonInteractiveFinish: ((KtDeclaration) -> Unit)?
|
||||||
) {
|
) {
|
||||||
val substringInfo = expression.extractableSubstringInfo
|
val substringInfo = expression.extractableSubstringInfo
|
||||||
val physicalExpression = expression.substringContextOrThis
|
val physicalExpression = expression.substringContextOrThis
|
||||||
@@ -440,19 +517,27 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
commonContainer = container
|
commonContainer = container
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val componentFunctions = physicalExpression.chooseApplicableComponentFunctions(replaceOccurrence) ?: return@Pass
|
||||||
|
|
||||||
val validator = NewDeclarationNameValidator(
|
val validator = NewDeclarationNameValidator(
|
||||||
commonContainer,
|
commonContainer,
|
||||||
calculateAnchor(commonParent, commonContainer, allReplaces),
|
calculateAnchor(commonParent, commonContainer, allReplaces),
|
||||||
NewDeclarationNameValidator.Target.VARIABLES
|
NewDeclarationNameValidator.Target.VARIABLES
|
||||||
)
|
)
|
||||||
val suggestedNames = KotlinNameSuggester.suggestNamesByExpressionAndType(expression,
|
val suggestedNames = if (componentFunctions.isNotEmpty()) {
|
||||||
substringInfo?.type,
|
val collectingValidator = CollectingNameValidator(filter = validator)
|
||||||
bindingContext,
|
componentFunctions.map { suggestNamesForComponent(it, project, collectingValidator) }
|
||||||
validator,
|
}
|
||||||
"value")
|
else {
|
||||||
|
KotlinNameSuggester.suggestNamesByExpressionAndType(expression,
|
||||||
|
substringInfo?.type,
|
||||||
|
bindingContext,
|
||||||
|
validator,
|
||||||
|
"value").singletonList()
|
||||||
|
}
|
||||||
val introduceVariableContext = IntroduceVariableContext(
|
val introduceVariableContext = IntroduceVariableContext(
|
||||||
expression, suggestedNames.iterator().next(), allReplaces, commonContainer, commonParent,
|
expression, suggestedNames, allReplaces, commonContainer, commonParent,
|
||||||
replaceOccurrence, noTypeInference, expressionType, bindingContext, resolutionFacade
|
replaceOccurrence, noTypeInference, expressionType, componentFunctions, bindingContext, resolutionFacade
|
||||||
)
|
)
|
||||||
project.executeCommand(INTRODUCE_VARIABLE, null) {
|
project.executeCommand(INTRODUCE_VARIABLE, null) {
|
||||||
runWriteAction { introduceVariableContext.runRefactoring() }
|
runWriteAction { introduceVariableContext.runRefactoring() }
|
||||||
@@ -471,18 +556,29 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
|
|
||||||
PsiDocumentManager.getInstance(project).commitDocument(editor.document)
|
PsiDocumentManager.getInstance(project).commitDocument(editor.document)
|
||||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||||
KotlinVariableInplaceIntroducer(
|
|
||||||
property,
|
when (property) {
|
||||||
introduceVariableContext.reference,
|
is KtProperty -> {
|
||||||
introduceVariableContext.references.toTypedArray(),
|
KotlinVariableInplaceIntroducer(
|
||||||
suggestedNames,
|
property,
|
||||||
/*todo*/ false,
|
introduceVariableContext.reference,
|
||||||
/*todo*/ false,
|
introduceVariableContext.references.toTypedArray(),
|
||||||
expressionType,
|
suggestedNames.single(),
|
||||||
noTypeInference,
|
/*todo*/ false,
|
||||||
project,
|
/*todo*/ false,
|
||||||
editor
|
expressionType,
|
||||||
).startInplaceIntroduceTemplate()
|
noTypeInference,
|
||||||
|
project,
|
||||||
|
editor
|
||||||
|
).startInplaceIntroduceTemplate()
|
||||||
|
}
|
||||||
|
|
||||||
|
is KtMultiDeclaration -> {
|
||||||
|
executeMultiDeclarationTemplate(project, editor, property, suggestedNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw AssertionError("Unexpected declaration: ${property.getElementTextWithContext()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,7 +664,7 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
|||||||
editor: Editor?,
|
editor: Editor?,
|
||||||
expressionToExtract: KtExpression?,
|
expressionToExtract: KtExpression?,
|
||||||
occurrencesToReplace: List<KtExpression>?,
|
occurrencesToReplace: List<KtExpression>?,
|
||||||
onNonInteractiveFinish: ((KtProperty) -> Unit)?
|
onNonInteractiveFinish: ((KtDeclaration) -> Unit)?
|
||||||
) {
|
) {
|
||||||
val expression = expressionToExtract?.let { KtPsiUtil.safeDeparenthesize(it) }
|
val expression = expressionToExtract?.let { KtPsiUtil.safeDeparenthesize(it) }
|
||||||
?: return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression"))
|
?: return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression"))
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.util.isValidOperator
|
||||||
|
|
||||||
|
fun getApplicableComponentFunctions(expression: KtExpression): List<FunctionDescriptor> {
|
||||||
|
val facade = expression.getResolutionFacade()
|
||||||
|
val scope = expression.getResolutionScope(facade.analyze(expression), facade)
|
||||||
|
|
||||||
|
val psiFactory = KtPsiFactory(expression)
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return sequence(1) { it + 1 }
|
||||||
|
.map {
|
||||||
|
val componentCallExpr = psiFactory.createExpressionByPattern("$0.$1", expression, "component$it()")
|
||||||
|
val newContext = componentCallExpr.analyzeInContext(scope, expression)
|
||||||
|
componentCallExpr.getResolvedCall(newContext)?.resultingDescriptor as? FunctionDescriptor
|
||||||
|
}
|
||||||
|
.takeWhile { it != null && it.isValidOperator() }
|
||||||
|
.toList() as List<FunctionDescriptor>
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun A.component1() = 1
|
||||||
|
fun A.component2() = 2
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
<selection>A()</selection>
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun A.component1() = 1
|
||||||
|
fun A.component2() = 2
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a = A()
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
operator fun A.component1() = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
<selection>A()</selection>
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
operator fun A.component1() = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a = A()
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
operator fun A.component1() = 1
|
||||||
|
operator fun A.component2() = 2
|
||||||
|
|
||||||
|
class B {
|
||||||
|
operator fun A.component3() = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
<selection>A()</selection>
|
||||||
|
}
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
operator fun A.component1() = 1
|
||||||
|
operator fun A.component2() = 2
|
||||||
|
|
||||||
|
class B {
|
||||||
|
operator fun A.component3() = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val (i, i1) = A()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test() {
|
||||||
|
<selection>1 to 2</selection>
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test() {
|
||||||
|
val (i, i1) = 1 to 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test() {
|
||||||
|
(<selection>1 to 2</selection>).first
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun test() {
|
||||||
|
val pair = 1 to 2
|
||||||
|
pair.first
|
||||||
|
}
|
||||||
+39
@@ -508,6 +508,45 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/refactoring/introduceVariable/multiDeclarations")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class MultiDeclarations extends AbstractExtractionTest {
|
||||||
|
public void testAllFilesPresentInMultiDeclarations() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceVariable/multiDeclarations"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notOperators.kt")
|
||||||
|
public void testNotOperators() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/multiDeclarations/notOperators.kt");
|
||||||
|
doIntroduceVariableTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("singleComponent.kt")
|
||||||
|
public void testSingleComponent() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/multiDeclarations/singleComponent.kt");
|
||||||
|
doIntroduceVariableTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unresolvedComponent3.kt")
|
||||||
|
public void testUnresolvedComponent3() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/multiDeclarations/unresolvedComponent3.kt");
|
||||||
|
doIntroduceVariableTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unusedExpr.kt")
|
||||||
|
public void testUnusedExpr() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/multiDeclarations/unusedExpr.kt");
|
||||||
|
doIntroduceVariableTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("usedExpr.kt")
|
||||||
|
public void testUsedExpr() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/multiDeclarations/usedExpr.kt");
|
||||||
|
doIntroduceVariableTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/refactoring/introduceVariable/stringTemplates")
|
@TestMetadata("idea/testData/refactoring/introduceVariable/stringTemplates")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user