Used ConvertMemberToExtensionIntention in MigrateExternalExtensionFix instead of manually creating extension method

This commit is contained in:
qx
2017-05-03 17:57:17 +03:00
parent 8ba61ce7aa
commit f6fe1c50c2
6 changed files with 31 additions and 78 deletions
@@ -54,6 +54,27 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
//TODO: local class //TODO: local class
override fun applyTo(element: KtCallableDeclaration, editor: Editor?) { override fun applyTo(element: KtCallableDeclaration, editor: Editor?) {
val (extension, bodyToSelect) = createExtensionCallableAndPrepareBodyToSelect(element)
editor?.apply {
unblockDocument()
if (bodyToSelect != null) {
val range = bodyToSelect!!.textRange
moveCaret(range.startOffset, ScrollType.CENTER)
selectionModel.setSelection(range.startOffset, range.endOffset)
}
else {
moveCaret(extension.textOffset, ScrollType.CENTER)
}
}
}
fun convert(element: KtCallableDeclaration): KtCallableDeclaration {
return createExtensionCallableAndPrepareBodyToSelect(element).first
}
private fun createExtensionCallableAndPrepareBodyToSelect(element: KtCallableDeclaration): Pair<KtCallableDeclaration, KtExpression?> {
val descriptor = element.resolveToDescriptor() val descriptor = element.resolveToDescriptor()
val containingClass = descriptor.containingDeclaration as ClassDescriptor val containingClass = descriptor.containingDeclaration as ClassDescriptor
@@ -179,18 +200,7 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
} }
} }
editor?.apply { return extension to bodyToSelect
unblockDocument()
if (bodyToSelect != null) {
val range = bodyToSelect!!.textRange
moveCaret(range.startOffset, ScrollType.CENTER)
selectionModel.setSelection(range.startOffset, range.endOffset)
}
else {
moveCaret(extension.textOffset, ScrollType.CENTER)
}
}
} }
private fun newTypeParameterList(member: KtCallableDeclaration): KtTypeParameterList? { private fun newTypeParameterList(member: KtCallableDeclaration): KtTypeParameterList? {
@@ -21,19 +21,16 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.checkAnnotationName import org.jetbrains.kotlin.descriptors.annotations.checkAnnotationName
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.setReceiverType
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
import org.jetbrains.kotlin.idea.intentions.declarations.ConvertMemberToExtensionIntention
import org.jetbrains.kotlin.idea.project.builtIns import org.jetbrains.kotlin.idea.project.builtIns
import org.jetbrains.kotlin.idea.quickfix.CleanupFix import org.jetbrains.kotlin.idea.quickfix.CleanupFix
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.idea.util.addAnnotation import org.jetbrains.kotlin.idea.util.addAnnotation
import org.jetbrains.kotlin.js.PredefinedAnnotation import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs
@@ -67,24 +64,19 @@ class MigrateExternalExtensionFix(declaration: KtNamedDeclaration)
} }
private fun fixNativeClass(containingClass: KtClassOrObject, project: Project, editor: Editor?, file: KtFile) { private fun fixNativeClass(containingClass: KtClassOrObject, project: Project, editor: Editor?, file: KtFile) {
val membersToFix = containingClass.declarations.filterIsInstance<KtNamedDeclaration>().filter { isMemberDeclaration(it) }. map { val membersToFix = containingClass.declarations.filterIsInstance<KtCallableDeclaration>().filter { isMemberDeclaration(it) }. map {
it to fetchJsNativeAnnotations(it) it to fetchJsNativeAnnotations(it)
}.filter { }.filter {
it.second.annotations.isNotEmpty() it.second.annotations.isNotEmpty()
} }
var anchor: PsiElement = containingClass membersToFix.asReversed().forEach { (memberDeclaration, annotations) ->
membersToFix.forEach { (memberDeclaration, annotations) ->
if (annotations.nativeAnnotation != null && !annotations.isGetter && !annotations.isSetter && !annotations.isInvoke) { if (annotations.nativeAnnotation != null && !annotations.isGetter && !annotations.isSetter && !annotations.isInvoke) {
convertNativeAnnotationToJsName(memberDeclaration, annotations) convertNativeAnnotationToJsName(memberDeclaration, annotations)
annotations.nativeAnnotation.delete() annotations.nativeAnnotation.delete()
} else { } else {
// TODO: find references val externalDeclaration = ConvertMemberToExtensionIntention().convert(memberDeclaration)
createExtensionDeclaration(memberDeclaration, project, anchor)?.let { externalDeclaration -> fixExtensionMemberDeclaration(externalDeclaration, project, null, file)
memberDeclaration.delete()
fixExtensionMemberDeclaration(externalDeclaration, project, null, file)
anchor = externalDeclaration
}
} }
} }
@@ -93,55 +85,6 @@ class MigrateExternalExtensionFix(declaration: KtNamedDeclaration)
fixAnnotations(containingClass, classAnnotations, null) fixAnnotations(containingClass, classAnnotations, null)
} }
private fun createExtensionDeclaration(declaration: KtNamedDeclaration, project: Project, anchor: PsiElement): KtNamedDeclaration? {
val containingClass = declaration.containingClassOrObject ?: return null
val builder = KtPsiFactory.CallableBuilder(if (declaration is KtNamedFunction) KtPsiFactory.CallableBuilder.Target.FUNCTION else KtPsiFactory.CallableBuilder.Target.READ_ONLY_PROPERTY)
// modifiers
declaration.annotationEntries.forEach {
builder.modifier(it.text)
}
// type parameters
builder.typeParams(containingClass.typeParameters.union((declaration as? KtTypeParameterListOwner)?.typeParameters ?: emptyList()).map { it.text })
// receiver
builder.receiver(containingClass.name ?: "XXX")
// name
builder.name(declaration.nameAsSafeName.identifier)
// parameters
(declaration as? KtCallableDeclaration)?.valueParameters?.forEach {
builder.param(it.nameAsSafeName.identifier, it.typeReference?.text ?: "XXX", it.defaultValue?.text)
}
// return type
val returnTypeReference = declaration.getReturnTypeReference()
if (returnTypeReference != null)
builder.returnType(returnTypeReference.text)
else
builder.noReturnType()
// body
(declaration as? KtDeclarationWithBody)?.bodyExpression?.let {
builder.blockBody(it.text)
}
// Create function etc
var newFunctionText = builder.asString()
val psiFactory = KtPsiFactory(project)
val newFunction = psiFactory.createFunction(newFunctionText)
val parent = anchor.parent
val result = parent.addAfter(newFunction, anchor) as KtCallableDeclaration
parent.addBefore(psiFactory.createNewLine(2), result)
// fix receiver type
result.setReceiverType((containingClass.resolveToDescriptor(BodyResolveMode.PARTIAL) as ClassDescriptor).defaultType)
return result
}
private data class JsNativeAnnotations(val annotations: List<KtAnnotationEntry>, val nativeAnnotation: KtAnnotationEntry?, val isGetter: Boolean, val isSetter: Boolean, val isInvoke: Boolean) private data class JsNativeAnnotations(val annotations: List<KtAnnotationEntry>, val nativeAnnotation: KtAnnotationEntry?, val isGetter: Boolean, val isSetter: Boolean, val isInvoke: Boolean)
private fun fetchJsNativeAnnotations(declaration: KtNamedDeclaration) : JsNativeAnnotations { private fun fetchJsNativeAnnotations(declaration: KtNamedDeclaration) : JsNativeAnnotations {
@@ -20,6 +20,6 @@ inline fun B.bar(a: B) {
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) { inline fun<T> B.exp(t: T) {
asDynamic()(t) asDynamic()(t)
} }
@@ -20,6 +20,6 @@ inline fun B.bar(a: B) {
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) { inline fun<T> B.exp(t: T) {
asDynamic()(t) asDynamic()(t)
} }
@@ -20,6 +20,6 @@ inline fun B.bar(a: B) {
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) { inline fun<T> B.exp(t: T) {
asDynamic()(t) asDynamic()(t)
} }
@@ -5,6 +5,6 @@ external class B {
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) { inline fun<T> B.exp(t: T) {
asDynamic()(t) asDynamic()(t)
} }