Extract Function: Forbid inappropriate declarations inside extractable fragment
#KT-20467 Fixed #KT-20469 Fixed
This commit is contained in:
+3
-2
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.KotlinExtractFunctionDialog
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.KotlinExtractFunctionDialog
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetSibling
|
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetSibling
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.validateExpressionElements
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
||||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
|
|
||||||
class ExtractKotlinFunctionHandler(
|
class ExtractKotlinFunctionHandler(
|
||||||
private val allContainersEnabled: Boolean = false,
|
private val allContainersEnabled: Boolean = false,
|
||||||
@@ -69,6 +69,7 @@ class ExtractKotlinFunctionHandler(
|
|||||||
file,
|
file,
|
||||||
"Select target code block",
|
"Select target code block",
|
||||||
listOf(CodeInsightUtils.ElementKind.EXPRESSION),
|
listOf(CodeInsightUtils.ElementKind.EXPRESSION),
|
||||||
|
::validateExpressionElements,
|
||||||
{ elements, parent -> parent.getExtractionContainers(elements.size == 1, allContainersEnabled) },
|
{ elements, parent -> parent.getExtractionContainers(elements.size == 1, allContainersEnabled) },
|
||||||
continuation
|
continuation
|
||||||
)
|
)
|
||||||
|
|||||||
+1
@@ -187,6 +187,7 @@ fun selectNewParameterContext(
|
|||||||
file = file,
|
file = file,
|
||||||
title = "Introduce parameter to declaration",
|
title = "Introduce parameter to declaration",
|
||||||
elementKinds = listOf(CodeInsightUtils.ElementKind.EXPRESSION),
|
elementKinds = listOf(CodeInsightUtils.ElementKind.EXPRESSION),
|
||||||
|
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)))
|
||||||
|
|||||||
+2
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetSibling
|
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetSibling
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHintByKey
|
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHintByKey
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.introduce.validateExpressionElements
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
||||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||||
import org.jetbrains.kotlin.psi.KtClassBody
|
import org.jetbrains.kotlin.psi.KtClassBody
|
||||||
@@ -73,6 +74,7 @@ class KotlinIntroducePropertyHandler(
|
|||||||
file,
|
file,
|
||||||
"Select target code block",
|
"Select target code block",
|
||||||
listOf(CodeInsightUtils.ElementKind.EXPRESSION),
|
listOf(CodeInsightUtils.ElementKind.EXPRESSION),
|
||||||
|
::validateExpressionElements,
|
||||||
{ _, parent ->
|
{ _, parent ->
|
||||||
parent.getExtractionContainers(strict = true, includeAll = true).filter { it is KtClassBody || (it is KtFile && !it.isScript()) }
|
parent.getExtractionContainers(strict = true, includeAll = true).filter { it is KtClassBody || (it is KtFile && !it.isScript()) }
|
||||||
},
|
},
|
||||||
|
|||||||
+1
@@ -55,6 +55,7 @@ open class KotlinIntroduceTypeAliasHandler : RefactoringActionHandler {
|
|||||||
file,
|
file,
|
||||||
"Select target code block",
|
"Select target code block",
|
||||||
listOf(TYPE_ELEMENT, TYPE_CONSTRUCTOR),
|
listOf(TYPE_ELEMENT, TYPE_CONSTRUCTOR),
|
||||||
|
{ null },
|
||||||
{ _, parent -> listOf(parent.containingFile) },
|
{ _, parent -> listOf(parent.containingFile) },
|
||||||
continuation
|
continuation
|
||||||
)
|
)
|
||||||
|
|||||||
+1
@@ -74,6 +74,7 @@ object KotlinIntroduceTypeParameterHandler : RefactoringActionHandler {
|
|||||||
file,
|
file,
|
||||||
"Introduce type parameter to declaration",
|
"Introduce type parameter to declaration",
|
||||||
listOf(CodeInsightUtils.ElementKind.TYPE_ELEMENT),
|
listOf(CodeInsightUtils.ElementKind.TYPE_ELEMENT),
|
||||||
|
{ null },
|
||||||
{ _, parent -> getPossibleTypeParameterContainers(parent) },
|
{ _, parent -> getPossibleTypeParameterContainers(parent) },
|
||||||
continuation
|
continuation
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ fun selectElementsWithTargetSibling(
|
|||||||
file: KtFile,
|
file: KtFile,
|
||||||
title: String,
|
title: String,
|
||||||
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
||||||
|
elementValidator: (List<PsiElement>) -> String?,
|
||||||
getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>,
|
getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>,
|
||||||
continuation: (elements: List<PsiElement>, targetSibling: PsiElement) -> Unit
|
continuation: (elements: List<PsiElement>, targetSibling: PsiElement) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -69,7 +70,7 @@ fun selectElementsWithTargetSibling(
|
|||||||
continuation(elements, outermostParent)
|
continuation(elements, outermostParent)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectElementsWithTargetParent(operationName, editor, file, title, elementKinds, getContainers, ::onSelectionComplete)
|
selectElementsWithTargetParent(operationName, editor, file, title, elementKinds, elementValidator, getContainers, ::onSelectionComplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectElementsWithTargetParent(
|
fun selectElementsWithTargetParent(
|
||||||
@@ -78,6 +79,7 @@ fun selectElementsWithTargetParent(
|
|||||||
file: KtFile,
|
file: KtFile,
|
||||||
title: String,
|
title: String,
|
||||||
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
elementKinds: Collection<CodeInsightUtils.ElementKind>,
|
||||||
|
elementValidator: (List<PsiElement>) -> String?,
|
||||||
getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>,
|
getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>,
|
||||||
continuation: (elements: List<PsiElement>, targetParent: PsiElement) -> Unit
|
continuation: (elements: List<PsiElement>, targetParent: PsiElement) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -86,6 +88,11 @@ fun selectElementsWithTargetParent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun selectTargetContainer(elements: List<PsiElement>) {
|
fun selectTargetContainer(elements: List<PsiElement>) {
|
||||||
|
elementValidator(elements)?.let {
|
||||||
|
showErrorHint(file.project, editor, it, operationName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val physicalElements = elements.map { it.substringContextOrThis }
|
val physicalElements = elements.map { it.substringContextOrThis }
|
||||||
val parent = PsiTreeUtil.findCommonParent(physicalElements)
|
val parent = PsiTreeUtil.findCommonParent(physicalElements)
|
||||||
?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}")
|
?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}")
|
||||||
@@ -236,4 +243,11 @@ fun <T : KtDeclaration> insertDeclaration(declaration: T, targetSibling: PsiElem
|
|||||||
return (targetContainer.addBefore(declaration, anchor) as T).apply {
|
return (targetContainer.addBefore(declaration, anchor) as T).apply {
|
||||||
targetContainer.addBefore(KtPsiFactory(declaration).createWhiteSpace("\n\n"), anchor)
|
targetContainer.addBefore(KtPsiFactory(declaration).createWhiteSpace("\n\n"), anchor)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun validateExpressionElements(elements: List<PsiElement>): String? {
|
||||||
|
if (elements.any { it is KtConstructor<*> || it is KtParameter || it is KtTypeAlias || it is KtPropertyAccessor }) {
|
||||||
|
return "Refactoring is not applicable to this code fragment"
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// SIBLING:
|
||||||
|
class Foo {
|
||||||
|
<selection>constructor(bar: Int)</selection>
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
Refactoring is not applicable to this code fragment
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// SIBLING:
|
||||||
|
class Foo(<selection>bar: String = "baz"</selection>)
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
Refactoring is not applicable to this code fragment
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
Cannot refactor in this place
|
Cannot refactor in this place
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
Cannot refactor in this place
|
Cannot refactor in this place
|
||||||
+10
@@ -986,6 +986,16 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
|||||||
runTest("idea/testData/refactoring/extractFunction/basic/refInReturn.kt");
|
runTest("idea/testData/refactoring/extractFunction/basic/refInReturn.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("selectedConstructor.kt")
|
||||||
|
public void testSelectedConstructor() throws Exception {
|
||||||
|
runTest("idea/testData/refactoring/extractFunction/basic/selectedConstructor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("selectedParameter.kt")
|
||||||
|
public void testSelectedParameter() throws Exception {
|
||||||
|
runTest("idea/testData/refactoring/extractFunction/basic/selectedParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("suspendCall.kt")
|
@TestMetadata("suspendCall.kt")
|
||||||
public void testSuspendCall() throws Exception {
|
public void testSuspendCall() throws Exception {
|
||||||
runTest("idea/testData/refactoring/extractFunction/basic/suspendCall.kt");
|
runTest("idea/testData/refactoring/extractFunction/basic/suspendCall.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user