Converting object literal to class allows to choose a name (KT-19254)
This commit is contained in:
+33
-19
@@ -16,13 +16,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtilCore
|
||||
import com.intellij.codeInsight.template.TemplateBuilderImpl
|
||||
import com.intellij.codeInsight.template.TemplateManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
|
||||
@@ -35,12 +38,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
|
||||
class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjectLiteralExpression>(
|
||||
@@ -55,16 +53,14 @@ class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjec
|
||||
val project = element.project
|
||||
|
||||
val scope = element.getResolutionScope()
|
||||
val context = element.analyze(BodyResolveMode.PARTIAL)
|
||||
val objectLiteralType = element.getType(context)
|
||||
val validator: (String) -> Boolean = { scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null }
|
||||
val className = if (objectLiteralType != null) {
|
||||
KotlinNameSuggester.suggestNamesByType(objectLiteralType, validator, "O").first()
|
||||
}
|
||||
else {
|
||||
KotlinNameSuggester.suggestNameByName("O", validator)
|
||||
}
|
||||
|
||||
val validator: (String) -> Boolean = { scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null }
|
||||
val classNames = element.objectDeclaration.superTypeListEntries
|
||||
.mapNotNull { it.typeReference?.typeElement?.let { KotlinNameSuggester.suggestTypeAliasNameByPsi(it, validator) } }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?: listOf(KotlinNameSuggester.suggestNameByName("O", validator))
|
||||
|
||||
val className = classNames.first()
|
||||
val psiFactory = KtPsiFactory(element)
|
||||
|
||||
val targetSibling = element.parentsWithSelf.first { it.parent == targetParent }
|
||||
@@ -102,8 +98,8 @@ class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjec
|
||||
)
|
||||
}
|
||||
}
|
||||
).run(editor, ExtractionData(element.containingKtFile, element.toRange(), targetSibling)) {
|
||||
val functionDeclaration = it.declaration as KtFunction
|
||||
).run(editor, ExtractionData(element.containingKtFile, element.toRange(), targetSibling)) { extractionResult ->
|
||||
val functionDeclaration = extractionResult.declaration as KtFunction
|
||||
if (functionDeclaration.valueParameters.isNotEmpty()) {
|
||||
val valKeyword = psiFactory.createValKeyword()
|
||||
newClass
|
||||
@@ -115,10 +111,24 @@ class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjec
|
||||
it.addModifier(KtTokens.PRIVATE_KEYWORD)
|
||||
}
|
||||
}
|
||||
functionDeclaration.replaced(newClass).apply {
|
||||
|
||||
val introducedClass = functionDeclaration.replaced(newClass).apply {
|
||||
if (hasMemberReference && containingClass == (parent.parent as? KtClass)) addModifier(KtTokens.INNER_KEYWORD)
|
||||
primaryConstructor?.let { CodeStyleManager.getInstance(project).reformat(it) }
|
||||
}.let { CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(it) }
|
||||
|
||||
val file = introducedClass.containingFile
|
||||
|
||||
val template = TemplateBuilderImpl(file).let { builder ->
|
||||
builder.replaceElement(introducedClass.nameIdentifier!!, NEW_CLASS_NAME, ChooseStringExpression(classNames), true)
|
||||
for (psiReference in ReferencesSearch.search(introducedClass, LocalSearchScope(file), false)) {
|
||||
builder.replaceElement(psiReference.element, USAGE_VARIABLE_NAME, NEW_CLASS_NAME, false)
|
||||
}
|
||||
builder.buildInlineTemplate()
|
||||
}
|
||||
|
||||
editor.caretModel.moveToOffset(file.startOffset)
|
||||
TemplateManager.getInstance(project).startTemplate(editor, template)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,3 +156,7 @@ class ConvertObjectLiteralToClassIntention : SelfTargetingRangeIntention<KtObjec
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val NEW_CLASS_NAME = "NEW_CLASS_NAME"
|
||||
|
||||
private const val USAGE_VARIABLE_NAME = "OTHER_VAR"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class Test { // TARGET_BLOCK:
|
||||
fun foo() {
|
||||
O()
|
||||
Runnable1()
|
||||
}
|
||||
|
||||
class O : Runnable {
|
||||
class Runnable1 : Runnable {
|
||||
override fun run() {
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,10 +2,10 @@ class Test { // TARGET_BLOCK:
|
||||
fun method() = 1
|
||||
|
||||
fun foo() {
|
||||
O()
|
||||
Runnable1()
|
||||
}
|
||||
|
||||
inner class O : Runnable {
|
||||
inner class Runnable1 : Runnable {
|
||||
override fun run() {
|
||||
method()
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,10 +2,10 @@ class Test { // TARGET_BLOCK:
|
||||
var field = 1
|
||||
|
||||
fun foo() {
|
||||
O()
|
||||
Runnable1()
|
||||
}
|
||||
|
||||
inner class O : Runnable {
|
||||
inner class Runnable1 : Runnable {
|
||||
override fun run() {
|
||||
field = 2
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ class Test {
|
||||
var field = 1
|
||||
|
||||
fun foo() { // TARGET_BLOCK:
|
||||
class O : Runnable {
|
||||
class Runnable1 : Runnable {
|
||||
override fun run() {
|
||||
field = 2
|
||||
}
|
||||
}
|
||||
|
||||
O()
|
||||
Runnable1()
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
open class K
|
||||
|
||||
fun foo(n: Int) {
|
||||
val x = <caret>O()
|
||||
val x = K1()
|
||||
}
|
||||
|
||||
class O : K() {
|
||||
class K1<caret> : K() {
|
||||
fun bar() = 1
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
fun foo(n: Int) {
|
||||
val x = <caret>O(n)
|
||||
val x = O(n)
|
||||
}
|
||||
|
||||
class O(private val n: Int) {
|
||||
class O<caret>(private val n: Int) {
|
||||
fun bar() = n
|
||||
}
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
open class K
|
||||
|
||||
fun foo(n: Int) {
|
||||
val x = <caret>O(n)
|
||||
val x = K1(n)
|
||||
}
|
||||
|
||||
class O(private val n: Int) : K() {
|
||||
class K1<caret>(private val n: Int) : K() {
|
||||
fun bar() = n
|
||||
}
|
||||
Reference in New Issue
Block a user