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
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 containingClass = descriptor.containingDeclaration as ClassDescriptor
@@ -179,18 +200,7 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
}
}
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)
}
}
return extension to bodyToSelect
}
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.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.checkAnnotationName
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
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.declarations.ConvertMemberToExtensionIntention
import org.jetbrains.kotlin.idea.project.builtIns
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
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.js.PredefinedAnnotation
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) {
val membersToFix = containingClass.declarations.filterIsInstance<KtNamedDeclaration>().filter { isMemberDeclaration(it) }. map {
val membersToFix = containingClass.declarations.filterIsInstance<KtCallableDeclaration>().filter { isMemberDeclaration(it) }. map {
it to fetchJsNativeAnnotations(it)
}.filter {
it.second.annotations.isNotEmpty()
}
var anchor: PsiElement = containingClass
membersToFix.forEach { (memberDeclaration, annotations) ->
membersToFix.asReversed().forEach { (memberDeclaration, annotations) ->
if (annotations.nativeAnnotation != null && !annotations.isGetter && !annotations.isSetter && !annotations.isInvoke) {
convertNativeAnnotationToJsName(memberDeclaration, annotations)
annotations.nativeAnnotation.delete()
} else {
// TODO: find references
createExtensionDeclaration(memberDeclaration, project, anchor)?.let { externalDeclaration ->
memberDeclaration.delete()
fixExtensionMemberDeclaration(externalDeclaration, project, null, file)
anchor = externalDeclaration
}
val externalDeclaration = ConvertMemberToExtensionIntention().convert(memberDeclaration)
fixExtensionMemberDeclaration(externalDeclaration, project, null, file)
}
}
@@ -93,55 +85,6 @@ class MigrateExternalExtensionFix(declaration: KtNamedDeclaration)
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 fun fetchJsNativeAnnotations(declaration: KtNamedDeclaration) : JsNativeAnnotations {
@@ -20,6 +20,6 @@ inline fun B.bar(a: B) {
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) {
inline fun<T> B.exp(t: T) {
asDynamic()(t)
}
@@ -20,6 +20,6 @@ inline fun B.bar(a: B) {
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) {
inline fun<T> B.exp(t: T) {
asDynamic()(t)
}
@@ -20,6 +20,6 @@ inline fun B.bar(a: B) {
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) {
inline fun<T> B.exp(t: T) {
asDynamic()(t)
}
@@ -5,6 +5,6 @@ external class B {
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> B.exp(t: T) {
inline fun<T> B.exp(t: T) {
asDynamic()(t)
}