fixup! Introduce Type Parameter
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
cannot.refactor.not.expression=Cannot find an expression to introduce
|
||||
cannot.refactor.not.expression.to.extract=Cannot find an expression to extract
|
||||
expressions.title=Expressions
|
||||
introduce.variable=Introduce Variable
|
||||
introduce.property=Introduce Property
|
||||
introduce.parameter=Introduce Parameter
|
||||
@@ -25,7 +24,7 @@ cannot.refactor.synthesized.function=Cannot refactor synthesized function ''{0}'
|
||||
error.types.in.generated.function=Cannot generate function with erroneous return type
|
||||
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 usage of extracted declaration. Would you like to review and replace {1,choice,1#it|2#them}?
|
||||
0.has.detected.1.code.fragments.in.2.that.can.be.replaced.with.3={0} has detected {1} code {1,choice,1#fragment|2#fragments} in {2} that can be replaced with {3}. 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.cant.refactor.vararg.functions=Can't refactor the function with variable arguments
|
||||
|
||||
@@ -522,8 +522,21 @@ public class KotlinRefactoringUtil {
|
||||
}
|
||||
});
|
||||
|
||||
String title = "Elements";
|
||||
if (elementKinds.size() == 1) {
|
||||
switch (elementKinds.iterator().next()) {
|
||||
case EXPRESSION:
|
||||
title = "Expressions";
|
||||
break;
|
||||
case TYPE_ELEMENT:
|
||||
case TYPE_CONSTRUCTOR:
|
||||
title = "Types";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JBPopupFactory.getInstance().createListPopupBuilder(list).
|
||||
setTitle(KotlinRefactoringBundle.message("expressions.title")).setMovable(false).setResizable(false).
|
||||
setTitle(title).setMovable(false).setResizable(false).
|
||||
setRequestFocus(true).setItemChoosenCallback(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
+7
-3
@@ -66,7 +66,9 @@ fun KotlinPsiRange.preview(project: Project, editor: Editor): RangeHighlighter?
|
||||
fun processDuplicates(
|
||||
duplicateReplacers: Map<KotlinPsiRange, () -> Unit>,
|
||||
project: Project,
|
||||
editor: Editor
|
||||
editor: Editor,
|
||||
scopeDescription: String = "this file",
|
||||
usageDescription: String = "a usage of extracted declaration"
|
||||
) {
|
||||
val size = duplicateReplacers.size
|
||||
if (size == 0) return
|
||||
@@ -81,9 +83,11 @@ fun processDuplicates(
|
||||
Messages.showYesNoDialog(
|
||||
project,
|
||||
KotlinRefactoringBundle.message(
|
||||
"0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.declaration",
|
||||
"0.has.detected.1.code.fragments.in.2.that.can.be.replaced.with.3",
|
||||
ApplicationNamesInfo.getInstance().productName,
|
||||
duplicateReplacers.size
|
||||
duplicateReplacers.size,
|
||||
scopeDescription,
|
||||
usageDescription
|
||||
),
|
||||
"Process Duplicates",
|
||||
Messages.getQuestionIcon()
|
||||
|
||||
+63
-31
@@ -24,12 +24,14 @@ 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.ElementDescriptionUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||
import com.intellij.usageView.UsageViewTypeLocation
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||
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
|
||||
@@ -39,6 +41,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.processD
|
||||
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.refactoring.rename.VariableInplaceRenameHandlerWithFinishHook
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
@@ -50,6 +53,10 @@ 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.psi.psiUtil.createSmartPointer
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
@@ -77,10 +84,15 @@ object KotlinIntroduceTypeParameterHandler : RefactoringActionHandler {
|
||||
val typeElementToExtract = elements.singleOrNull() as? KtTypeElement
|
||||
?: return showErrorHint(project, editor, "No type to refactor", REFACTORING_NAME)
|
||||
|
||||
val typeElementToExtractPointer = typeElementToExtract.createSmartPointer()
|
||||
|
||||
val scope = targetOwner.getResolutionScope()
|
||||
val suggestedNames = KotlinNameSuggester.suggestNamesForTypeParameters(1) {
|
||||
scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null
|
||||
}
|
||||
val suggestedNames = KotlinNameSuggester.suggestNamesForTypeParameters(
|
||||
1,
|
||||
CollectingNameValidator(targetOwner.typeParameters.mapNotNull { it.name }) {
|
||||
scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null
|
||||
}
|
||||
)
|
||||
val defaultName = suggestedNames.single()
|
||||
|
||||
val context = typeElementToExtract.analyze(BodyResolveMode.PARTIAL)
|
||||
@@ -91,40 +103,60 @@ object KotlinIntroduceTypeParameterHandler : RefactoringActionHandler {
|
||||
?.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
|
||||
val newTypeParameterPointer = newTypeParameter.createSmartPointer()
|
||||
|
||||
val postRename = postRename@ {
|
||||
val restoredTypeParameter = newTypeParameterPointer.element ?: return@postRename
|
||||
val restoredOwner = restoredTypeParameter.getStrictParentOfType<KtTypeParameterListOwner>() ?: return@postRename
|
||||
val restoredOriginalTypeElement = typeElementToExtractPointer.element ?: return@postRename
|
||||
|
||||
runWriteAction {
|
||||
val parameterRefElement = KtPsiFactory(project).createType(restoredTypeParameter.name ?: "_").typeElement!!
|
||||
|
||||
val duplicateRanges = restoredOriginalTypeElement
|
||||
.toRange()
|
||||
.match(restoredOwner, KotlinPsiUnifier.DEFAULT)
|
||||
.filterNot {
|
||||
val textRange = it.range.getTextRange()
|
||||
restoredOriginalTypeElement.textRange.intersects(textRange)
|
||||
|| restoredOwner.typeParameterList?.textRange?.intersects(textRange) ?: false
|
||||
}
|
||||
.mapNotNull { it.range.elements.toRange() }
|
||||
|
||||
restoredOriginalTypeElement.replace(parameterRefElement)
|
||||
|
||||
processDuplicates(
|
||||
duplicateRanges.keysToMap {
|
||||
{
|
||||
it.elements.singleOrNull()?.replace(parameterRefElement)
|
||||
Unit
|
||||
}
|
||||
},
|
||||
project,
|
||||
editor,
|
||||
ElementDescriptionUtil.getElementDescription(restoredOwner, UsageViewTypeLocation.INSTANCE) + " '${restoredOwner.name}'",
|
||||
"a reference to extracted type parameter"
|
||||
)
|
||||
|
||||
restoredTypeParameter.extendsBound?.let {
|
||||
editor.selectionModel.setSelection(it.startOffset, it.endOffset)
|
||||
editor.caretModel.moveToOffset(it.startOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, newTypeParameter,
|
||||
(editor as? EditorEx)?.dataContext)
|
||||
VariableInplaceRenameHandler().doRename(newTypeParameter, editor, dataContext)
|
||||
editor.selectionModel.removeSelection()
|
||||
editor.caretModel.moveToOffset(newTypeParameter.startOffset)
|
||||
VariableInplaceRenameHandlerWithFinishHook(postRename).doRename(newTypeParameter, editor, dataContext)
|
||||
}
|
||||
else {
|
||||
postRename()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.rename
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
|
||||
|
||||
class VariableInplaceRenameHandlerWithFinishHook(private val onFinish: () -> Unit) : VariableInplaceRenameHandler() {
|
||||
override fun createRenamer(elementToRename: PsiElement, editor: Editor?): VariableInplaceRenamer? {
|
||||
return object : VariableInplaceRenamer(elementToRename as PsiNamedElement, editor) {
|
||||
override fun performRefactoring(): Boolean {
|
||||
try {
|
||||
return super.performRefactoring()
|
||||
}
|
||||
finally {
|
||||
onFinish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user