Fixed intentions that used not valid PSI
Fixed ConvertToMemberIntention and ObjectLiteralToLambdaIntention that are used PSI after lazy block reparse.
This commit is contained in:
@@ -112,10 +112,10 @@ class ObjectLiteralToLambdaIntention : SelfTargetingRangeIntention<KtObjectLiter
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtObjectLiteralExpression, editor: Editor?) {
|
||||
val commentSaver = CommentSaver(element)
|
||||
|
||||
val (_, baseType, singleFunction) = extractData(element)!!
|
||||
|
||||
val commentSaver = CommentSaver(element)
|
||||
val returnSaver = ReturnSaver(singleFunction)
|
||||
|
||||
val body = singleFunction.bodyExpression!!
|
||||
@@ -157,14 +157,13 @@ class ObjectLiteralToLambdaIntention : SelfTargetingRangeIntention<KtObjectLiter
|
||||
}
|
||||
|
||||
val replaced = element.replaced(newExpression)
|
||||
commentSaver.restore(replaced, forceAdjustIndent = true/* by some reason lambda body is sometimes not properly indented */)
|
||||
|
||||
val callee = replaced.getCalleeExpressionIfAny()!! as KtNameReferenceExpression
|
||||
val callExpression = callee.parent as KtCallExpression
|
||||
val functionLiteral = callExpression.lambdaArguments.single().getLambdaExpression()!!
|
||||
|
||||
val returnLabel = callee.getReferencedNameAsName()
|
||||
returnSaver.restore(functionLiteral, returnLabel)
|
||||
commentSaver.restore(replaced, forceAdjustIndent = true/* by some reason lambda body is sometimes not properly indented */)
|
||||
|
||||
val parentCall = ((replaced.parent as? KtValueArgument)
|
||||
?.parent as? KtValueArgumentList)
|
||||
|
||||
+70
-33
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.intentions.declarations
|
||||
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.ui.Messages
|
||||
@@ -47,6 +48,8 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
private val LOG = Logger.getInstance(ConvertMemberToExtensionIntention::class.java)
|
||||
|
||||
class ConvertMemberToExtensionIntention :
|
||||
SelfTargetingRangeIntention<KtCallableDeclaration>(KtCallableDeclaration::class.java, "Convert member to extension"),
|
||||
LowPriorityAction {
|
||||
@@ -81,34 +84,71 @@ class ConvertMemberToExtensionIntention :
|
||||
}
|
||||
|
||||
runWriteAction {
|
||||
val (extension, bodyToSelect) = createExtensionCallableAndPrepareBodyToSelect(element, allowExpected)
|
||||
val (extension, bodyTypeToSelect) = createExtensionCallableAndPrepareBodyToSelect(element, allowExpected)
|
||||
|
||||
editor?.apply {
|
||||
unblockDocument()
|
||||
|
||||
if (bodyToSelect != null) {
|
||||
val range = bodyToSelect.textRange
|
||||
moveCaret(range.startOffset, ScrollType.CENTER)
|
||||
if (extension.isValid) {
|
||||
|
||||
val parent = bodyToSelect.parent
|
||||
val lastSibling =
|
||||
if (parent is KtBlockExpression)
|
||||
parent.rBrace?.siblings(forward = false, withItself = false)?.first { it !is PsiWhiteSpace }
|
||||
else
|
||||
bodyToSelect.siblings(forward = true, withItself = false).lastOrNull()
|
||||
val endOffset = lastSibling?.endOffset ?: range.endOffset
|
||||
selectionModel.setSelection(range.startOffset, endOffset)
|
||||
if (bodyTypeToSelect != GeneratedBodyType.NOTHING) {
|
||||
val bodyToSelect = getBodyForSelection(extension, bodyTypeToSelect)
|
||||
|
||||
if (bodyToSelect != null) {
|
||||
val range = bodyToSelect.textRange
|
||||
moveCaret(range.startOffset, ScrollType.CENTER)
|
||||
|
||||
val parent = bodyToSelect.parent
|
||||
val lastSibling =
|
||||
if (parent is KtBlockExpression)
|
||||
parent.rBrace?.siblings(forward = false, withItself = false)?.first { it !is PsiWhiteSpace }
|
||||
else
|
||||
bodyToSelect.siblings(forward = true, withItself = false).lastOrNull()
|
||||
val endOffset = lastSibling?.endOffset ?: range.endOffset
|
||||
selectionModel.setSelection(range.startOffset, endOffset)
|
||||
} else {
|
||||
LOG.error("Extension created with new method body for $bodyToSelect but this body was not found after document commit. Extension text: \"${extension.text}\"")
|
||||
moveCaret(extension.textOffset, ScrollType.CENTER)
|
||||
}
|
||||
} else {
|
||||
moveCaret(extension.textOffset, ScrollType.CENTER)
|
||||
}
|
||||
} else {
|
||||
moveCaret(extension.textOffset, ScrollType.CENTER)
|
||||
LOG.error("Extension invalidated during document commit. Extension text \"${extension.text}\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBodyForSelection(extension: KtCallableDeclaration, bodyTypeToSelect: GeneratedBodyType): KtExpression? {
|
||||
fun selectBody(declaration: KtDeclarationWithBody): KtExpression? {
|
||||
|
||||
if (!declaration.hasBody()) return extension
|
||||
|
||||
return declaration.bodyExpression?.let {
|
||||
(it as? KtBlockExpression)?.statements?.singleOrNull() ?: it
|
||||
}
|
||||
}
|
||||
|
||||
return when (bodyTypeToSelect) {
|
||||
GeneratedBodyType.FUNCTION -> (extension as? KtFunction)?.let { selectBody(it) }
|
||||
GeneratedBodyType.GETTER -> (extension as? KtProperty)?.getter?.let { selectBody(it) }
|
||||
GeneratedBodyType.SETTER -> (extension as? KtProperty)?.setter?.let { selectBody(it) }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private enum class GeneratedBodyType {
|
||||
NOTHING,
|
||||
FUNCTION,
|
||||
SETTER,
|
||||
GETTER
|
||||
}
|
||||
|
||||
private fun processSingleDeclaration(
|
||||
element: KtCallableDeclaration,
|
||||
allowExpected: Boolean
|
||||
): Pair<KtCallableDeclaration, KtExpression?> {
|
||||
): Pair<KtCallableDeclaration, GeneratedBodyType> {
|
||||
val descriptor = element.unsafeResolveToDescriptor()
|
||||
val containingClass = descriptor.containingDeclaration as ClassDescriptor
|
||||
|
||||
@@ -161,14 +201,7 @@ class ConvertMemberToExtensionIntention :
|
||||
extension.addModifier(KtTokens.EXPECT_KEYWORD)
|
||||
}
|
||||
|
||||
var bodyToSelect: KtExpression? = null
|
||||
|
||||
fun selectBody(declaration: KtDeclarationWithBody) {
|
||||
if (bodyToSelect == null) {
|
||||
val body = declaration.bodyExpression
|
||||
bodyToSelect = (body as? KtBlockExpression)?.statements?.single() ?: body
|
||||
}
|
||||
}
|
||||
var bodyTypeToSelect = GeneratedBodyType.NOTHING
|
||||
|
||||
val bodyText = getFunctionBodyTextFromTemplate(
|
||||
project,
|
||||
@@ -183,7 +216,7 @@ class ConvertMemberToExtensionIntention :
|
||||
if (!extension.hasBody() && !isEffectivelyExpected) {
|
||||
//TODO: methods in PSI for setBody
|
||||
extension.add(psiFactory.createBlock(bodyText))
|
||||
selectBody(extension)
|
||||
bodyTypeToSelect = GeneratedBodyType.FUNCTION
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,10 +231,10 @@ class ConvertMemberToExtensionIntention :
|
||||
if (getter == null) {
|
||||
getter = extension.addAfter(templateGetter, extension.typeReference) as KtPropertyAccessor
|
||||
extension.addBefore(psiFactory.createNewLine(), getter)
|
||||
selectBody(getter)
|
||||
bodyTypeToSelect = GeneratedBodyType.GETTER
|
||||
} else if (!getter.hasBody()) {
|
||||
getter = getter.replace(templateGetter) as KtPropertyAccessor
|
||||
selectBody(getter)
|
||||
bodyTypeToSelect = GeneratedBodyType.GETTER
|
||||
}
|
||||
|
||||
if (extension.isVar) {
|
||||
@@ -209,10 +242,14 @@ class ConvertMemberToExtensionIntention :
|
||||
if (setter == null) {
|
||||
setter = extension.addAfter(templateSetter, getter) as KtPropertyAccessor
|
||||
extension.addBefore(psiFactory.createNewLine(), setter)
|
||||
selectBody(setter)
|
||||
if (bodyTypeToSelect == GeneratedBodyType.NOTHING) {
|
||||
bodyTypeToSelect = GeneratedBodyType.SETTER
|
||||
}
|
||||
} else if (!setter.hasBody()) {
|
||||
setter = setter.replace(templateSetter) as KtPropertyAccessor
|
||||
selectBody(setter)
|
||||
setter.replace(templateSetter) as KtPropertyAccessor
|
||||
if (bodyTypeToSelect == GeneratedBodyType.NOTHING) {
|
||||
bodyTypeToSelect = GeneratedBodyType.SETTER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,7 +277,7 @@ class ConvertMemberToExtensionIntention :
|
||||
}
|
||||
}
|
||||
|
||||
return extension to bodyToSelect
|
||||
return extension to bodyTypeToSelect
|
||||
}
|
||||
|
||||
private fun askIfExpectedIsAllowed(file: KtFile): Boolean {
|
||||
@@ -258,7 +295,7 @@ class ConvertMemberToExtensionIntention :
|
||||
private fun createExtensionCallableAndPrepareBodyToSelect(
|
||||
element: KtCallableDeclaration,
|
||||
allowExpected: Boolean = true
|
||||
): Pair<KtCallableDeclaration, KtExpression?> {
|
||||
): Pair<KtCallableDeclaration, GeneratedBodyType> {
|
||||
val expectedDeclaration = element.liftToExpected() as? KtCallableDeclaration
|
||||
if (expectedDeclaration != null) {
|
||||
element.withExpectedActuals().filterIsInstance<KtCallableDeclaration>().forEach {
|
||||
@@ -269,11 +306,11 @@ class ConvertMemberToExtensionIntention :
|
||||
}
|
||||
|
||||
val classVisibility = element.containingClass()?.visibilityModifierType()
|
||||
val (extension, bodyToSelect) = processSingleDeclaration(element, allowExpected)
|
||||
val (extension, bodyTypeToSelect) = processSingleDeclaration(element, allowExpected)
|
||||
if (classVisibility != null && extension.visibilityModifier() == null) {
|
||||
extension.addModifier(classVisibility)
|
||||
}
|
||||
return extension to bodyToSelect
|
||||
return extension to bodyTypeToSelect
|
||||
}
|
||||
|
||||
private fun newTypeParameterList(member: KtCallableDeclaration): KtTypeParameterList? {
|
||||
@@ -290,4 +327,4 @@ class ConvertMemberToExtensionIntention :
|
||||
return ConvertMemberToExtensionIntention().createExtensionCallableAndPrepareBodyToSelect(element).first
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user