From a23c6a2cdc9cad251d7e5ccd37b742e0f61a4204 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 11 May 2015 20:09:51 +0300 Subject: [PATCH] Smaller range + refactoring of ConvertMemberToExtension --- .../kotlin/idea/JetBundle.properties | 2 - .../declarations/ConvertMemberToExtension.kt | 186 ++++++++---------- .../funcitonNoName.kt | 1 + .../funcitonNoName.kt.after | 6 - 4 files changed, 85 insertions(+), 110 deletions(-) delete mode 100644 idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt.after diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index 121bf6694fb..3c8d9110526 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -330,8 +330,6 @@ hierarchy.legend.member.is.defined.in.class=Member is defined in the class hierarchy.legend.member.defined.in.superclass=Member is not defined in the class but defined in superclass hierarchy.legend.member.should.be.defined=Member should be defined since the class is not abstract -convert.to.extension=Convert to extension - suppress.warnings.family=Suppress Warnings suppress.warning.for=Suppress ''{0}'' for {1} {2} diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtension.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtension.kt index 0b55bd10b71..c2a8616ab69 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtension.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/declarations/ConvertMemberToExtension.kt @@ -17,79 +17,69 @@ package org.jetbrains.kotlin.idea.intentions.declarations import com.intellij.codeInsight.CodeInsightUtilCore -import com.intellij.codeInsight.intention.impl.BaseIntentionAction import com.intellij.openapi.editor.Editor -import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.Function -import com.intellij.util.IncorrectOperationException import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.idea.JetBundle -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention import org.jetbrains.kotlin.lexer.JetModifierKeywordToken import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR -public class ConvertMemberToExtension : BaseIntentionAction() { - - override fun getText(): String { - return getFamilyName() - } - - override fun getFamilyName(): String { - return JetBundle.message("convert.to.extension") - } - - override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean { - val declaration = getTarget(editor, file) - if (declaration is JetProperty) { - if (declaration.hasInitializer()) return false +public class ConvertMemberToExtension : JetSelfTargetingRangeIntention(javaClass(), "Convert to extension") { + override fun applicabilityRange(element: JetCallableDeclaration): TextRange? { + val classBody = element.getParent() as? JetClassBody ?: return null + if (classBody.getParent() !is JetClass) return null + if (element.getReceiverTypeReference() != null) return null + when (element) { + is JetProperty -> if (element.hasInitializer()) return null + is JetSecondaryConstructor -> return null } - return declaration != null && declaration !is JetSecondaryConstructor && declaration.getParent() is JetClassBody && declaration.getParent().getParent() is JetClass && declaration.getReceiverTypeReference() == null + return (element.getNameIdentifier() ?: return null).getTextRange() } - throws(IncorrectOperationException::class) - override fun invoke(project: Project, editor: Editor, file: PsiFile) { - val member = getTarget(editor, file) - assert(member != null, "Must be checked by isAvailable") + override fun applyTo(element: JetCallableDeclaration, editor: Editor) { + val descriptor = element.resolveToDescriptor() + val containingClass = descriptor.getContainingDeclaration() as ClassDescriptor - val bindingContext = member!!.analyzeFully() - val memberDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, member) ?: return + val file = element.getContainingJetFile() + val outermostParent = JetPsiUtil.getOutermostParent(element, file, false) - val containingClass = memberDescriptor.getContainingDeclaration() - assert(containingClass is ClassDescriptor) { "Members must be contained in classes: \n" + "Descriptor: " + memberDescriptor + "\n" + "Declaration & context: " + member.getParent().getParent().getText() } + val receiver = containingClass.getDefaultType().toString() + "." + val name = element.getNameIdentifier()!!.getText() - val outermostParent = JetPsiUtil.getOutermostParent(member, file, false) + val valueParameterList = element.getValueParameterList() + val returnTypeRef = element.getTypeReference() - val receiver = (containingClass as ClassDescriptor).getDefaultType().toString() + "." - val identifier = member.getNameIdentifier() - val name = if (identifier == null) "" else identifier.getText() + val extensionText = modifiers(element) + + memberType(element) + + " " + + typeParameters(element) + + receiver + + name + + (if (valueParameterList == null) "" else valueParameterList.getText()) + + (if (returnTypeRef != null) ": " + returnTypeRef.getText() else "") + + body(element) - val valueParameterList = member.getValueParameterList() - val returnTypeRef = member.getTypeReference() - - val extensionText = modifiers(member) + memberType(member) + " " + typeParameters(member) + receiver + name + (if (valueParameterList == null) "" else valueParameterList.getText()) + (if (returnTypeRef != null) ": " + returnTypeRef.getText() else "") + body(member) - - val psiFactory = JetPsiFactory(member) + val psiFactory = JetPsiFactory(element) val extension = psiFactory.createDeclaration(extensionText) val added = file.addAfter(extension, outermostParent) file.addAfter(psiFactory.createNewLine(), outermostParent) file.addAfter(psiFactory.createNewLine(), outermostParent) - member.delete() + element.delete() CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(added) val caretAnchor = added.getText().indexOf(CARET_ANCHOR) if (caretAnchor >= 0) { val caretOffset = added.getTextRange().getStartOffset() + caretAnchor - val anchor = PsiTreeUtil.findElementOfClassAtOffset(file, caretOffset, javaClass(), false) + val anchor = PsiTreeUtil.findElementOfClassAtOffset(file, caretOffset, javaClass(), false) if (anchor != null && CARET_ANCHOR == anchor.getReferencedName()) { val throwException = psiFactory.createExpression(THROW_UNSUPPORTED_OPERATION_EXCEPTION) val replaced = anchor.replace(throwException) @@ -100,76 +90,68 @@ public class ConvertMemberToExtension : BaseIntentionAction() { } } - companion object { + private val CARET_ANCHOR = "____CARET_ANCHOR____" + private val THROW_UNSUPPORTED_OPERATION_EXCEPTION = " throw UnsupportedOperationException()" - public val CARET_ANCHOR: String = "____CARET_ANCHOR____" - public val THROW_UNSUPPORTED_OPERATION_EXCEPTION: String = " throw UnsupportedOperationException()" - - private fun getTarget(editor: Editor, file: PsiFile): JetCallableDeclaration? { - val element = file.findElementAt(editor.getCaretModel().getOffset()) - return PsiTreeUtil.getParentOfType(element, javaClass(), false, javaClass()) + private fun memberType(member: JetCallableDeclaration): String { + if (member is JetFunction) { + return "fun" } + return (member as JetProperty).getValOrVarNode().getText() + } - private fun memberType(member: JetCallableDeclaration): String { - if (member is JetFunction) { - return "fun" + private fun modifiers(member: JetCallableDeclaration): String { + val modifierList = member.getModifierList() ?: return "" + for (modifierType in JetTokens.VISIBILITY_MODIFIERS.getTypes()) { + val modifier = modifierList.getModifier(modifierType as JetModifierKeywordToken) + if (modifier != null) { + return if (modifierType == JetTokens.PROTECTED_KEYWORD) "" else modifier.getText() + " " } - return (member as JetProperty).getValOrVarNode().getText() } + return "" + } - private fun modifiers(member: JetCallableDeclaration): String { - val modifierList = member.getModifierList() ?: return "" - for (modifierType in JetTokens.VISIBILITY_MODIFIERS.getTypes()) { - val modifier = modifierList.getModifier(modifierType as JetModifierKeywordToken) - if (modifier != null) { - return if (modifierType == JetTokens.PROTECTED_KEYWORD) "" else modifier.getText() + " " - } + private fun typeParameters(member: JetCallableDeclaration): String { + val classElement = member.getParent().getParent() + assert(classElement is JetClass) { "Must be checked in isAvailable: " + classElement.getText() } + + val allTypeParameters = ContainerUtil.concat((classElement as JetClass).getTypeParameters(), member.getTypeParameters()) + if (allTypeParameters.isEmpty()) return "" + return "<" + StringUtil.join(allTypeParameters, object : Function { + override fun `fun`(parameter: JetTypeParameter): String { + return parameter.getText() } + }, ", ") + "> " + } + + private fun body(member: JetCallableDeclaration): String { + if (member is JetProperty) { + return "\n" + getter(member) + "\n" + setter(member, !synthesizeBody(member.getGetter())) + } + else if (member is JetFunction) { + val bodyExpression = member.getBodyExpression() ?: return "{" + CARET_ANCHOR + "}" + if (!member.hasBlockBody()) return " = " + bodyExpression.getText() + return bodyExpression.getText() + } + else { return "" } + } - private fun typeParameters(member: JetCallableDeclaration): String { - val classElement = member.getParent().getParent() - assert(classElement is JetClass) { "Must be checked in isAvailable: " + classElement.getText() } + private fun getter(property: JetProperty): String { + val getter = property.getGetter() + if (synthesizeBody(getter)) return "get() = " + CARET_ANCHOR + return getter!!.getText() + } - val allTypeParameters = ContainerUtil.concat((classElement as JetClass).getTypeParameters(), member.getTypeParameters()) - if (allTypeParameters.isEmpty()) return "" - return "<" + StringUtil.join(allTypeParameters, object : Function { - override fun `fun`(parameter: JetTypeParameter): String { - return parameter.getText() - } - }, ", ") + "> " - } + private fun setter(property: JetProperty, allowCaretAnchor: Boolean): String { + if (!property.isVar()) return "" + val setter = property.getSetter() + if (synthesizeBody(setter)) return "set(value) {" + (if (allowCaretAnchor) CARET_ANCHOR else THROW_UNSUPPORTED_OPERATION_EXCEPTION) + "}" + return setter!!.getText() + } - private fun body(member: JetCallableDeclaration): String { - if (member is JetProperty) { - return "\n" + getter(member) + "\n" + setter(member, !synthesizeBody(member.getGetter())) - } - else if (member is JetFunction) { - val bodyExpression = member.getBodyExpression() ?: return "{" + CARET_ANCHOR + "}" - if (!member.hasBlockBody()) return " = " + bodyExpression.getText() - return bodyExpression.getText() - } - else { - return "" - } - } - - private fun getter(property: JetProperty): String { - val getter = property.getGetter() - if (synthesizeBody(getter)) return "get() = " + CARET_ANCHOR - return getter.getText() - } - - private fun setter(property: JetProperty, allowCaretAnchor: Boolean): String { - if (!property.isVar()) return "" - val setter = property.getSetter() - if (synthesizeBody(setter)) return "set(value) {" + (if (allowCaretAnchor) CARET_ANCHOR else THROW_UNSUPPORTED_OPERATION_EXCEPTION) + "}" - return setter.getText() - } - - private fun synthesizeBody(getter: JetPropertyAccessor?): Boolean { - return getter == null || getter.getBodyExpression() == null - } + private fun synthesizeBody(getter: JetPropertyAccessor?): Boolean { + return getter == null || getter.getBodyExpression() == null } } diff --git a/idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt b/idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt index 4c5478d63e8..c16de7a0a71 100644 --- a/idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt +++ b/idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt @@ -1,3 +1,4 @@ +// IS_APPLICABLE: false // ERROR: Function declaration must have a name class Owner { fun () {} diff --git a/idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt.after b/idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt.after deleted file mode 100644 index a223121db61..00000000000 --- a/idea/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt.after +++ /dev/null @@ -1,6 +0,0 @@ -// ERROR: Function declaration must have a name -class Owner { -} - -fun Owner.() { -} \ No newline at end of file