Introduce Type Parameter
#KT-13155 Fixed
This commit is contained in:
@@ -118,6 +118,10 @@ These artifacts include extensions for the types available in the latter JDKs, s
|
|||||||
- [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Report separate conflicts for each property accessor
|
- [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Report separate conflicts for each property accessor
|
||||||
- [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Forbid moving of enum entries
|
- [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Forbid moving of enum entries
|
||||||
|
|
||||||
|
##### New features
|
||||||
|
|
||||||
|
- [`KT-13155`](https://youtrack.jetbrains.com/issue/KT-13155) Implement "Introduce Type Parameter" refactoring
|
||||||
|
|
||||||
## 1.0.4
|
## 1.0.4
|
||||||
|
|
||||||
### Compiler
|
### Compiler
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
|||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.takeSnapshot
|
import org.jetbrains.kotlin.resolve.scopes.utils.takeSnapshot
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||||
|
|
||||||
operator fun <K, V: Any> BindingContext.get(slice: ReadOnlySlice<K, V>, key: K): V? = get(slice, key)
|
operator fun <K, V: Any> BindingContext.get(slice: ReadOnlySlice<K, V>, key: K): V? = get(slice, key)
|
||||||
@@ -99,3 +101,15 @@ fun KtExpression.getReferenceTargets(context: BindingContext): Collection<Declar
|
|||||||
|
|
||||||
fun KtTypeReference.getAbbreviatedTypeOrType(context: BindingContext) =
|
fun KtTypeReference.getAbbreviatedTypeOrType(context: BindingContext) =
|
||||||
context[BindingContext.ABBREVIATED_TYPE, this] ?: context[BindingContext.TYPE, this]
|
context[BindingContext.ABBREVIATED_TYPE, this] ?: context[BindingContext.TYPE, this]
|
||||||
|
|
||||||
|
fun KtTypeElement.getAbbreviatedTypeOrType(context: BindingContext): KotlinType? {
|
||||||
|
val parent = parent
|
||||||
|
return when (parent) {
|
||||||
|
is KtTypeReference -> parent.getAbbreviatedTypeOrType(context)
|
||||||
|
is KtNullableType -> {
|
||||||
|
val outerType = parent.getAbbreviatedTypeOrType(context)
|
||||||
|
if (this is KtNullableType) outerType else outerType?.makeNotNullable()
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -782,6 +782,7 @@ fun main(args: Array<String>) {
|
|||||||
model("refactoring/introduceParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceSimpleParameterTest")
|
model("refactoring/introduceParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceSimpleParameterTest")
|
||||||
model("refactoring/introduceLambdaParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceLambdaParameterTest")
|
model("refactoring/introduceLambdaParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceLambdaParameterTest")
|
||||||
model("refactoring/introduceJavaParameter", extension = "java", testMethod = "doIntroduceJavaParameterTest")
|
model("refactoring/introduceJavaParameter", extension = "java", testMethod = "doIntroduceJavaParameterTest")
|
||||||
|
model("refactoring/introduceTypeParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeParameterTest")
|
||||||
model("refactoring/introduceTypeAlias", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeAliasTest")
|
model("refactoring/introduceTypeAlias", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeAliasTest")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,10 +162,16 @@
|
|||||||
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractFunction"/>
|
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractFunction"/>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action id="IntroduceTypeParameter" class="org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeParameter.IntroduceTypeParameterAction"
|
||||||
|
text="T_ype Parameter...">
|
||||||
|
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift Y"/>
|
||||||
|
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractFunctionToScope"/>
|
||||||
|
</action>
|
||||||
|
|
||||||
<action id="IntroduceTypeAlias" class="org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias.IntroduceTypeAliasAction"
|
<action id="IntroduceTypeAlias" class="org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias.IntroduceTypeAliasAction"
|
||||||
text="Type _Alias...">
|
text="Type _Alias...">
|
||||||
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift A"/>
|
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift A"/>
|
||||||
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractFunctionToScope"/>
|
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceTypeParameter"/>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
<!-- Kotlin Console REPL-->
|
<!-- Kotlin Console REPL-->
|
||||||
|
|||||||
+8
-4
@@ -53,18 +53,22 @@ object CreateTypeParameterByRefActionFactory : KotlinIntentionActionFactoryWithD
|
|||||||
return ktUserType
|
return ktUserType
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun extractFixData(element: KtUserType, diagnostic: Diagnostic): CreateTypeParameterData? {
|
fun extractFixData(element: KtTypeElement, newName: String): CreateTypeParameterData? {
|
||||||
val name = element.referencedName ?: return null
|
|
||||||
val declaration = element.parents.firstOrNull {
|
val declaration = element.parents.firstOrNull {
|
||||||
it is KtProperty || it is KtNamedFunction || it is KtClass
|
it is KtProperty || it is KtNamedFunction || it is KtClass
|
||||||
} as? KtTypeParameterListOwner ?: return null
|
} as? KtTypeParameterListOwner ?: return null
|
||||||
val containingDescriptor = declaration.resolveToDescriptor()
|
val containingDescriptor = declaration.resolveToDescriptor()
|
||||||
val fakeTypeParameter = createFakeTypeParameterDescriptor(containingDescriptor, name)
|
val fakeTypeParameter = createFakeTypeParameterDescriptor(containingDescriptor, newName)
|
||||||
val upperBoundType = getUnsubstitutedTypeConstraintInfo(element)?.let {
|
val upperBoundType = getUnsubstitutedTypeConstraintInfo(element)?.let {
|
||||||
it.performSubstitution(it.typeParameter.typeConstructor to TypeProjectionImpl(fakeTypeParameter.defaultType))?.upperBound
|
it.performSubstitution(it.typeParameter.typeConstructor to TypeProjectionImpl(fakeTypeParameter.defaultType))?.upperBound
|
||||||
}
|
}
|
||||||
if (upperBoundType != null && upperBoundType.containsError()) return null
|
if (upperBoundType != null && upperBoundType.containsError()) return null
|
||||||
return CreateTypeParameterData(name, declaration, upperBoundType, fakeTypeParameter)
|
return CreateTypeParameterData(newName, declaration, upperBoundType, fakeTypeParameter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun extractFixData(element: KtUserType, diagnostic: Diagnostic): CreateTypeParameterData? {
|
||||||
|
val name = element.referencedName ?: return null
|
||||||
|
return extractFixData(element, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createFixes(
|
override fun createFixes(
|
||||||
|
|||||||
+13
-5
@@ -42,16 +42,21 @@ import org.jetbrains.kotlin.types.typeUtil.isNullableAny
|
|||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
|
|
||||||
class CreateTypeParameterFromUsageFix(
|
class CreateTypeParameterFromUsageFix(
|
||||||
originalElement: KtUserType,
|
originalElement: KtTypeElement,
|
||||||
private val data: CreateTypeParameterData
|
private val data: CreateTypeParameterData
|
||||||
) : CreateFromUsageFixBase<KtUserType>(originalElement) {
|
) : CreateFromUsageFixBase<KtTypeElement>(originalElement) {
|
||||||
override fun getText() = "Create type parameter '${data.name}' in " +
|
override fun getText() = "Create type parameter '${data.name}' in " +
|
||||||
ElementDescriptionUtil.getElementDescription(data.declaration, UsageViewTypeLocation.INSTANCE) + " '${data.declaration.name}'"
|
ElementDescriptionUtil.getElementDescription(data.declaration, UsageViewTypeLocation.INSTANCE) + " '${data.declaration.name}'"
|
||||||
|
|
||||||
override fun startInWriteAction() = false
|
override fun startInWriteAction() = false
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
doInvoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doInvoke(): KtTypeParameter? {
|
||||||
val declaration = data.declaration
|
val declaration = data.declaration
|
||||||
|
val project = declaration.project
|
||||||
val usages = project.runSynchronouslyWithProgress("Searching ${declaration.name}...", true) {
|
val usages = project.runSynchronouslyWithProgress("Searching ${declaration.name}...", true) {
|
||||||
runReadAction {
|
runReadAction {
|
||||||
ReferencesSearch
|
ReferencesSearch
|
||||||
@@ -62,9 +67,9 @@ class CreateTypeParameterFromUsageFix(
|
|||||||
}
|
}
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
} ?: return
|
} ?: return null
|
||||||
|
|
||||||
runWriteAction {
|
return runWriteAction {
|
||||||
val psiFactory = KtPsiFactory(project)
|
val psiFactory = KtPsiFactory(project)
|
||||||
|
|
||||||
val elementsToShorten = SmartList<KtElement>()
|
val elementsToShorten = SmartList<KtElement>()
|
||||||
@@ -76,7 +81,8 @@ class CreateTypeParameterFromUsageFix(
|
|||||||
else null
|
else null
|
||||||
val upperBound = upperBoundText?.let { psiFactory.createType(it) }
|
val upperBound = upperBoundText?.let { psiFactory.createType(it) }
|
||||||
val newTypeParameterText = if (upperBound != null) "${data.name} : ${upperBound.text}" else data.name
|
val newTypeParameterText = if (upperBound != null) "${data.name} : ${upperBound.text}" else data.name
|
||||||
elementsToShorten += declaration.addTypeParameter(psiFactory.createTypeParameter(newTypeParameterText))!!
|
val newTypeParameter = declaration.addTypeParameter(psiFactory.createTypeParameter(newTypeParameterText))!!
|
||||||
|
elementsToShorten += newTypeParameter
|
||||||
|
|
||||||
val anonymizedTypeParameter = createFakeTypeParameterDescriptor(data.fakeTypeParameter.containingDeclaration, "_")
|
val anonymizedTypeParameter = createFakeTypeParameterDescriptor(data.fakeTypeParameter.containingDeclaration, "_")
|
||||||
val anonymizedUpperBoundText = upperBoundType?.let {
|
val anonymizedUpperBoundText = upperBoundType?.let {
|
||||||
@@ -132,6 +138,8 @@ class CreateTypeParameterFromUsageFix(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShortenReferences.DEFAULT.process(elementsToShorten)
|
ShortenReferences.DEFAULT.process(elementsToShorten)
|
||||||
|
|
||||||
|
newTypeParameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ cannot.refactor.synthesized.function=Cannot refactor synthesized function ''{0}'
|
|||||||
error.types.in.generated.function=Cannot generate function with erroneous return type
|
error.types.in.generated.function=Cannot generate function with erroneous return type
|
||||||
cannot.introduce.parameter.of.0.type=Cannot introduce parameter of type ''{0}''
|
cannot.introduce.parameter.of.0.type=Cannot introduce parameter of type ''{0}''
|
||||||
|
|
||||||
0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.declaration={0} has detected {1} code {1,choice,1#fragment|2#fragments} in this file that can be replaced with a call to extracted declaration. Would you like to review and replace {1,choice,1#it|2#them}?
|
0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.declaration={0} has detected {1} code {1,choice,1#fragment|2#fragments} in this file that can be replaced with a usage of extracted declaration. Would you like to review and replace {1,choice,1#it|2#them}?
|
||||||
|
|
||||||
error.wrong.caret.position.function.or.constructor.name=The caret should be positioned at the name of the function or constructor to be refactored.
|
error.wrong.caret.position.function.or.constructor.name=The caret should be positioned at the name of the function or constructor to be refactored.
|
||||||
error.cant.refactor.vararg.functions=Can't refactor the function with variable arguments
|
error.cant.refactor.vararg.functions=Can't refactor the function with variable arguments
|
||||||
|
|||||||
-2
@@ -176,8 +176,6 @@ fun IntroduceParameterDescriptor.performRefactoring() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isObjectOrNonInnerClass(e: PsiElement): Boolean = e is KtObjectDeclaration || (e is KtClass && !e.isInner())
|
|
||||||
|
|
||||||
fun selectNewParameterContext(
|
fun selectNewParameterContext(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
file: KtFile,
|
file: KtFile,
|
||||||
|
|||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.introduceTypeParameter
|
||||||
|
|
||||||
|
import com.intellij.lang.refactoring.RefactoringSupportProvider
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.editor.ex.EditorEx
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.refactoring.RefactoringActionHandler
|
||||||
|
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||||
|
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeParameter.CreateTypeParameterByRefActionFactory
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeParameter.CreateTypeParameterFromUsageFix
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeParameter.getPossibleTypeParameterContainers
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractIntroduceAction
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.processDuplicates
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias.KotlinIntroduceTypeAliasHandler
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetParent
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
||||||
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.psi.KtTypeElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
|
||||||
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||||
|
import org.jetbrains.kotlin.utils.keysToMap
|
||||||
|
import java.lang.AssertionError
|
||||||
|
|
||||||
|
object KotlinIntroduceTypeParameterHandler : RefactoringActionHandler {
|
||||||
|
@JvmField
|
||||||
|
val REFACTORING_NAME = "Introduce Type Parameter"
|
||||||
|
|
||||||
|
fun selectElements(editor: Editor, file: KtFile, continuation: (elements: List<PsiElement>, targetParent: PsiElement) -> Unit) {
|
||||||
|
selectElementsWithTargetParent(
|
||||||
|
REFACTORING_NAME,
|
||||||
|
editor,
|
||||||
|
file,
|
||||||
|
"Introduce type parameter to declaration",
|
||||||
|
listOf(CodeInsightUtils.ElementKind.TYPE_ELEMENT),
|
||||||
|
{ elements, parent -> getPossibleTypeParameterContainers(parent) },
|
||||||
|
continuation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doInvoke(project: Project, editor: Editor, elements: List<PsiElement>, targetParent: PsiElement) {
|
||||||
|
val targetOwner = targetParent as KtTypeParameterListOwner
|
||||||
|
val typeElementToExtract = elements.singleOrNull() as? KtTypeElement
|
||||||
|
?: return showErrorHint(project, editor, "No type to refactor", REFACTORING_NAME)
|
||||||
|
|
||||||
|
val scope = targetOwner.getResolutionScope()
|
||||||
|
val suggestedNames = KotlinNameSuggester.suggestNamesForTypeParameters(1) {
|
||||||
|
scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null
|
||||||
|
}
|
||||||
|
val defaultName = suggestedNames.single()
|
||||||
|
|
||||||
|
val context = typeElementToExtract.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val originalType = typeElementToExtract.getAbbreviatedTypeOrType(context)
|
||||||
|
|
||||||
|
val createTypeParameterData =
|
||||||
|
CreateTypeParameterByRefActionFactory.extractFixData(typeElementToExtract, defaultName)
|
||||||
|
?.copy(upperBoundType = originalType, declaration = targetOwner)
|
||||||
|
?: return showErrorHint(project, editor, "Refactoring is not applicable in the current context", REFACTORING_NAME)
|
||||||
|
|
||||||
|
val parameterRefElement = KtPsiFactory(project).createType(defaultName).typeElement!!
|
||||||
|
|
||||||
|
val duplicateRanges = typeElementToExtract
|
||||||
|
.toRange()
|
||||||
|
.match(targetParent, KotlinPsiUnifier.DEFAULT)
|
||||||
|
.filterNot {
|
||||||
|
val textRange = it.range.getTextRange()
|
||||||
|
typeElementToExtract.textRange.intersects(textRange) || targetOwner.typeParameterList?.textRange?.intersects(textRange) ?: false
|
||||||
|
}
|
||||||
|
.mapNotNull { it.range.elements.toRange() }
|
||||||
|
|
||||||
|
project.executeCommand(REFACTORING_NAME) {
|
||||||
|
runWriteAction {
|
||||||
|
typeElementToExtract.replace(parameterRefElement)
|
||||||
|
|
||||||
|
processDuplicates(
|
||||||
|
duplicateRanges.keysToMap {
|
||||||
|
{
|
||||||
|
it.elements.singleOrNull()?.replace(parameterRefElement)
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
},
|
||||||
|
project,
|
||||||
|
editor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newTypeParameter = CreateTypeParameterFromUsageFix(typeElementToExtract, createTypeParameterData).doInvoke()
|
||||||
|
?: return@executeCommand
|
||||||
|
|
||||||
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, newTypeParameter,
|
||||||
|
(editor as? EditorEx)?.dataContext)
|
||||||
|
VariableInplaceRenameHandler().doRename(newTypeParameter, editor, dataContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
|
||||||
|
if (file !is KtFile) return
|
||||||
|
selectElements(editor, file) { elements, targetParent -> doInvoke(project, editor, elements, targetParent) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
|
||||||
|
throw AssertionError("${KotlinIntroduceTypeAliasHandler.REFACTORING_NAME} can only be invoked from editor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntroduceTypeParameterAction : AbstractIntroduceAction() {
|
||||||
|
override fun getRefactoringHandler(provider: RefactoringSupportProvider) = KotlinIntroduceTypeParameterHandler
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun foo(x: List<A>, f: (<selection>A</selection>) -> Int) {
|
||||||
|
val a: A? = x.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(listOf(A())) { 1 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun <T : A> foo(x: List<T>, f: (T) -> Int) {
|
||||||
|
val a: T? = x.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(listOf(A())) { 1 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun foo(x: List<<selection>(A?) -> List<Int></selection>>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(listOf({ a -> listOf(1) }))
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun <T : (A?) -> List<Int>> foo(x: List<T>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(listOf({ a -> listOf(1) }))
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
open class X(x: List<A>, f: (<selection>A</selection>) -> Int) {
|
||||||
|
val a: A? = x.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y : X(listOf(A()), { 1 })
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
X(listOf(A())) { 1 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
open class X<T : A>(x: List<T>, f: (T) -> Int) {
|
||||||
|
val a: T? = x.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y : X<A>(listOf(A()), { 1 })
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
X(listOf(A())) { 1 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
val List<<selection>A</selection>>.foo: (A) -> Int
|
||||||
|
get() {
|
||||||
|
val a: A? = firstOrNull()
|
||||||
|
return { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val t = listOf(A()).foo
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
val <T : A> List<T>.foo: (T) -> Int
|
||||||
|
get() {
|
||||||
|
val a: T? = firstOrNull()
|
||||||
|
return { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val t = listOf(A()).foo
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun foo(x: List<<selection>A?</selection>>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(listOf(A()))
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun <T : A?> foo(x: List<T>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(listOf(A()))
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun foo(x: (<selection>List<A?></selection>) -> Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo { 1 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun <T : List<A?>> foo(x: (T) -> Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo<List<A?>> { 1 }
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.*
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.INTRODUCE_PROPERTY
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.INTRODUCE_PROPERTY
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias.KotlinIntroduceTypeAliasHandler
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias.KotlinIntroduceTypeAliasHandler
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeParameter.KotlinIntroduceTypeParameterHandler
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
@@ -314,6 +315,18 @@ abstract class AbstractExtractionTest() : KotlinLightCodeInsightFixtureTestCase(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun doIntroduceTypeParameterTest(path: String) {
|
||||||
|
doTest(path) { file ->
|
||||||
|
file as KtFile
|
||||||
|
|
||||||
|
val explicitPreviousSibling = file.findElementByCommentPrefix("// SIBLING:")
|
||||||
|
val editor = fixture.editor
|
||||||
|
KotlinIntroduceTypeParameterHandler.selectElements(editor, file) { elements, previousSibling ->
|
||||||
|
KotlinIntroduceTypeParameterHandler.doInvoke(project, editor, elements, explicitPreviousSibling ?: previousSibling)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected fun doIntroduceTypeAliasTest(path: String) {
|
protected fun doIntroduceTypeAliasTest(path: String) {
|
||||||
doTest(path) { file ->
|
doTest(path) { file ->
|
||||||
file as KtFile
|
file as KtFile
|
||||||
|
|||||||
+45
@@ -3992,6 +3992,51 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/refactoring/introduceTypeParameter")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class IntroduceTypeParameter extends AbstractExtractionTest {
|
||||||
|
public void testAllFilesPresentInIntroduceTypeParameter() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceTypeParameter"), Pattern.compile("^(.+)\\.(kt|kts)$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("duplicates.kt")
|
||||||
|
public void testDuplicates() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/duplicates.kt");
|
||||||
|
doIntroduceTypeParameterTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionType.kt")
|
||||||
|
public void testFunctionType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/functionType.kt");
|
||||||
|
doIntroduceTypeParameterTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inClass.kt")
|
||||||
|
public void testInClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/inClass.kt");
|
||||||
|
doIntroduceTypeParameterTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inProperty.kt")
|
||||||
|
public void testInProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/inProperty.kt");
|
||||||
|
doIntroduceTypeParameterTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableType.kt")
|
||||||
|
public void testNullableType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/nullableType.kt");
|
||||||
|
doIntroduceTypeParameterTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("userType.kt")
|
||||||
|
public void testUserType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/userType.kt");
|
||||||
|
doIntroduceTypeParameterTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/refactoring/introduceTypeAlias")
|
@TestMetadata("idea/testData/refactoring/introduceTypeAlias")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user